找回密码
 立即注册
首页 业界区 安全 Spring AI Alibaba 项目源码学习(十一)-Hook ...

Spring AI Alibaba 项目源码学习(十一)-Hook

叶芷雁 2025-11-18 21:00:11
Hook 机制分析

请关注微信公众号:阿呆-bot
概述

本文档分析 Spring AI Alibaba Agent Framework 中的 Hook(钩子)机制,包括 Hook 接口、AgentHook、ModelHook 的设计、实现原理、执行位置、跳转机制以及具体实现类。
入口类说明

Hook - 钩子接口

Hook 是所有钩子的基接口,定义了钩子的基本能力。
核心职责

  • 定义钩子名称
  • 定义钩子类型
  • 定义执行位置
  • 定义跳转能力
关键代码
  1. public interface Hook {
  2.         String getName();
  3.         HookType getHookType();
  4.         List<JumpTo> canJumpTo();
  5.         /**
  6.          * Get the positions where this hook should be executed.
  7.          * By default, this method checks for the @HookPositions annotation on the implementing class.
  8.          *
  9.          * @return array of HookPosition values
  10.          */
  11.         default HookPosition[] getHookPositions() {
  12.                 HookPositions annotation = this.getClass().getAnnotation(HookPositions.class);
  13.                 if (annotation != null) {
  14.                         return annotation.value();
  15.                 }
  16.                 // Default fallback based on hook type
  17.                 if (this instanceof AgentHook) {
  18.                         return new HookPosition[]{HookPosition.BEFORE_AGENT, HookPosition.AFTER_AGENT};
  19.                 } else if (this instanceof ModelHook) {
  20.                         return new HookPosition[]{HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL};
  21.                 }
  22.                 return new HookPosition[0];
  23.         }
  24. }
复制代码
HookPosition - 执行位置

HookPosition 枚举定义了钩子的执行位置。
关键代码
  1. public enum HookPosition {
  2.         /**
  3.          * Hook executes before the agent starts processing
  4.          */
  5.         BEFORE_AGENT,
  6.         /**
  7.          * Hook executes after the agent completes processing
  8.          */
  9.         AFTER_AGENT,
  10.         /**
  11.          * Hook executes before the model is called
  12.          */
  13.         BEFORE_MODEL,
  14.         /**
  15.          * Hook executes after the model returns a response
  16.          */
  17.         AFTER_MODEL
  18. }
复制代码
AgentHook - Agent 级别钩子

AgentHook 在 Agent 执行前后插入逻辑。
关键代码
  1. public interface AgentHook extends Hook {
  2.     default CompletableFuture<Map<String, Object>> beforeAgent(OverAllState state, RunnableConfig config) {
  3.         return CompletableFuture.completedFuture(Map.of());
  4.     }
  5.     default CompletableFuture<Map<String, Object>> afterAgent(OverAllState state, RunnableConfig config) {
  6.         return CompletableFuture.completedFuture(Map.of());
  7.     }
  8. }
复制代码
ModelHook - 模型级别钩子

ModelHook 在模型调用前后插入逻辑。
关键代码
  1. public interface ModelHook extends Hook {
  2.     default CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {
  3.         return CompletableFuture.completedFuture(Map.of());
  4.     }
  5.     default CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {
  6.         return CompletableFuture.completedFuture(Map.of());
  7.     }
  8.     @Override
  9.     default HookType getHookType() {
  10.         return HookType.MODEL;
  11.     }
  12. }
复制代码
JumpTo - 跳转目标

JumpTo 枚举定义了钩子可以跳转的目标。
关键代码
  1. public enum JumpTo {
  2.         tool,
  3.         model,
  4.         end
  5. }
复制代码
Hook 集成机制

Graph 集成

Hook 被集成到 ReactAgent 的 Graph 中:
关键代码
  1. // Add hook nodes
  2.                 for (Hook hook : hooks) {
  3.                         if (hook instanceof AgentHook agentHook) {
  4.                                 graph.addNode(hook.getName() + ".before", agentHook::beforeAgent);
  5.                                 graph.addNode(hook.getName() + ".after", agentHook::afterAgent);
  6.                         } else if (hook instanceof ModelHook modelHook) {
  7.                                 graph.addNode(hook.getName() + ".beforeModel", modelHook::beforeModel);
  8.                                 if (modelHook instanceof HumanInTheLoopHook humanInTheLoopHook) {
  9.                                         graph.addNode(hook.getName() + ".afterModel", humanInTheLoopHook);
  10.                                 } else {
  11.                                         graph.addNode(hook.getName() + ".afterModel", modelHook::afterModel);
  12.                                 }
  13.                         }
  14.                         else {
  15.                                 throw new UnsupportedOperationException("Unsupported hook type: " + hook.getClass().getName());
  16.                         }
  17.                 }
  18.                 // Categorize hooks by position
  19.                 List<Hook> beforeAgentHooks = filterHooksByPosition(hooks, HookPosition.BEFORE_AGENT);
  20.                 List<Hook> afterAgentHooks = filterHooksByPosition(hooks, HookPosition.AFTER_AGENT);
  21.                 List<Hook> beforeModelHooks = filterHooksByPosition(hooks, HookPosition.BEFORE_MODEL);
  22.                 List<Hook> afterModelHooks = filterHooksByPosition(hooks, HookPosition.AFTER_MODEL);
  23.                 // Determine node flow
  24.                 String entryNode = determineEntryNode(beforeAgentHooks, beforeModelHooks);
  25.                 String loopEntryNode = determineLoopEntryNode(beforeModelHooks);
  26.                 String loopExitNode = determineLoopExitNode(afterModelHooks);
  27.                 String exitNode = determineExitNode(afterAgentHooks);
  28.                 // Set up edges
  29.                 graph.addEdge(START, entryNode);
  30.                 setupHookEdges(graph, beforeAgentHooks, afterAgentHooks, beforeModelHooks, afterModelHooks,
  31.                                 entryNode, loopEntryNode, loopExitNode, exitNode, true, this);
  32.                 return graph;
复制代码
工具注入机制

Hook 可以通过 ToolInjection 接口注入工具:
关键代码
  1. private void setupToolsForHooks(List<Hook> hooks, AgentToolNode toolNode) {
  2.                 if (hooks == null || hooks.isEmpty() || toolNode == null) {
  3.                         return;
  4.                 }
  5.                 List<ToolCallback> availableTools = toolNode.getToolCallbacks();
  6.                 if (availableTools == null || availableTools.isEmpty()) {
  7.                         return;
  8.                 }
  9.                 for (Hook hook : hooks) {
  10.                         if (hook instanceof ToolInjection) {
  11.                                 ToolInjection toolInjection = (ToolInjection) hook;
  12.                                 ToolCallback toolToInject = findToolForHook(toolInjection, availableTools);
  13.                                 if (toolToInject != null) {
  14.                                         toolInjection.injectTool(toolToInject);
  15.                                 }
  16.                         }
  17.                 }
  18.         }
复制代码
具体实现

ModelCallLimitHook - 模型调用限制钩子

ModelCallLimitHook 限制模型调用次数,防止过度调用。
关键代码
  1. @HookPositions({HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL})
  2. public class ModelCallLimitHook implements ModelHook {
  3.         private static final String THREAD_COUNT_KEY = "__model_call_limit_thread_count__";
  4.         private static final String RUN_COUNT_KEY = "__model_call_limit_run_count__";
  5.         private final Integer threadLimit;
  6.         private final Integer runLimit;
  7.         private final ExitBehavior exitBehavior;
  8.         @Override
  9.         public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {
  10.                 // Read current counts from state
  11.                 int threadModelCallCount = state.value(THREAD_COUNT_KEY, Integer.class).orElse(0);
  12.                 int runModelCallCount = state.value(RUN_COUNT_KEY, Integer.class).orElse(0);
  13.                 // Check if limits are already exceeded (before making the call)
  14.                 boolean threadLimitExceeded = threadLimit != null && threadModelCallCount >= threadLimit;
  15.                 boolean runLimitExceeded = runLimit != null && runModelCallCount >= runLimit;
  16.                 if (threadLimitExceeded || runLimitExceeded) {
  17.                         if (exitBehavior == ExitBehavior.ERROR) {
  18.                                 throw new ModelCallLimitExceededException(...);
  19.                         }
  20.                         else if (exitBehavior == ExitBehavior.END) {
  21.                                 // Add message indicating limit was exceeded and jump to end
  22.                                 String message = buildLimitExceededMessage(...);
  23.                                 List<Message> messages = new ArrayList<>((List<Message>) state.value("messages")
  24.                                                 .orElse(new ArrayList<>()));
  25.                                 messages.add(new AssistantMessage(message));
  26.                                 Map<String, Object> updates = new HashMap<>();
  27.                                 updates.put("messages", messages);
  28.                                 return CompletableFuture.completedFuture(updates);
  29.                         }
  30.                 }
  31.                 return CompletableFuture.completedFuture(Map.of());
  32.         }
  33.         @Override
  34.         public CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {
  35.                 // Read current counts from state
  36.                 int threadModelCallCount = state.value(THREAD_COUNT_KEY, Integer.class).orElse(0);
  37.                 int runModelCallCount = state.value(RUN_COUNT_KEY, Integer.class).orElse(0);
  38.                 // Increment counters after the model call
  39.                 Map<String, Object> updates = new HashMap<>();
  40.                 updates.put(THREAD_COUNT_KEY, threadModelCallCount + 1);
  41.                 updates.put(RUN_COUNT_KEY, runModelCallCount + 1);
  42.                 return CompletableFuture.completedFuture(updates);
  43.         }
复制代码
功能

  • 跟踪线程级别和运行级别的模型调用次数
  • 达到限制时终止 Agent 或抛出异常
  • 支持跳转到 END
HumanInTheLoopHook

HumanInTheLoopHook 实现交互机制,允许人工干预 Agent 执行。
功能

  • 在模型调用后暂停执行
  • 等待人工输入或确认
  • 根据人工反馈决定继续或终止
PIIDetectionHook - PII 检测钩子

PIIDetectionHook 检测和处理个人身份信息(PII)。
功能

  • 检测消息中的 PII
  • 标记或过滤敏感信息
  • 保护用户隐私
关键类关系

以下 PlantUML 类图展示了 Hook 系统的类关系:
1.png

关键流程

以下 PlantUML 时序图展示了 Hook 的执行流程:
2.png

实现关键点说明

1. Graph 节点集成

Hook 被转换为 Graph 节点:

  • 每个 Hook 方法对应一个节点
  • 节点名称格式:{hookName}.{methodName}
  • 通过边连接形成执行链
2. 位置分类

Hook 按位置分类:

  • BEFORE_AGENT:Agent 执行前
  • AFTER_AGENT:Agent 执行后
  • BEFORE_MODEL:模型调用前
  • AFTER_MODEL:模型调用后
3. 链式执行

Hook 按顺序链式执行:

  • beforeAgent hooks 按顺序执行
  • beforeModel hooks 按顺序执行
  • afterModel hooks 按逆序执行
  • afterAgent hooks 按逆序执行
4. 跳转机制

Hook 可以通过 canJumpTo() 定义跳转能力:

  • JumpTo.tool:跳转到工具节点
  • JumpTo.model:跳转到模型节点
  • JumpTo.end:跳转到结束节点
跳转通过状态中的 jump_to 键控制。
5. 工具注入

Hook 可以通过 ToolInjection 接口注入工具:

  • 在 Graph 初始化时自动注入
  • 支持按名称或类型匹配
  • 用于 Hook 内部功能实现
6. 状态更新

Hook 通过返回 Map 更新状态:

  • 返回的 Map 会合并到当前状态
  • 支持键策略控制更新方式
  • 可以修改消息、添加元数据等
总结说明

核心设计理念


  • 生命周期钩子:在 Agent 和 Model 的关键生命周期点插入逻辑
  • Graph 集成:Hook 作为 Graph 节点集成到执行流程
  • 跳转控制:支持灵活的流程控制,可以跳转到不同节点
  • 工具注入:支持 Hook 使用工具实现复杂功能
关键优势


  • 灵活性:可以在多个位置插入自定义逻辑
  • 可扩展性:易于添加新的 Hook 实现
  • 流程控制:支持跳转机制实现复杂流程
  • 解耦设计:Hook 与核心逻辑解耦
解决的问题


  • 监控和限制:通过 ModelCallLimitHook 限制调用次数
  • 人工干预:通过 HumanInTheLoopHook 实现人工干预
  • 安全检测:通过 PIIDetectionHook 检测敏感信息
  • 工具集成:通过 ToolInjection 支持 Hook 使用工具
使用场景


  • ModelCallLimitHook:需要限制模型调用次数的场景
  • HumanInTheLoopHook:需要人工审核或确认的场景
  • PIIDetectionHook:需要保护用户隐私的场景
  • 自定义 Hook:需要扩展 Agent 功能的场景
Hook 机制为 Agent Framework 提供了强大的扩展能力,使开发者能够在 Agent 生命周期的关键点插入自定义逻辑,实现复杂的业务需求。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册