Hooks 和 Interceptors

让开发者在每个步骤控制和自定义 Agent 执行。

Hooks 和 Interceptors 提供了一种更精细控制 Agent 内部行为的方式。

核心 Agent 循环涉及调用模型、让其选择要执行的工具,直到不需要调用工具时完成。

reactagent

Hooks 和 Interceptors 在这些步骤的前后暴露了钩子点,允许你:

reactagent
  • 监控: 通过日志、分析和调试跟踪 Agent 行为;

  • 修改: 转换提示、工具选择和输出格式;

  • 控制: 添加重试、回退和提前终止逻辑;

  • 强制执行: 应用速率限制、护栏和 PII 检测。

通过将它们传递给 ReactAgent.builder() 来添加 Hooks 和 Interceptors:


import com.alibaba.cloud.ai.graph.agent.ReactAgent;


import com.alibaba.cloud.ai.graph.agent.hook.*;


import com.alibaba.cloud.ai.graph.agent.interceptor.*;





ReactAgent agent = ReactAgent.builder()


    .name("my_agent")


    .model(chatModel)


    .tools(tools)


    .hooks(loggingHook, messageTrimmingHook)


    .interceptors(guardrailInterceptor, retryInterceptor)


    .build();

Hooks 和 Interceptors 能做什么?

  • 监控:使用日志、分析和调试跟踪 Agent 行为;

  • 修改:转换提示、工具选择和输出格式;

  • 控制:添加重试、回退和提前终止逻辑;

  • 强制执行:应用速率限制、护栏和 PII 检测。

内置实现

Spring AI Alibaba 为常见用例提供了预构建的 Hooks 和 Interceptors 实现:

消息压缩(Summarization)

当接近 token 限制时自动压缩对话历史。

适用场景:

  • 超出上下文窗口的长期对话;

  • 具有大量历史记录的多轮对话;

  • 需要保留完整对话上下文的应用程序。


import com.alibaba.cloud.ai.graph.agent.hook.summarization.SummarizationHook;





// 创建消息压缩 Hook


SummarizationHook summarizationHook = SummarizationHook.builder()


    .model(chatModel)


    .maxTokensBeforeSummary(4000)


    .messagesToKeep(20)


    .build();





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("my_agent")


    .model(chatModel)


    .hooks(summarizationHook)


    .build();

配置选项:

  • model: 用于生成摘要的 ChatModel;

  • maxTokensBeforeSummary: 触发摘要之前的最大 token 数;

  • messagesToKeep: 摘要后保留的最新消息数。

Human-in-the-Loop(人机协同)

暂停 Agent 执行以获得人工批准、编辑或拒绝工具调用。

适用场景:

  • 需要人工批准的高风险操作(数据库写入、金融交易);

  • 人工监督是强制性的合规工作流程;

  • 长期对话,使用人工反馈引导 Agent。


import com.alibaba.cloud.ai.graph.agent.hook.hip.HumanInTheLoopHook;


import com.alibaba.cloud.ai.graph.agent.hook.hip.ToolConfig;





// 创建 Human-in-the-Loop Hook


HumanInTheLoopHook humanReviewHook = HumanInTheLoopHook.builder()


    .approvalOn("sendEmailTool", ToolConfig.builder().description("Please confirm sending the email.").build())


    .approvalOn("deleteDataTool")


    .build();





ReactAgent agent = ReactAgent.builder()


    .name("supervised_agent")


    .model(chatModel)


    .tools(sendEmailTool, deleteDataTool)


    .hooks(humanReviewHook)


    .saver(new RedisSaver())


    .build();

重要提示:Human-in-the-loop Hook 需要 checkpointer 来维护跨中断的状态。示例中我们演示用了 RedisSaver。

模型调用限制(Model Call Limit)

限制模型调用次数以防止无限循环或过度成本。

适用场景:

  • 防止失控的 Agent 进行太多 API 调用;

  • 在生产部署中强制执行成本控制;

  • 在特定调用预算内测试 Agent 行为。


ReactAgent agent = ReactAgent.builder()


    .name("my_agent")


    .model(chatModel)


    .hooks(ModelCallLimitHook.builder().runLimit(5).build())  // 限制模型调用次数为5次


    .saver(new MemorySaver())


    .build();

PII 检测(Personally Identifiable Information)

检测和处理对话中的个人身份信息。

适用场景:

  • 具有合规要求的医疗保健和金融应用;

  • 需要清理日志的客户服务 Agent;

  • 任何处理敏感用户数据的应用程序。


import com.alibaba.cloud.ai.graph.agent.hook.pii.PIIDetectionHook;


import com.alibaba.cloud.ai.graph.agent.hook.pii.PIIType;


import com.alibaba.cloud.ai.graph.agent.hook.pii.RedactionStrategy;





PIIDetectionHook pii = PIIDetectionHook.builder()


    .piiType(PIIType.EMAIL)


    .strategy(RedactionStrategy.REDACT)


    .applyToInput(true)


    .build();





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("secure_agent")


    .model(chatModel)


    .hooks(pii)


    .build();

工具重试(Tool Retry)

自动重试失败的工具调用,具有可配置的指数退避。

适用场景:

  • 处理外部 API 调用中的瞬态故障;

  • 提高依赖网络的工具的可靠性;

  • 构建优雅处理临时错误的弹性 Agent。


import com.alibaba.cloud.ai.graph.agent.interceptor.toolretry.ToolRetryInterceptor;





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("resilient_agent")


    .model(chatModel)


    .tools(searchTool, databaseTool)


    .interceptors(ToolRetryInterceptor.builder()


        .maxRetries(2)


        .onFailure(ToolRetryInterceptor.OnFailureBehavior.RETURN_MESSAGE)


        .build())


    .build();

Planning(规划)

在执行工具之前强制执行一个规划步骤,以概述 Agent 将要采取的步骤。

适用场景:

  • 需要执行复杂、多步骤任务的 Agent;

  • 通过在执行前显示 Agent 的计划来提高透明度;

  • 通过检查建议的计划来调试错误。


import com.alibaba.cloud.ai.graph.agent.interceptor.todolist.TodoListInterceptor;





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("planning_agent")


    .model(chatModel)


    .tools(myTool)


    .interceptors(TodoListInterceptor.builder().build())


    .build();

LLM Tool Selector(LLM 工具选择器)

使用一个 LLM 来决定在多个可用工具之间选择哪个工具。

适用场景:

  • 当多个工具可以实现相似目标时;

  • 需要根据细微的上下文差异进行工具选择;

  • 动态选择最适合特定输入的工具。


import com.alibaba.cloud.ai.graph.agent.interceptor.toolselection.ToolSelectionInterceptor;





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("smart_selector_agent")


    .model(chatModel)


    .tools(tool1, tool2)


    .interceptors(ToolSelectionInterceptor.builder().build())


    .build();

LLM Tool Emulator(LLM 工具模拟器)

在没有实际执行工具的情况下,使用 LLM 模拟工具的输出。

适用场景:

  • 在演示或测试期间模拟 API;

  • 在开发过程中为工具提供占位符行为;

  • 在不产生实际成本或副作用的情况下测试 Agent 逻辑。


import com.alibaba.cloud.ai.graph.agent.interceptor.toolemulator.ToolEmulatorInterceptor;





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("emulator_agent")


    .model(chatModel)


    .tools(simulatedTool)


    .interceptors(ToolEmulatorInterceptor.builder().model(chatModel).build())


    .build();

Context Editing(上下文编辑)

在将上下文发送给 LLM 之前对其进行修改,以注入、删除或修改信息。

适用场景:

  • 向 LLM 提供额外的上下文或指令;

  • 从对话历史中删除不相关或冗余的信息;

  • 动态修改上下文以引导 Agent 的行为。


import com.alibaba.cloud.ai.graph.agent.interceptor.contextediting.ContextEditingInterceptor;





// 使用


ReactAgent agent = ReactAgent.builder()


    .name("context_aware_agent")


    .model(chatModel)


    .interceptors(ContextEditingInterceptor.builder().trigger(120000).clearAtLeast(60000).build())


    .build();

自定义 Hooks 和 Interceptors

通过实现在 Agent 执行流程中特定点运行的钩子来构建自定义功能。

你可以通过以下方式创建自定义功能:

  1. MessagesModelHook - 在模型调用前后执行,专注于消息操作(推荐);

  2. ModelHook - 在模型调用前后执行,可访问完整状态;

  3. AgentHook - 在 Agent 开始和结束时执行;

  4. ModelInterceptor - 拦截和修改模型请求/响应;

  5. ToolInterceptor - 拦截和修改工具调用。

MessagesModelHook

MessagesModelHook 是一个专门用于操作消息列表的 Hook,使用更简单,更推荐。它直接接收和返回消息列表,无需处理复杂的 OverAllState。

适用场景:

  • 消息修剪、过滤或转换;

  • 添加系统提示或上下文消息;

  • 消息压缩和摘要;

  • 简单的消息操作需求。


import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;


import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import org.springframework.ai.chat.messages.Message;





@HookPositions({HookPosition.BEFORE_MODEL})


public class MessageTrimmingHook extends MessagesModelHook {


    private static final int MAX_MESSAGES = 10;





    @Override


    public String getName() {


        return "message_trimming";


    }





    @Override


    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {


        // 如果消息数量超过限制,只保留最后 MAX_MESSAGES 条消息


        if (previousMessages.size() > MAX_MESSAGES) {


            List<Message> trimmedMessages = previousMessages.subList(


                previousMessages.size() - MAX_MESSAGES,


                previousMessages.size()


            );


            // 使用 REPLACE 策略替换所有消息


            return new AgentCommand(trimmedMessages, UpdatePolicy.REPLACE);


        }


        // 如果消息数量未超过限制,返回原始消息(不进行修改)


        return new AgentCommand(previousMessages);


    }


}

AgentCommand 和 UpdatePolicy:

MessagesModelHook 通过 AgentCommand 返回操作结果:

  • REPLACE 策略:替换所有现有消息;

  • APPEND 策略:将新消息追加到现有消息列表。


import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;


import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import org.springframework.ai.chat.messages.Message;


import org.springframework.ai.chat.messages.SystemMessage;


import org.springframework.ai.chat.messages.UserMessage;


import java.util.ArrayList;


import java.util.List;





@HookPositions({HookPosition.BEFORE_MODEL})


public class ContextEnhancementHook extends MessagesModelHook {


    


    @Override


    public String getName() {


        return "context_enhancement";


    }





    @Override


    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {


        // 示例 1: 使用 REPLACE 策略替换所有消息


        List<Message> newMessages = new ArrayList<>();


        newMessages.add(new SystemMessage("你是一个专业的助手"));


        newMessages.addAll(previousMessages);


        return new AgentCommand(newMessages, UpdatePolicy.REPLACE);


        


        // 示例 2: 使用 APPEND 策略追加消息


        // List<Message> additionalMessages = List.of(


        //     new UserMessage("请记住:保持友好和专业")


        // );


        // return new AgentCommand(additionalMessages, UpdatePolicy.APPEND);


    }


}

支持跳转控制:

MessagesModelHook 也支持通过 JumpTo 实现提前退出:


import com.alibaba.cloud.ai.graph.agent.hook.JumpTo;


import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import org.springframework.ai.chat.messages.Message;


import java.util.List;





@HookPositions({HookPosition.BEFORE_MODEL})


public class EarlyExitHook extends MessagesModelHook {


    


    @Override


    public String getName() {


        return "early_exit";


    }





    @Override


    public List<JumpTo> canJumpTo() {


        return List.of(JumpTo.end);


    }





    @Override


    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {


        // 检查某些条件,如果满足则提前退出


        if (shouldExit(previousMessages)) {


            return new AgentCommand(JumpTo.end, previousMessages);


        }


        return new AgentCommand(previousMessages);


    }


    


    private boolean shouldExit(List<Message> messages) {


        // 实现你的退出逻辑


        return false;


    }


}

ModelHook

在模型调用前后执行自定义逻辑:


import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import java.util.concurrent.CompletableFuture;





@HookPositions({HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL})


public class CustomModelHook extends ModelHook {





    @Override


    public String getName() {


        return "custom_model_hook";


    }





    @Override


    public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {


        // 在模型调用前执行


        System.out.println("准备调用模型...");





        // 可以修改状态


        // 例如:添加额外的上下文


        return CompletableFuture.completedFuture(Map.of("extra_context", "某些额外信息"));


    }





    @Override


    public CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {


        // 在模型调用后执行


        System.out.println("模型调用完成");





        // 可以记录响应信息


        return CompletableFuture.completedFuture(Map.of());


    }


}

删除消息:

使用 ModelHook 时,可以通过 RemoveByHash 来删除 messages 中的消息。重要提示:返回的消息列表必须保持原消息列表的顺序,不能打乱顺序。由于 ModelHook 的复杂度,因此我们更推荐直接使用 MessagesModelHook。


import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.OverAllState;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import com.alibaba.cloud.ai.graph.state.RemoveByHash;


import org.springframework.ai.chat.messages.Message;


import java.util.ArrayList;


import java.util.List;


import java.util.Map;


import java.util.Optional;


import java.util.concurrent.CompletableFuture;





@HookPositions({HookPosition.BEFORE_MODEL})


public class MessageDeletionHook extends ModelHook {





    @Override


    public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {


        Optional<Object> messagesOpt = state.value("messages");


        if (!messagesOpt.isPresent()) {


            return CompletableFuture.completedFuture(Map.of());


        }





        List<Message> messages = (List<Message>) messagesOpt.get();


        


        // 构建新的消息列表,保持原顺序


        List<Object> newMessages = new ArrayList<>();


        for (Message msg : messages) {


            // 根据条件决定保留或删除


            if (shouldKeep(msg)) {


                newMessages.add(msg);  // 保留消息


            } else {


                newMessages.add(RemoveByHash.of(msg));  // 标记删除


            }


        }


        


        return CompletableFuture.completedFuture(Map.of("messages", newMessages));


    }


    


    private boolean shouldKeep(Message msg) {


        // 实现你的保留逻辑


        return true;


    }


}

MessagesModelHook vs ModelHook:如何选择?

MessagesModelHook 和 ModelHook 都可以在模型调用前后执行自定义逻辑,但它们有不同的设计目标和适用场景。

核心区别

特性MessagesModelHookModelHook
易用性⭐⭐⭐⭐⭐ 更简单,直接操作消息列表⭐⭐⭐ 需要理解 OverAllState
灵活性⭐⭐⭐ 专注于消息操作⭐⭐⭐⭐⭐ 可访问和修改完整状态
推荐场景消息修剪、过滤、添加系统提示需要访问全局状态、自定义状态管理
API 复杂度简单:AgentCommand 返回消息列表复杂:返回 Map<String, Object> 更新状态

使用建议

选择 MessagesModelHook,如果你需要:

  • ✅ 简单的消息操作(修剪、过滤、转换);

  • ✅ 添加或修改系统提示;

  • ✅ 消息压缩和摘要;

  • ✅ 快速实现消息相关的 Hook。

选择 ModelHook,如果你需要:

  • ✅ 访问和修改 OverAllState 中的其他数据;

  • ✅ 在状态中存储自定义信息(如计数器、缓存等);

  • ✅ 基于全局状态做复杂决策;

  • ✅ 需要查看 Agent 执行过程中的完整上下文。

对比示例

场景:消息修剪

使用 MessagesModelHook(推荐):


import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;


import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import org.springframework.ai.chat.messages.Message;


import java.util.List;





@HookPositions({HookPosition.BEFORE_MODEL})


public class SimpleMessageTrimmingHook extends MessagesModelHook {


    private static final int MAX_MESSAGES = 10;





    @Override


    public String getName() {


        return "simple_message_trimming";


    }





    @Override


    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {


        if (previousMessages.size() > MAX_MESSAGES) {


            List<Message> trimmed = previousMessages.subList(


                previousMessages.size() - MAX_MESSAGES,


                previousMessages.size()


            );


            return new AgentCommand(trimmed, UpdatePolicy.REPLACE);


        }


        return new AgentCommand(previousMessages);


    }


}

使用 ModelHook(更复杂但更灵活):


import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.OverAllState;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import com.alibaba.cloud.ai.graph.state.ReplaceAllWith;


import org.springframework.ai.chat.messages.Message;


import java.util.List;


import java.util.Map;


import java.util.Optional;


import java.util.concurrent.CompletableFuture;





@HookPositions({HookPosition.BEFORE_MODEL})


public class AdvancedMessageTrimmingHook extends ModelHook {


    private static final int MAX_MESSAGES = 10;


    private static final String TRIM_COUNT_KEY = "trim_count";





    @Override


    public String getName() {


        return "advanced_message_trimming";


    }





    @Override


    public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {


        // 可以访问完整状态


        Optional<Object> messagesOpt = state.value("messages");


        if (messagesOpt.isEmpty()) {


            return CompletableFuture.completedFuture(Map.of());


        }





        List<Message> messages = (List<Message>) messagesOpt.get();


        


        // 可以访问和更新自定义状态


        int trimCount = (Integer) state.value(TRIM_COUNT_KEY).orElse(0);


        


        if (messages.size() > MAX_MESSAGES) {


            List<Message> trimmed = messages.subList(


                messages.size() - MAX_MESSAGES,


                messages.size()


            );


            


            // 可以同时更新消息和自定义状态


            return CompletableFuture.completedFuture(Map.of(


                "messages", ReplaceAllWith.of(trimmed),


                TRIM_COUNT_KEY, trimCount + 1  // 记录修剪次数


            ));


        }


        


        return CompletableFuture.completedFuture(Map.of());


    }


}

总结:

  • 优先使用 MessagesModelHook:对于大多数消息操作场景,它提供了更简洁的 API,代码更易读易维护。

  • 使用 ModelHook:当你需要访问全局状态、存储自定义数据或进行复杂的状态管理时。

AgentHook

在 Agent 整体执行的开始和结束时执行:


import com.alibaba.cloud.ai.graph.agent.hook.AgentHook;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import java.util.concurrent.CompletableFuture;





@HookPositions({HookPosition.BEFORE_AGENT, HookPosition.AFTER_AGENT})


public class CustomAgentHook extends AgentHook {





    @Override


    public String getName() {


        return "custom_agent_hook";


    }





    @Override


    public CompletableFuture<Map<String, Object>> beforeAgent(OverAllState state, RunnableConfig config) {


        System.out.println("Agent 开始执行");


        // 可以初始化资源、记录开始时间等


        return CompletableFuture.completedFuture(Map.of("start_time", System.currentTimeMillis()));


    }





    @Override


    public CompletableFuture<Map<String, Object>> afterAgent(OverAllState state, RunnableConfig config) {


        System.out.println("Agent 执行完成");


        // 可以清理资源、计算执行时间等


        Optional<Object> startTime = state.value("start_time");


        if (startTime.isPresent()) {


            long duration = System.currentTimeMillis() - (Long) startTime.get();


            System.out.println("执行耗时: " + duration + "ms");


        }


        return CompletableFuture.completedFuture(Map.of());


    }


}

ModelInterceptor

拦截和修改模型请求和响应:


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelInterceptor;


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelRequest;


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelResponse;


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelCallHandler;





public class LoggingInterceptor extends ModelInterceptor {





    @Override


    public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {


        // 请求前记录


        System.out.println("发送请求到模型: " + request.getMessages().size() + " 条消息");





        long startTime = System.currentTimeMillis();





        // 执行实际调用


        ModelResponse response = handler.call(request);





        // 响应后记录


        long duration = System.currentTimeMillis() - startTime;


        System.out.println("模型响应耗时: " + duration + "ms");





        return response;


    }





    @Override


    public String getName() {


        return "LoggingInterceptor";


    }


}

动态工具管理:

ModelInterceptor 支持在模型调用前动态管理工具:

  • dynamicToolCallbacks:动态添加工具回调,可以在运行时根据上下文添加新的工具

  • tools:动态筛选工具,指定本次调用可用的工具名称列表。如果为空,则使用所有默认工具


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelInterceptor;


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelRequest;


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelResponse;


import com.alibaba.cloud.ai.graph.agent.interceptor.ModelCallHandler;


import org.springframework.ai.tool.ToolCallback;


import org.springframework.ai.tool.function.FunctionToolCallback;


import java.util.ArrayList;


import java.util.List;


import java.util.Map;





public class DynamicToolInterceptor extends ModelInterceptor {





    // 示例:根据上下文动态创建的工具


    private ToolCallback createContextualTool(String context) {


        return FunctionToolCallback.builder("contextual_tool", (String input) -> {


            return "处理上下文: " + context + ", 输入: " + input;


        })


        .description("根据上下文动态创建的工具")


        .build();


    }





    @Override


    public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {


        // 从上下文中获取信息,决定添加哪些工具


        Map<String, Object> context = request.getContext();


        String userRole = (String) context.getOrDefault("user_role", "default");





        // 构建修改后的请求


        ModelRequest.Builder builder = ModelRequest.builder(request);





        // 示例 1: 动态添加工具回调


        List<ToolCallback> dynamicTools = new ArrayList<>();


        if ("premium".equals(userRole)) {


            // 为高级用户添加额外工具


            dynamicTools.add(createContextualTool("premium_feature"));


        }


        builder.dynamicToolCallbacks(dynamicTools);





        // 示例 2: 动态筛选工具(只允许使用指定的工具)


        if (shouldRestrictTools(context)) {


            // 只允许使用 search 和 calculator 工具


            builder.tools(List.of("search", "calculator"));


        }


        // 如果 tools 为空列表,则使用所有默认工具





        ModelRequest modifiedRequest = builder.build();


        return handler.call(modifiedRequest);


    }





    private boolean shouldRestrictTools(Map<String, Object> context) {


        // 根据上下文决定是否限制工具


        return context.containsKey("restrict_tools");


    }





    @Override


    public String getName() {


        return "DynamicToolInterceptor";


    }


}

使用场景:

  • 根据用户权限动态添加或移除工具;

  • 根据对话上下文临时启用特定工具;

  • 实现工具的动态加载和卸载;

  • 在特定条件下限制可用的工具集。

ToolInterceptor

拦截和修改工具调用:


import com.alibaba.cloud.ai.graph.agent.interceptor.ToolInterceptor;


import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallRequest;


import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallResponse;


import com.alibaba.cloud.ai.graph.agent.interceptor.ToolCallHandler;





public class ToolMonitoringInterceptor extends ToolInterceptor {





    @Override


    public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {


        String toolName = request.getToolName();


        long startTime = System.currentTimeMillis();





        System.out.println("执行工具: " + toolName);





        try {


            ToolCallResponse response = handler.call(request);





            long duration = System.currentTimeMillis() - startTime;


            System.out.println("工具 " + toolName + " 执行成功 (耗时: " + duration + "ms)");





            return response;


        } catch (Exception e) {


            long duration = System.currentTimeMillis() - startTime;


            System.err.println("工具 " + toolName + " 执行失败 (耗时: " + duration + "ms): " + e.getMessage());





            return ToolCallResponse.of(


                request.getToolCallId(),


                request.getToolName(),


                "工具执行失败: " + e.getMessage()


            );


        }


    }





    @Override


    public String getName() {


        return "ToolMonitoringInterceptor";


    }


}

使用 RunnableConfig 跨调用共享数据

RunnableConfig 提供了一个 context() 方法,允许你在同一个执行流程中的多个 Hook 调用、多轮模型或工具调用之间共享数据。这对于实现计数器、累积统计信息或跨多次调用维护状态非常有用。

适用场景:

  • 跟踪模型或工具调用次数;

  • 累积性能指标(总耗时、平均响应时间等);

  • 在 before/after Hook 之间传递临时数据;

  • 实现基于计数的限流或断路器。

示例:使用 RunnableConfig.context() 实现调用计数器


import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.RunnableConfig;


import com.alibaba.cloud.ai.graph.OverAllState;


import java.util.concurrent.CompletableFuture;


import java.util.Map;





@HookPositions({HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL})


public class ModelCallCounterHook extends ModelHook {





    private static final String CALL_COUNT_KEY = "__model_call_count__";


    private static final String TOTAL_TIME_KEY = "__total_model_time__";


    private static final String START_TIME_KEY = "__call_start_time__";





    @Override


    public String getName() {


        return "model_call_counter";


    }





    @Override


    public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {


        // 从 context 读取当前计数(如果不存在则默认为 0)


        int currentCount = config.context().containsKey(CALL_COUNT_KEY)


                ? (int) config.context().get(CALL_COUNT_KEY) : 0;





        System.out.println("模型调用 #" + (currentCount + 1));





        // 记录开始时间


        config.context().put(START_TIME_KEY, System.currentTimeMillis());





        return CompletableFuture.completedFuture(Map.of());


    }





    @Override


    public CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {


        // 读取当前计数并递增


        int currentCount = config.context().containsKey(CALL_COUNT_KEY)


                ? (int) config.context().get(CALL_COUNT_KEY) : 0;


        config.context().put(CALL_COUNT_KEY, currentCount + 1);





        // 计算本次调用耗时并累加到总耗时


        if (config.context().containsKey(START_TIME_KEY)) {


            long startTime = (long) config.context().get(START_TIME_KEY);


            long duration = System.currentTimeMillis() - startTime;





            long totalTime = config.context().containsKey(TOTAL_TIME_KEY)


                    ? (long) config.context().get(TOTAL_TIME_KEY) : 0L;


            config.context().put(TOTAL_TIME_KEY, totalTime + duration);





            // 输出统计信息


            int newCount = currentCount + 1;


            long newTotalTime = totalTime + duration;


            System.out.println("模型调用完成: " + duration + "ms");


            System.out.println("累计统计 - 调用次数: " + newCount + ", 总耗时: " + newTotalTime + "ms, 平均: " + (newTotalTime / newCount) + "ms");


        }





        return CompletableFuture.completedFuture(Map.of());


    }


}

示例:基于 context 实现调用次数限制


import com.alibaba.cloud.ai.graph.agent.hook.ModelHook;


import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;


import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;


import com.alibaba.cloud.ai.graph.agent.hook.JumpTo;


import org.springframework.ai.chat.messages.AssistantMessage;


import java.util.List;


import java.util.ArrayList;





@HookPositions({HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL})


public class ModelCallLimiterHook extends ModelHook {





    private static final String CALL_COUNT_KEY = "__model_call_count__";


    private final int maxCalls;





    public ModelCallLimiterHook(int maxCalls) {


        this.maxCalls = maxCalls;


    }





    @Override


    public String getName() {


        return "model_call_limiter";


    }





    @Override


    public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {


        // 读取当前调用次数


        int callCount = config.context().containsKey(CALL_COUNT_KEY)


                ? (int) config.context().get(CALL_COUNT_KEY) : 0;





        // 检查是否超过限制


        if (callCount >= maxCalls) {


            System.out.println("达到模型调用次数限制: " + maxCalls);





            // 添加终止消息


            List<Message> messages = new ArrayList<>(


                (List<Message>) state.value("messages").orElse(new ArrayList<>())


            );


            messages.add(new AssistantMessage(


                "已达到模型调用次数限制 (" + callCount + "/" + maxCalls + "),Agent 执行终止。"


            ));





            // 返回更新并跳转到结束


            return CompletableFuture.completedFuture(Map.of("messages", messages));


        }





        return CompletableFuture.completedFuture(Map.of());


    }





    @Override


    public CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {


        // 递增计数器


        int callCount = config.context().containsKey(CALL_COUNT_KEY)


                ? (int) config.context().get(CALL_COUNT_KEY) : 0;


        config.context().put(CALL_COUNT_KEY, callCount + 1);





        return CompletableFuture.completedFuture(Map.of());


    }





    @Override


    public List<JumpTo> canJumpTo() {


        return List.of(JumpTo.end);


    }


}

使用示例:


ReactAgent agent = ReactAgent.builder()


    .name("limited_agent")


    .model(chatModel)


    .tools(tools)


    .hooks(new ModelCallCounterHook())  // 监控调用统计


    .hooks(new ModelCallLimiterHook(5)) // 限制最多调用 5 次


    .build();

关键要点:

  • context() 是共享的: 同一个执行流程中的所有 Hook 共享同一个 context;

  • 数据持久性: context 中的数据在整个 Agent 执行期间保持有效;

  • 类型安全: 需要自己管理 context 中数据的类型转换;

  • 命名约定: 建议使用双下划线前缀命名 context key(如 __model_call_count__)以避免与用户数据冲突。

执行顺序

使用多个 Hooks 和 Interceptors 时,理解执行顺序很重要:


ReactAgent agent = ReactAgent.builder()


    .name("my_agent")


    .model(chatModel)


    .hooks(hook1, hook2, hook3)


    .interceptors(interceptor1, interceptor2)


    .interceptors(toolInterceptor1, toolInterceptor2)


    .build();

执行流程:

  1. Before Agent Hooks(按顺序):

    • hook1.beforeAgent()

    • hook2.beforeAgent()

    • hook3.beforeAgent()

  2. Agent 循环开始

  3. Before Model Hooks(按顺序):

    • hook1.beforeModel()

    • hook2.beforeModel()

    • hook3.beforeModel()

  4. Model Interceptors(嵌套调用):

    • interceptor1 → interceptor2 → 模型调用
  5. After Model Hooks(逆序):

    • hook3.afterModel()

    • hook2.afterModel()

    • hook1.afterModel()

  6. Tool Interceptors(如果有工具调用,嵌套调用):

    • toolInterceptor1 → toolInterceptor2 → 工具执行
  7. Agent 循环结束

  8. After Agent Hooks(逆序):

    • hook3.afterAgent()

    • hook2.afterAgent()

    • hook1.afterAgent()

关键规则:

  • before_* hooks: 从第一个到最后一个

  • after_* hooks: 从最后一个到第一个(逆序)

  • Interceptors: 嵌套调用(第一个拦截器包装所有其他的)

实际示例

示例 1:内容审核 Interceptor


public class ContentModerationInterceptor extends ModelInterceptor {





    private static final List<String> BLOCKED_WORDS =


        List.of("敏感词1", "敏感词2", "敏感词3");





    @Override


    public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {


        // 检查输入


        for (Message msg : request.getMessages()) {


            String content = msg.getText().toLowerCase();


            for (String blocked : BLOCKED_WORDS) {


                if (content.contains(blocked)) {


					return ModelResponse.of(AssistantMessage.builder().content("检测到不适当的内容,请修改您的输入").build());


                }


            }


        }





        // 执行模型调用


        ModelResponse response = handler.call(request);





        // 检查输出


        String output = response.getContent();


        for (String blocked : BLOCKED_WORDS) {


            if (output.contains(blocked)) {


                // 清理输出


                output = output.replaceAll(blocked, "[已过滤]");


                return response.withContent(output);


            }


        }





        return response;


    }





    @Override


    public String getName() {


        return "ContentModerationInterceptor";


    }


}

示例 2:性能监控 - 使用 Interceptor

使用 ModelInterceptor 和 ToolInterceptor 监控模型和工具调用的性能:


// 模型调用性能监控


public class ModelPerformanceInterceptor extends ModelInterceptor {





    @Override


    public ModelResponse interceptModel(ModelRequest request, ModelCallHandler handler) {


        // 请求前记录


        System.out.println("发送请求到模型: " + request.getMessages().size() + " 条消息");





        long startTime = System.currentTimeMillis();





        // 执行实际调用


        ModelResponse response = handler.call(request);





        // 响应后记录


        long duration = System.currentTimeMillis() - startTime;


        System.out.println("模型响应耗时: " + duration + "ms");





        return response;


    }





    @Override


    public String getName() {


        return "ModelPerformanceInterceptor";


    }


}





// 工具调用性能监控


public class ToolPerformanceInterceptor extends ToolInterceptor {





    @Override


    public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {


        String toolName = request.getToolName();


        long startTime = System.currentTimeMillis();





        System.out.println("执行工具: " + toolName);





        try {


            ToolCallResponse response = handler.call(request);





            long duration = System.currentTimeMillis() - startTime;


            System.out.println("工具 " + toolName + " 执行成功 (耗时: " + duration + "ms)");





            return response;


        } catch (Exception e) {


            long duration = System.currentTimeMillis() - startTime;


            System.err.println("工具 " + toolName + " 执行失败 (耗时: " + duration + "ms): " + e.getMessage());





            return ToolCallResponse.of(


                request.getToolCallId(),


                request.getToolName(),


                "工具执行失败: " + e.getMessage()


            );


        }


    }





    @Override


    public String getName() {


        return "ToolPerformanceInterceptor";


    }


}





// 使用示例


ReactAgent agent = ReactAgent.builder()


    .name("monitored_agent")


    .model(chatModel)


    .tools(tools)


    .interceptors(new ModelPerformanceInterceptor())


    .interceptors(new ToolPerformanceInterceptor())


    .build();

示例 3:工具缓存 Interceptor


public class ToolCacheInterceptor extends ToolInterceptor {





    private Map<String, ToolCallResponse> cache = new ConcurrentHashMap<>();


    private final long ttlMs;





    public ToolCacheInterceptor(long ttlMs) {


        this.ttlMs = ttlMs;


    }





    @Override


    public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {


        String cacheKey = generateCacheKey(request);





        // 检查缓存


        ToolCallResponse cached = cache.get(cacheKey);


        if (cached != null && !isExpired(cached)) {


            System.out.println("缓存命中: " + request.getToolName());


            return cached;


        }





        // 执行工具


        ToolCallResponse response = handler.call(request);





        // 缓存结果


        cache.put(cacheKey, response);





        return response;


    }





    @Override


    public String getName() {


        return "ToolCacheInterceptor";


    }





    private String generateCacheKey(ToolCallRequest request) {


        return request.getToolName() + ":" +


               request.getArguments();


    }





    private boolean isExpired(ToolCallResponse response) {


        // 实现 TTL 检查逻辑


        return false;


    }


}

与 Interceptor 的区别

在 Spring AI Alibaba 中,Hook 和 Interceptor 都可以用于干预 Agent 执行:

| 特性 | Hook | Interceptor |

| ------------ | ---------------------------------------------------- | ----------------------------- |

| 执行位置 | Agent 级别(before/after agent, before/after model) | 模型或工具调用级别 |

| 中断能力 | 支持中断和恢复(如 HumanInTheLoopHook) | 不支持中断,仅拦截和修改 |

| 使用场景 | 人工审批、Agent 间协调 | 日志记录、重试、降级 |

| 配置方式 | .hooks(List.of(...)) | .interceptors(List.of(...)) |

相关资源

Spring AI Alibaba / Hooks 和 Interceptors 0 字 0 行 cosolar
2026-09-13T09:32:58.415782278Z 2026-09-13T10:01:18.207661664Z