Spring AI Alibaba(二)Hooks 和Interceptors

2.1 定义和区别

Interceptor (拦截器 - 如 ModelInterceptor, ToolInterceptor):

  • 图示表现: 你可以看到 ModelInterceptor 是一个大框,直接包裹Model;同样 ToolInterceptor 也直接包裹了 Tool
  • 含义: 拦截器是核心执行单元的代理/包装器 。它拥有对目标组件(大模型或工具)调用的绝对控制权 。请求必须先进入拦截器,由拦截器决定是否调用实际的 ModelTool,以及如何处理调用前后的输入输出。它甚至可以直接阻断调用(例如:命中缓存时直接返回结果,不再请求大模型)。

Hook (钩子 - 如 AgentHook, ModelHook):

  • 图示表现: 钩子是以独立节点的形式,按照顺序串联在执行流水线(Pipeline)上的。它们分为前置和后置节点。
  • 含义: 钩子更像是生命周期中的观测点或切入点。当流程走到特定的阶段(例如:准备调用模型前、模型返回结果后),会触发相应的 Hook。它们通常用于执行一些旁路逻辑,比如:日志记录、埋点监控、状态更新、请求组装或结果过滤等。
特性 Hook (钩子) Interceptor (拦截器)
图示形态 流水线上的独立、顺序节点 包裹核心组件的容器
核心职责 生命周期观测、状态介入、上下文处理 控制核心调用的执行、阻断、替换
层级区分 有宏观(Agent)与微观(Model)之分 通常与被代理的实体(Model/Tool)一一对应
执行位置 Agent 级别(before/after agent, before/after model) 模型或工具调用级别
中断能力 支持中断和恢复(如 HumanInTheLoopHook) 不支持中断,仅拦截和修改
使用场景 人工审批、Agent 间协调 日志记录、重试、降级
配置方式 .hooks(List.of(...)) .interceptors(List.of(...))

2.2 作用

2.2.1 消息压缩
java 复制代码
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();
2.2.2 人机协同(需要人工批准的操作)
java 复制代码
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();

还可以使用interceptToolCall进行拦截确认

java 复制代码
public class ConsoleApprovalInterceptor extends ToolInterceptor {

    private final Set<String> toolsRequiringApproval;
    private final Scanner scanner;

    public ConsoleApprovalInterceptor(Set<String> toolsRequiringApproval, Scanner scanner) {
        this.toolsRequiringApproval = toolsRequiringApproval;
        this.scanner = scanner;
    }

    @Override
    public ToolCallResponse interceptToolCall(ToolCallRequest request, ToolCallHandler handler) {
        String toolName = request.getToolName();

        if (toolsRequiringApproval.contains(toolName)) {
            System.out.println("\n[Approval Required]");
            System.out.println("Tool: " + toolName);
            System.out.println("Arguments: " + request.getArguments());
            System.out.print("Approve? (y/n): ");

            String approval = scanner.nextLine();

            if (!"y".equalsIgnoreCase(approval)) {
                return ToolCallResponse.of(
                        request.getToolCallId(),
                        toolName,
                        "Tool call rejected by user"
                );
            }
        }
        return handler.call(request);
    }

    @Override
    public String getName() {
        return "console-approval";
    }
}
2.2.3 模型调用限制
java 复制代码
ReactAgent agent = ReactAgent.builder()
    .name("my_agent")
    .model(chatModel)
    .hooks(ModelCallLimitHook.builder().runLimit(5).build()) // 限制模型调用次数为5次
    .saver(new MemorySaver())
    .build();

注意:这里是限制用户发起一次对话,agent在底层调用大模型的次数,不是用户调用大模型的次数

2.2.4 PII检测
java 复制代码
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();

简单来说就是对敏感信息进行脱敏:

例如:请你帮我给 123@gmail.com 发送一个邮件,祝他生日快乐。

PII脱敏之后,大模型收到的消息为:请你帮我给[脱敏邮箱]发送一个邮件,祝他生日快乐。

缺点就是:如果你需要大模型帮你调用工具发送邮箱,由于被脱敏保护,大模型会丢失关键邮箱参数,导致不能发送邮箱

2.2.5 工具重试
java 复制代码
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();
2.2.6 LLM 工具选择器、LLM 工具模拟器、上下文编辑
java 复制代码
ReactAgent agent = ReactAgent.builder()
      .name("smart_selector_agent")
      .model(chatModel)
      .tools(tool1, tool2)
      .interceptors(ToolSelectionInterceptor.builder().build(),
                   ToolEmulatorInterceptor.builder().model(chatModel).build(),
                   ContextEditingInterceptor.builder().trigger(120000).clearAtLeast(60000).build()
                   )
      .build();

2.3 自定义Hooks和Interceptors

  • 自定义Hooks:

    1. 标记Hooks的所在位置(模型调用前后 / agent调用前后)

    2. 继承AgentHook / MessagesModelHook / ModelHook 并重写方法

      java 复制代码
      package com.dyl.springaialibabastudy.hooks;
      
      import com.alibaba.cloud.ai.graph.OverAllState;
      import com.alibaba.cloud.ai.graph.RunnableConfig;
      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 org.springframework.stereotype.Component;
      
      import java.util.Map;
      import java.util.concurrent.CompletableFuture;
      
      // 1. AgentHook - 在 Agent 开始/结束时执行,每次Agent调用只会运行一次
      @HookPositions({HookPosition.BEFORE_AGENT, HookPosition.AFTER_AGENT})
      @Component
      public class LoggingHook extends AgentHook {
          @Override
          public String getName() {
              return "logging";
          }
      
          @Override
          public CompletableFuture<Map<String, Object>> beforeAgent(OverAllState state, RunnableConfig config) {
              System.out.println("Agent 开始执行");
              return CompletableFuture.completedFuture(Map.of());
          }
      
          @Override
          public CompletableFuture<Map<String, Object>> afterAgent(OverAllState state, RunnableConfig config) {
              System.out.println("Agent 执行完成");
              return CompletableFuture.completedFuture(Map.of());
          }
      }
      
      
      // 模型前后:@HookPositions({HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL})
      // extends MessagesModelHook  或 ModelHook

    MessagesModelHook 和 ModelHook 区别:

    特性 MessagesModelHook (推荐) ModelHook
    关注核心 专注于 消息列表 (Messages) 的操作 关注 Agent 的 完整状态 (OverAllState)
    数据访问范围 只能接收和返回模型的对话消息(如 SystemMessage, UserMessage, AssistantMessage 等) 可以访问并修改 Agent 运行时的全部上下文状态,包括消息列表、业务变量、中间状态等
    使用复杂度 极简。不需要了解底层的复杂状态机,只需处理 List 即可 较高 。需要处理复杂的 OverAllState 对象
    官方态度 ⭐️ 更推荐(针对大多数常规需求) 基础/底层 Hook(针对复杂需求)

    MessagesModelHook(消息钩子): 它是 ModelHook 的一种更聚焦、更高层的抽象。在实际开发中,我们在大模型调用前后,90% 的工作都是在处理"说的话"(消息)。这个 Hook 把你从复杂的 Agent 状态引擎中解放出来,只塞给你一堆 Message,你处理完再把 Message 还给它就行了。

    ModelHook(模型状态钩子): 它是更底层的机制。当 Agent 运行到一个节点准备调用模型时,ModelHook 能够拿到这个 Agent 当前脑子里的所有东西(OverAllState) 。如果你不仅想改消息,还想根据消息偷偷修改 Agent 的某些业务标记(比如:识别到用户生气了,在全局状态里打个 is_angry=true 的 Tag,供后面的其他节点使用),你就必须用它。

  • 自定义Interceptor

    • ModelInterceptor
    • ToolInterceptor

2.4 执行顺序

java 复制代码
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 (嵌套调用):
    • interceptor1interceptor2 → 模型调用
  5. After Model Hooks (逆序):
    • hook3.afterModel()
    • hook2.afterModel()
    • hook1.afterModel()
  6. Tool Interceptors (如果有工具调用,嵌套调用): (先调用模型,然后模型请求调用tool,所以ToolInterceptor一定会在ModelHooks 和 ModelInterceptor后面)
    • toolInterceptor1toolInterceptor2 → 工具执行
  7. Agent 循环结束
  8. After Agent Hooks (逆序):
    • hook3.afterAgent()
    • hook2.afterAgent()
    • hook1.afterAgent()

关键规则

  • before_* hooks: 从第一个到最后一个
  • after_* hooks: 从最后一个到第一个(逆序)
  • Interceptors: 嵌套调用(第一个拦截器包装所有其他的)
相关推荐
万兴丶3 小时前
Unity 用AI自动开发游戏近一年----最新Cursor使用心得
人工智能·游戏·unity·cursor
鬼蛟3 小时前
Spring Boot整合全局异常处理器、junit、多环境、logback
java·spring boot·后端
进击的女IT3 小时前
Java使用poi-tl实现word模版渲染文本/图片
java·数据库·word
badhope3 小时前
2026年零基础打造专属AI机器人:从GitHub开源项目到个人智能助手,完整实战指南
人工智能·python·深度学习·计算机视觉·数据挖掘·github·语音识别
庞轩px3 小时前
ThreadLocal 源码分析与内存泄漏问题
java·jvm·线程·threadlocal·内存泄露·key-value
人工干智能3 小时前
科普:神经网络输入层shape与训练集x_train的shape
人工智能·深度学习·神经网络
小江的记录本3 小时前
【Logback】Logback 日志框架 与 SLF4J绑定、三层模块、MDC链路追踪、异步日志、滚动策略
java·spring boot·后端·spring·log4j·maven·logback
随风,奔跑3 小时前
Spring Boot笔记
java·spring boot·笔记·后端
studyForMokey3 小时前
【Android面试】Handler专题
android·java·面试