文章目录
- [1. 概述](#1. 概述)
- [2. Prioritized 接口](#2. Prioritized 接口)
- [3. Hook 接口](#3. Hook 接口)
-
- [3.1 基础属性方法](#3.1 基础属性方法)
- [3.2 默认扩展方法](#3.2 默认扩展方法)
-
- [3.2.1 拦截器注入](#3.2.1 拦截器注入)
- [3.2.2 工具注入](#3.2.2 工具注入)
- [3.2.3 执行流控制](#3.2.3 执行流控制)
- [3.2.4 状态管理](#3.2.4 状态管理)
- [3.2.5 执行位置](#3.2.5 执行位置)
- [3.3 静态方法](#3.3 静态方法)
- [4. 四大抽象类](#4. 四大抽象类)
-
- [4.1 AgentHook](#4.1 AgentHook)
- [4.2 ModelHook](#4.2 ModelHook)
- [4.3 MessagesAgentHook](#4.3 MessagesAgentHook)
-
- [4.3.1 AgentCommand](#4.3.1 AgentCommand)
- [4.3.2 BeforeAgentAction](#4.3.2 BeforeAgentAction)
- [4.3.3 AfterAgentAction](#4.3.3 AfterAgentAction)
- [4.4 MessagesModelHook](#4.4 MessagesModelHook)
-
- [4.4.1 BeforeModelAction](#4.4.1 BeforeModelAction)
- [4.4.1 AfterModelAction](#4.4.1 AfterModelAction)
1. 概述
Hook 是 Spring AI Alibaba Agent 框架中用于在 Agent 执行过程的关键节点注入自定义逻辑的机制。在执行前后埋点,用于监控、统计、状态修改。
2. Prioritized 接口
Hook 的执行顺序直接决定业务逻辑的正确性,Prioritized 作为框架顶层优先级接口,是所有可排序组件(Hook)的基础规范,统一管控执行优先级。
接口定义两个全局优先级常量,简化开发配置:
| 常量 | 值 | 含义 | 使用场景 |
|---|---|---|---|
| HIGHEST_PRECEDENCE | Integer.MIN_VALUE | 最高优先级 | 核心安全、权限、审核类 Hook |
| LOWEST_PRECEDENCE | Integer.MAX_VALUE | 最低优先级 | 日志、统计、收尾类 Hook |
排序规则(核心):
- 数值越小 → 优先级越高 → 执行越靠前
- 框架内部采用升序排序:
order=1优先于order=5 - 未指定优先级时,框架会赋予默认值(通常为
0)
接口源码如下:
java
/**
* 优先级排序接口
* <p>
* 定义了对象的优先级标准,实现该接口的对象可通过优先级数值进行排序。
* 核心规则:优先级数值越小,对象优先级越高,排序位置越靠前。
* </p>
* <p>
* 内置了最高优先级和最低优先级的常量,用于统一规范优先级边界值。
* </p>
*
* @author 通用优先级接口
* @since 1.0
*/
public interface Prioritized {
/**
* 最高优先级常量
* <p>
* 取值为整数最小值 {@link Integer#MIN_VALUE},代表最高优先级
* </p>
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* 最低优先级常量
* <p>
* 取值为整数最大值 {@link Integer#MAX_VALUE},代表最低优先级
* </p>
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* 获取当前对象的优先级数值
* <p>
* 该数值用于和其他 {@link Prioritized} 实现类对象进行优先级排序,
* 排序规则为 <b>升序排序</b>:数值小的对象排在数值大的对象前面。
* </p>
* <p>
* 示例:
* <ul>
* <li>优先级 1 的对象 → 排在 优先级 5 的对象前面</li>
* <li>优先级 10 的对象 → 排在 优先级 20 的对象前面</li>
* </ul>
* </p>
*
* @return 优先级数值(数值越小,优先级越高)
*/
int getOrder();
}
3. Hook 接口
Hook(钩子) 是 Agent 执行流程拦截、自定义扩展的顶层标准接口,允许您在 Agent 执行生命周期的特定点注入自定义逻辑,还可以提供拦截器、控制流程路由,并定义状态管理的键策略,这种设计模式类似于 AOP(面向切面编程),提供了横切关注点的处理能力。
主要有两种类型的 Hook:
AgentHook:在整个Agent循环之前/之后执行ModelHook:在模型调用之前/之后执行
继承关系:

3.1 基础属性方法
基础属性方法(必填实现):
| 方法 | 作用 |
|---|---|
| getName() | Hook 唯一标识,框架内部用于区分钩子 |
| setAgentName/getAgentName | 绑定所属 Agent 名称 |
| setAgent/getAgent | 持有 Agent 实例,支持状态 / 配置访问 |
java
/**
* 获取此Hook的唯一名称。
* 该名称用于在执行图中标识Hook。
*
* @return Hook名称,不能为null
*/
String getName();
/**
* 设置拥有此Hook的Agent名称。
* 通常在Agent初始化时由框架调用。
*
* @param agentName Agent名称,不能为null
*/
void setAgentName(String agentName);
/**
* 获取拥有此Hook的Agent名称。
*
* @return Agent名称,如果未设置则返回null
*/
String getAgentName();
/**
* 获取拥有此Hook的ReactAgent实例。
* 允许Hook访问Agent的状态和配置。
*
* @return ReactAgent实例,如果未设置则返回null
*/
ReactAgent getAgent();
/**
* 设置拥有此Hook的ReactAgent实例。
* 通常在Agent初始化时由框架调用。
*
* @param agent ReactAgent实例,不能为null
*/
void setAgent(ReactAgent agent);
3.2 默认扩展方法
3.2.1 拦截器注入
支持通过 Hook 注入模型拦截器、工具拦截器,Agent 在初始化时,会进行加载。
源码如下:
java
/**
* 获取此Hook提供的模型拦截器列表。
* <p>
* 模型拦截器可以修改模型请求/响应、添加重试逻辑、
* 实现护栏(guardrails),或提供其他横切关注点。
* <p>
* 这些拦截器将与Agent配置的拦截器合并,
* 并应用于所有模型调用。
*
* @return 模型拦截器列表,默认为空列表
* @see ModelInterceptor
*/
default List<ModelInterceptor> getModelInterceptors() {
return List.of();
}
/**
* 获取此Hook提供的工具拦截器列表。
* <p>
* 工具拦截器可以修改工具调用、添加重试逻辑、实现工具选择,
* 或为工具执行提供其他横切关注点。
* <p>
* 这些拦截器将与Agent配置的拦截器合并,
* 并应用于所有工具调用。
*
* @return 工具拦截器列表,默认为空列表
* @see ToolInterceptor
*/
default List<ToolInterceptor> getToolInterceptors() {
return List.of();
}
3.2.2 工具注入
支持通过 Hook 注入工具,Agent 在初始化时,会进行加载并自动合并到 Agent 工具集。
源码如下:
java
/**
* 获取此Hook提供的工具列表。
* <p>
* Hook提供的工具将与Agent配置的工具合并,
* 并在执行期间供Agent使用。
* <p>
* 这些工具将注册到Agent的工具节点,
* 并可在Agent执行期间被调用。
*
* @return 工具回调列表,默认为空列表
* @see ToolCallback
*/
default List<ToolCallback> getTools() {
return List.of();
}
3.2.3 执行流控制
获取此 Hook 允许的跳转目标列表。Hook 可以通过指定 Hook 执行后 Agent 可以跳转的位置来控制执行流程。
源码如下:
java
/**
* 获取此Hook允许的跳转目标列表。
* <p>
* Hook可以通过指定Hook执行后Agent可以跳转的位置来控制执行流程。
* 这允许Hook根据其逻辑重定向执行流程。
* <p>
* 常见的跳转目标包括:
* <ul>
* <li>{@link JumpTo#model}:跳转到模型节点</li>
* <li>{@link JumpTo#tool}:跳转到工具节点</li>
* <li>{@link JumpTo#end}:跳转到结束(终止Agent)</li>
* </ul>
*
* @return 允许的跳转目标列表,默认为空列表
* @see JumpTo
*/
default List<JumpTo> canJumpTo() {
return List.of();
}
工作流跳转目标枚举:
java
/**
* 代理工作流跳转目标枚举
* <p>
* 定义 AI 智能体工作流中可跳转的目标节点类型,
* 集成 Jackson 序列化/反序列化能力,支持字符串与枚举的自动转换,
* 且反序列化时支持<b>大小写不敏感</b>匹配。
* </p>
*
* @author AI Graph Agent
* @since 1.0.0
*/
public enum JumpTo {
/**
* 跳转至工具执行节点
*/
tool,
/**
* 跳转至大模型推理节点
*/
model,
/**
* 跳转至工作流结束节点
*/
end;
/**
* 获取枚举的字符串表示(用于 JSON 序列化)
* <p>
* 通过 {@link JsonValue} 注解指定序列化时使用枚举名称作为输出值
* </p>
* @return 枚举常量的名称字符串
*/
@Override
@JsonValue
public String toString() {
return name();
}
/**
* 字符串转换为枚举实例(用于 JSON 反序列化)
* <p>
* 支持大小写不敏感匹配,空值会直接抛出异常
* </p>
* @param value 待转换的字符串
* @return 匹配的 JumpTo 枚举实例
* @throws IllegalArgumentException 当值为 null 或无匹配枚举时抛出
*/
@JsonCreator
public static JumpTo fromString(String value) {
if (value == null) {
throw new IllegalArgumentException("JumpTo value cannot be null");
}
// 大小写不敏感匹配枚举常量
for (JumpTo jumpTo : values()) {
if (jumpTo.name().equalsIgnoreCase(value)) {
return jumpTo;
}
}
// 无匹配值时抛出明确异常
throw new IllegalArgumentException(
"Unknown JumpTo value: " + value + ". Valid values are: tool, model, end");
}
/**
* 安全的字符串转枚举方法(不抛出异常)
* <p>
* 若值为 null、空字符串或非法值,均返回 null,避免业务代码异常
* </p>
* @param value 待转换的字符串
* @return 匹配的枚举实例,匹配失败则返回 null
*/
public static JumpTo fromStringOrNull(String value) {
if (value == null || value.trim().isEmpty()) {
return null;
}
try {
return fromString(value);
}
catch (IllegalArgumentException e) {
return null;
}
}
}
3.2.4 状态管理
获取状态管理的键策略。键策略定义了更新 Agent 状态时状态值的合并方式,这允许 Hook 控制其状态更新如何与 Agent 的状态交互。
源码如下:
java
/**
* 获取状态管理的键策略。
* <p>
* 键策略定义了更新Agent状态时状态值的合并方式。
* 这允许Hook控制其状态更新如何与Agent的状态交互。
* <p>
* 常见的策略包括:
* <ul>
* <li>{@link com.alibaba.cloud.ai.graph.state.strategy.AppendStrategy}:追加到现有值</li>
* <li>{@link com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy}:替换现有值</li>
* </ul>
*
* @return 键名到策略的映射,默认为空映射
* @see KeyStrategy
*/
default Map<String, KeyStrategy> getKeyStrategys() {
return Map.of();
}
3.2.5 执行位置
Hook 在 Agent 执行流程中应该执行的位置。
执行顺序规则:如果实现类有 HookPositions 注解,使用其值。
否则,根据 Hook 类型使用默认位置:
AgentHook:HookPosition#BEFORE_AGENT、HookPosition#AFTER_AGENTModelHook:HookPosition#BEFORE_MODEL、HookPosition#AFTER_MODEL
如果都不适用,返回空数组(Hook 将不会被执行)
java
/**
* 获取此Hook在Agent执行流程中应该执行的位置。
*
* @return HookPosition值数组,指示此Hook应执行的位置
* @see HookPosition
* @see HookPositions
*/
default HookPosition[] getHookPositions() {
HookPositions annotation = this.getClass().getAnnotation(HookPositions.class);
if (annotation != null) {
return annotation.value();
}
// 根据Hook类型的默认回退
if (this instanceof AgentHook) {
return new HookPosition[]{HookPosition.BEFORE_AGENT, HookPosition.AFTER_AGENT};
} else if (this instanceof ModelHook) {
return new HookPosition[]{HookPosition.BEFORE_MODEL, HookPosition.AFTER_MODEL};
}
return new HookPosition[0];
}
HookPosition 枚举类定义了 Hook 执行位置:
java
public enum HookPosition {
BEFORE_AGENT, // Agent 循环开始前
AFTER_AGENT, // Agent 循环结束后
BEFORE_MODEL, // 每次 LLM 调用前
AFTER_MODEL // 每次 LLM 调用后
}
3.3 静态方法
为执行图中的 Hook 生成完整的限定名称:
java
/**
* 为执行图中的Hook生成完整的限定名称。
* <p>
* 完整名称通过将Hook名称加上Agent Hook名称前缀来构造。
* 这确保了Hook在图执行上下文中的唯一标识。
*
* @param hook Hook实例,不能为null
* @return 带前缀的完整Hook名称(例如 "agent.hook.myHook")
*/
static String getFullHookName(Hook hook) {
return AGENT_HOOK_NAME_PREFIX + hook.getName();
}
4. 四大抽象类
Hook 类型按操作对象分类表:
| 类型 | 说明 | 适用场景 |
|---|---|---|
| AgentHook | 直接操作 OverAllState | 需要访问完整状态、自定义状态键 |
| ModelHook | 直接操作 OverAllState | 需要访问完整状态、中断/跳转控制 |
| MessagesAgentHook | 操作 List<Message> | 只需要操作消息列表 |
| MessagesModelHook | 操作 List<Message> | 只需要操作消息列表 |
4.1 AgentHook
AgentHook 是针对 Agent 级别操作的 Hook 抽象类,用于在整个 Agent 循环执行前后注入自定义逻辑。继承此类的Hook 可以通过实现 beforeAgent 和 afterAgent 方法,在 Agent 执行的开始和结束时进行预处理和后处理。
默认会在以下位置执行:
HookPosition#BEFORE_AGENT:Agent循环开始前HookPosition#AFTER_AGENT:Agent循环结束后
典型应用场景:
- 初始化
Agent状态或上下文 - 记录
Agent执行的开始和结束时间 - 在
Agent执行后进行结果验证或清理 - 实现自定义的执行流程控制
核心属性:
java
/**
* Agent名称,标识拥有此Hook的Agent。
*/
private String agentName;
/**
* ReactAgent实例,允许Hook访问Agent的状态和配置。
*/
private ReactAgent reactAgent;
/**
* 获取Agent名称。
*
* @return Agent名称
*/
@Override
public String getAgentName() {
return agentName;
}
/**
* 设置Agent名称。
*
* @param agentName Agent名称
*/
@Override
public void setAgentName(String agentName) {
this.agentName = agentName;
}
/**
* 获取ReactAgent实例。
*
* @return ReactAgent实例
*/
@Override
public ReactAgent getAgent() {
return reactAgent;
}
/**
* 设置ReactAgent实例。
*
* @param reactAgent ReactAgent实例
*/
@Override
public void setAgent(ReactAgent reactAgent) {
this.reactAgent = reactAgent;
}
在 Agent 循环开始前执行的钩子方法:
java
/**
* 在Agent循环开始前执行的钩子方法。
* <p>
* 此方法在Agent开始执行前被调用,可以用于:
* <ul>
* <li>初始化Agent的状态</li>
* <li>设置执行上下文</li>
* <li>进行前置检查或验证</li>
* <li>记录执行开始时间</li>
* </ul>
* <p>
* 返回的Map将被合并到Agent的状态中。
*
* @param state 当前的全局状态
* @param config 运行配置
* @return 包含状态更新的CompletableFuture,默认返回空Map
*/
public CompletableFuture<Map<String, Object>> beforeAgent(OverAllState state, RunnableConfig config) {
return CompletableFuture.completedFuture(Map.of());
}
在 Agent 循环结束后执行的钩子方法
java
/**
* 在Agent循环结束后执行的钩子方法。
* <p>
* 此方法在Agent执行完成后被调用,可以用于:
* <ul>
* <li>处理Agent的执行结果</li>
* <li>进行后置验证或清理</li>
* <li>记录执行结束时间和统计信息</li>
* <li>触发后续操作或通知</li>
* </ul>
* <p>
* 返回的Map将被合并到Agent的状态中。
*
* @param state 当前的全局状态
* @param config 运行配置
* @return 包含状态更新的CompletableFuture,默认返回空Map
*/
public CompletableFuture<Map<String, Object>> afterAgent(OverAllState state, RunnableConfig config) {
return CompletableFuture.completedFuture(Map.of());
}
4.2 ModelHook
ModelHook 抽象类,用于在模型调用前后注入自定义逻辑。继承此类的 Hook 可以通过实现 beforeModel 和 afterModel 方法, 在模型调用的前后进行预处理和后处理。
继承自 Hook 接口,默认会在以下位置执行:
HookPosition#BEFORE_MODEL:模型调用前HookPosition#AFTER_MODEL:模型调用后
典型应用场景:
- 修改或增强发送给模型的请求
- 实现模型调用的重试逻辑
- 添加护栏(
guardrails)来过滤输入/输出 - 记录模型调用的输入和输出
- 实现模型调用超时控制
- 缓存模型响应以提高性能
属性定义:
java
public abstract class ModelHook implements Hook {
/**
* Agent名称,标识拥有此Hook的Agent。
*/
private String agentName;
/**
* ReactAgent实例,允许Hook访问Agent的状态和配置。
*/
private ReactAgent reactAgent;
在模型调用前执行的钩子方法:
java
/**
* 在模型调用前执行的钩子方法。
* <p>
* 此方法在模型调用前被调用,可以用于:
* <ul>
* <li>修改或增强模型请求参数</li>
* <li>添加系统提示或上下文信息</li>
* <li>实现输入验证或护栏</li>
* <li>记录模型请求内容</li>
* <li>实现请求缓存检查</li>
* </ul>
* <p>
* 返回的Map将被合并到Agent的状态中,可用于修改模型的输入。
*
* @param state 当前的全局状态
* @param config 运行配置
* @return 包含状态更新的CompletableFuture,默认返回空Map
*/
public CompletableFuture<Map<String, Object>> beforeModel(OverAllState state, RunnableConfig config) {
return CompletableFuture.completedFuture(Map.of());
}
在模型调用后执行的钩子方法:
java
/**
* 在模型调用后执行的钩子方法。
* <p>
* 此方法在模型调用后被调用,可以用于:
* <ul>
* <li>处理或转换模型的响应</li>
* <li>实现输出验证或护栏</li>
* <li>记录模型响应内容</li>
* <li>缓存模型响应</li>
* <li>进行错误处理或重试判断</li>
* </ul>
* <p>
* 返回的Map将被合并到Agent的状态中,可用于修改或增强模型的输出。
*
* @param state 当前的全局状态
* @param config 运行配置
* @return 包含状态更新的CompletableFuture,默认返回空Map
*/
public CompletableFuture<Map<String, Object>> afterModel(OverAllState state, RunnableConfig config) {
return CompletableFuture.completedFuture(Map.of());
}
MessagesAgentHook
4.3 MessagesAgentHook
消息级别的 Agent Hook 抽象类,用于在 Agent 整体执行前后处理消息列表。与 MessagesModelHook 类似,MessagesAgentHook 直接操作消息列表,但它在 Agent 整体生命周期层面执行(beforeAgent 和 afterAgent),而非在每次模型调用前后执行(beforeModel 和 afterModel)。
主要功能:
- 在
Agent开始执行前预处理消息列表 - 在
Agent执行结束后后处理消息列表 - 支持消息替换或追加策略
- 支持流程跳转控制
典型应用场景:
- 对话历史初始化:在
Agent开始前设置初始消息 - 对话历史保存:在
Agent结束后保存完整对话历史 - 消息归档整理:整理和归档
Agent执行期间产生的所有消息 - 对话上下文管理:管理整个对话周期的上下文状态
- 执行统计和报告:生成
Agent执行过程的完整报告
与 MessagesModelHook 的区别
- 执行时机:
Agent级别 vs 模型调用级别 - 执行频率:整个
Agent执行一次vs每次模型调用都执行 - 处理范围:整个对话周期
vs单次模型调用
属性定义:
java
/**
* Agent 名称,标识拥有此 Hook 的 Agent。
*/
private String agentName;
/**
* 设置 Agent 名称。
*
* @param agentName Agent 名称
*/
public void setAgentName(String agentName) {
this.agentName = agentName;
}
/**
* 获取 Agent 名称。
*
* @return Agent 名称
*/
public String getAgentName() {
return agentName;
}
在 Agent 开始执行前执行的钩子方法(只执行一次):
java
/**
* 在 Agent 开始执行前执行的钩子方法。
* <p>
* 此方法在 Agent 整体执行前被调用(只执行一次),可以用于:
* <ul>
* <li>初始化对话历史消息</li>
* <li>添加系统提示或初始上下文</li>
* <li>恢复之前保存的对话状态</li>
* <li>设置执行前的检查条件</li>
* </ul>
* <p>
* 返回的 {@link AgentCommand} 包含:
* <ul>
* <li>处理后的消息列表</li>
* <li>消息更新策略(替换或追加)</li>
* <li>可选的流程跳转指令</li>
* </ul>
* <p>
* 默认实现返回原始消息列表,不做任何修改。
*
* @param previousMessages 当前的消息列表
* @param config 运行配置
* @return AgentCommand 包含处理后的消息和可选的跳转指令
*/
public AgentCommand beforeAgent(List<Message> previousMessages, RunnableConfig config) {
return new AgentCommand(previousMessages);
}
在 Agent 执行结束后执行的钩子方法(只执行一次):
java
/**
* 在 Agent 执行结束后执行的钩子方法。
* <p>
* 此方法在 Agent 整体执行后被调用(只执行一次),可以用于:
* <ul>
* <li>保存完整的对话历史</li>
* <li>归档整理所有消息</li>
* <li>生成执行报告</li>
* <li>清理临时状态</li>
* <li>触发后续处理流程</li>
* </ul>
* <p>
* 返回的 {@link AgentCommand} 包含:
* <ul>
* <li>处理后的消息列表</li>
* <li>消息更新策略(替换或追加)</li>
* <li>可选的流程跳转指令</li>
* </ul>
* <p>
* 默认实现返回原始消息列表,不做任何修改。
*
* @param previousMessages 当前的消息列表(包含整个执行过程的对话历史)
* @param config 运行配置
* @return AgentCommand 包含处理后的消息和可选的跳转指令
*/
public AgentCommand afterAgent(List<Message> previousMessages, RunnableConfig config) {
return new AgentCommand(previousMessages);
}
为给定的 MessagesAgentHook 创建 BeforeAgentAction 实例静态方法:
java
/**
* 为给定的 MessagesAgentHook 创建 BeforeAgentAction 实例。
* <p>
* BeforeAgentAction 实现了 {@link AsyncNodeActionWithConfig} 接口,
* 可以作为图节点执行 beforeAgent 钩子逻辑。
*
* @param hook 要代理的 MessagesAgentHook 实例
* @return BeforeAgentAction 实例
*/
public static BeforeAgentAction beforeAgentAction(MessagesAgentHook hook) {
return new BeforeAgentAction(hook);
}
为给定的 MessagesAgentHook 创建 AfterAgentAction 实例静态方法:
java
/**
* 为给定的 MessagesAgentHook 创建 AfterAgentAction 实例。
* <p>
* AfterAgentAction 实现了 {@link AsyncNodeActionWithConfig} 接口,
* 可以作为图节点执行 afterAgent 钩子逻辑。
*
* @param hook 要代理的 MessagesAgentHook 实例
* @return AfterAgentAction 实例
*/
public static AfterAgentAction afterAgentAction(MessagesAgentHook hook) {
return new AfterAgentAction(hook);
}
4.3.1 AgentCommand
Agent 命令对象,用于封装 Hook 处理后的结果,此对象允许 Hook 不仅修改消息,还能控制 Agent 的执行流程。
由 MessagesModelHook 的 beforeModel 和 afterModel 方法返回,包含以下信息:
messages: 处理后的消息列表updatePolicy: 消息更新策略(替换或追加)jumpTo: 可选的流程跳转目标
源码如下:
java
public class AgentCommand {
/**
* 处理后的消息列表。
*/
private final List<Message> messages;
/**
* 消息更新策略。
*/
private final UpdatePolicy updatePolicy;
/**
* 流程跳转目标(可选)。
*/
private final JumpTo jumpTo;
/**
* 构造函数,使用默认更新策略(APPEND)和无跳转。
*
* @param messages 消息列表
*/
public AgentCommand(List<Message> messages) {
this(messages, UpdatePolicy.APPEND, null);
}
/**
* 构造函数,指定消息列表和更新策略。
*
* @param messages 消息列表
* @param updatePolicy 更新策略
*/
public AgentCommand(List<Message> messages, UpdatePolicy updatePolicy) {
this(messages, updatePolicy, null);
}
/**
* 完整构造函数。
*
* @param messages 消息列表
* @param updatePolicy 更新策略
* @param jumpTo 跳转目标
*/
public AgentCommand(List<Message> messages, UpdatePolicy updatePolicy, JumpTo jumpTo) {
this.messages = messages;
this.updatePolicy = updatePolicy;
this.jumpTo = jumpTo;
}
/**
* 获取消息列表。
*
* @return 消息列表
*/
public List<Message> getMessages() {
return messages;
}
/**
* 获取更新策略。
*
* @return 更新策略
*/
public UpdatePolicy getUpdatePolicy() {
return updatePolicy;
}
/**
* 获取跳转目标。
*
* @return 跳转目标,如果无跳转则返回 null
*/
public JumpTo getJumpTo() {
return jumpTo;
}
/**
* 创建 Builder 实例。
*
* @return Builder 实例
*/
public static Builder builder() {
return new Builder();
}
/**
* Builder 类,用于构建 AgentCommand 实例。
*/
public static class Builder {
private List<Message> messages;
private UpdatePolicy updatePolicy = UpdatePolicy.APPEND;
private JumpTo jumpTo;
/**
* 设置消息列表。
*
* @param messages 消息列表
* @return Builder 实例
*/
public Builder messages(List<Message> messages) {
this.messages = messages;
return this;
}
/**
* 设置更新策略。
*
* @param updatePolicy 更新策略
* @return Builder 实例
*/
public Builder updatePolicy(UpdatePolicy updatePolicy) {
this.updatePolicy = updatePolicy;
return this;
}
/**
* 设置跳转目标。
*
* @param jumpTo 跳转目标
* @return Builder 实例
*/
public Builder jumpTo(JumpTo jumpTo) {
this.jumpTo = jumpTo;
return this;
}
/**
* 构建 AgentCommand 实例。
*
* @return AgentCommand 实例
*/
public AgentCommand build() {
return new AgentCommand(messages, updatePolicy, jumpTo);
}
}
}
4.3.2 BeforeAgentAction
Agent 执行前的适配器,将 MessagesAgentHook#beforeAgent 业务方法,适配为 AI 图引擎可执行的节点动作。
java
/**
* 静态内部类:Agent 执行前 动作适配器
* 实现引擎接口 AsyncNodeActionWithConfig,作为图节点执行
*/
public static class BeforeAgentAction implements AsyncNodeActionWithConfig {
// 持有外部类的实例(核心:调用业务方法)
private final MessagesAgentHook messagesAgentHook;
// 构造函数:注入外部类实例
public BeforeAgentAction(MessagesAgentHook messagesAgentHook) {
this.messagesAgentHook = messagesAgentHook;
}
/**
* 引擎强制实现的方法:执行核心逻辑
* 输入:全局状态、运行配置
* 输出:异步结果(状态更新 Map)
*/
@Override
public CompletableFuture<Map<String, Object>> apply(OverAllState state, RunnableConfig config) {
// 1. 从全局状态中获取消息列表
List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());
// 2. 调用外部类的业务方法:执行 Agent 执行前的钩子逻辑
AgentCommand command = messagesAgentHook.beforeAgent(messages, config);
// 3. 封装结果:转换为引擎可识别的状态更新
Map<String, Object> result = new HashMap<>();
if (command.getMessages() != null) {
// 替换消息 / 追加消息(根据策略)
if (UpdatePolicy.REPLACE == command.getUpdatePolicy()) {
result.put("messages", ReplaceAllWith.of(command.getMessages()));
} else {
result.put("messages", command.getMessages());
}
}
// 4. 封装流程跳转指令
if (command.getJumpTo() != null) {
result.put("jump_to", command.getJumpTo().name());
}
// 5. 返回异步结果(引擎要求)
return CompletableFuture.completedFuture(result);
}
}
4.3.3 AfterAgentAction
Agent 执行后的适配器,将 MessagesAgentHook#afterAgent 业务方法,适配为 AI 图引擎可执行的节点动作。
java
/**
* 静态内部类:Agent 执行后 动作适配器
* 实现引擎接口 AsyncNodeActionWithConfig,作为图节点执行
*/
public static class AfterAgentAction implements AsyncNodeActionWithConfig {
// 持有外部类实例
private final MessagesAgentHook messagesAgentHook;
// 构造函数注入
public AfterAgentAction(MessagesAgentHook messagesAgentHook) {
this.messagesAgentHook = messagesAgentHook;
}
/**
* 引擎执行方法:逻辑和 BeforeAgentAction 完全一致
* 唯一区别:调用的是 afterAgent() 业务方法
*/
@Override
public CompletableFuture<Map<String, Object>> apply(OverAllState state, RunnableConfig config) {
// 1. 获取消息
List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());
// 2. 调用外部类:Agent 执行后钩子
AgentCommand command = messagesAgentHook.afterAgent(messages, config);
// 3. 封装状态更新
Map<String, Object> result = new HashMap<>();
if (command.getMessages() != null) {
if (UpdatePolicy.REPLACE == command.getUpdatePolicy()) {
result.put("messages", ReplaceAllWith.of(command.getMessages()));
} else {
result.put("messages", command.getMessages());
}
}
if (command.getJumpTo() != null) {
result.put("jump_to", command.getJumpTo().name());
}
return CompletableFuture.completedFuture(result);
}
}
4.4 MessagesModelHook
消息级别的模型 Hook 抽象类,用于在模型调用前后处理消息列表。
与标准的 ModelHook 不同,MessagesModelHook 直接操作消息列表,提供更细粒度的消息处理能力。子类可以通过实现 beforeModel 和 afterModel 方法来修改、过滤或增强消息。
主要功能:
- 在模型调用前预处理消息列表
- 在模型调用后后处理消息列表
- 支持消息替换或追加策略
- 支持流程跳转控制
典型应用场景:
- 消息过滤:移除敏感信息或无用消息
- 消息增强:添加上下文信息或系统提示
- 消息压缩:压缩历史消息以控制
token数量 - 消息验证:验证消息格式和内容
- 流程控制:根据消息内容决定跳转到不同节点
属性定义:
java
/**
* Agent 名称,标识拥有此 Hook 的 Agent。
*/
private String agentName;
/**
* ReactAgent 实例,允许 Hook 访问 Agent 的状态和配置。
*/
private ReactAgent agent;
在模型调用前执行的钩子方法:
java
/**
* 在模型调用前执行的钩子方法。
* <p>
* 此方法接收当前的消息列表,可以对其进行修改、过滤或增强。
* 返回的 {@link AgentCommand} 包含:
* <ul>
* <li>修改后的消息列表</li>
* <li>消息更新策略(替换或追加)</li>
* <li>可选的流程跳转指令</li>
* </ul>
* <p>
* 默认实现返回原始消息列表,不做任何修改。
*
* @param previousMessages 当前的消息列表
* @param config 运行配置
* @return AgentCommand 包含处理后的消息和可选的跳转指令
*/
public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
return new AgentCommand(previousMessages);
}
在模型调用后执行的钩子方法:
java
/**
* 在模型调用后执行的钩子方法。
* <p>
* 此方法接收模型调用后的消息列表,可以对其进行修改或验证。
* 返回的 {@link AgentCommand} 包含:
* <ul>
* <li>修改后的消息列表</li>
* <li>消息更新策略(替换或追加)</li>
* <li>可选的流程跳转指令</li>
* </ul>
* <p>
* 默认实现返回原始消息列表,不做任何修改。
*
* @param previousMessages 当前的消息列表(包含模型响应)
* @param config 运行配置
* @return AgentCommand 包含处理后的消息和可选的跳转指令
*/
public AgentCommand afterModel(List<Message> previousMessages, RunnableConfig config) {
return new AgentCommand(previousMessages);
}
为给定的 MessagesModelHook 创建 BeforeModelAction 实例:
java
/**
* 为给定的 MessagesModelHook 创建 BeforeModelAction 实例。
* <p>
* BeforeModelAction 实现了 {@link AsyncNodeActionWithConfig} 接口,
* 可以作为图节点执行 beforeModel 钩子逻辑。
*
* @param hook 要代理的 MessagesModelHook 实例
* @return BeforeModelAction 实例
*/
public static BeforeModelAction beforeModelAction(MessagesModelHook hook) {
return new BeforeModelAction(hook);
}
为给定的 MessagesModelHook 创建 AfterModelAction 实例:
java
/**
* 为给定的 MessagesModelHook 创建 AfterModelAction 实例。
* <p>
* AfterModelAction 实现了 {@link AsyncNodeActionWithConfig} 接口,
* 可以作为图节点执行 afterModel 钩子逻辑。
*
* @param hook 要代理的 MessagesModelHook 实例
* @return AfterModelAction 实例
*/
public static AfterModelAction afterModelAction(MessagesModelHook hook) {
return new AfterModelAction(hook);
}
4.4.1 BeforeModelAction
内部静态类,代理 MessagesModelHook 并实现 AsyncNodeActionWithConfig 接口。
java
/**
* 内部静态类,代理 MessagesModelHook 并实现 AsyncNodeActionWithConfig 接口。
* <p>
* 此类将 beforeModel 钩子的调用结果转换为状态更新:
* <ul>
* <li>将 AgentCommand 中的消息列表转换为状态中的 "messages" 键</li>
* <li>根据 UpdatePolicy 决定是替换还是追加消息</li>
* <li>如果有跳转指令,设置 "jump_to" 状态键</li>
* </ul>
*/
public static class BeforeModelAction implements AsyncNodeActionWithConfig {
/**
* 被代理的 MessagesModelHook 实例。
*/
private final MessagesModelHook messagesModelHook;
/**
* 构造函数。
*
* @param messagesModelHook 要代理的 MessagesModelHook 实例
*/
public BeforeModelAction(MessagesModelHook messagesModelHook) {
this.messagesModelHook = messagesModelHook;
}
/**
* 执行 beforeModel 钩子并将结果转换为状态更新。
* <p>
* 执行流程:
* <ol>
* <li>从状态中获取当前消息列表</li>
* <li>调用 messagesModelHook.beforeModel()</li>
* <li>将 AgentCommand 转换为状态更新 Map</li>
* </ol>
*
* @param state 当前的全局状态
* @param config 运行配置
* @return 包含状态更新的 CompletableFuture
*/
@Override
public CompletableFuture<Map<String, Object>> apply(OverAllState state, RunnableConfig config) {
@SuppressWarnings("unchecked")
List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());
AgentCommand command = messagesModelHook.beforeModel(messages, config);
Map<String, Object> result = new HashMap<>();
if (command.getMessages() != null) {
if (UpdatePolicy.REPLACE == command.getUpdatePolicy()) {
// 使用 ReplaceAllWith 替换所有消息
result.put("messages", ReplaceAllWith.of(command.getMessages()));
} else {
// 默认追加消息
result.put("messages", command.getMessages());
}
}
if (command.getJumpTo() != null) {
// 设置跳转目标
result.put("jump_to", command.getJumpTo().name());
}
return CompletableFuture.completedFuture(result);
}
}
4.4.1 AfterModelAction
内部静态类,代理 MessagesModelHook 并实现 AsyncNodeActionWithConfig 接口。
java
/**
* 内部静态类,代理 MessagesModelHook 并实现 AsyncNodeActionWithConfig 接口,
* 用于 afterModel 钩子。
* <p>
* 此类将 afterModel 钩子的调用结果转换为状态更新:
* <ul>
* <li>将 AgentCommand 中的消息列表转换为状态中的 "messages" 键</li>
* <li>根据 UpdatePolicy 决定是替换还是追加消息</li>
* <li>如果有跳转指令,设置 "jump_to" 状态键</li>
* </ul>
*/
public static class AfterModelAction implements AsyncNodeActionWithConfig {
/**
* 被代理的 MessagesModelHook 实例。
*/
private final MessagesModelHook messagesModelHook;
/**
* 构造函数。
*
* @param messagesModelHook 要代理的 MessagesModelHook 实例
*/
public AfterModelAction(MessagesModelHook messagesModelHook) {
this.messagesModelHook = messagesModelHook;
}
/**
* 执行 afterModel 钩子并将结果转换为状态更新。
* <p>
* 执行流程:
* <ol>
* <li>从状态中获取当前消息列表</li>
* <li>调用 messagesModelHook.afterModel()</li>
* <li>将 AgentCommand 转换为状态更新 Map</li>
* </ol>
*
* @param state 当前的全局状态
* @param config 运行配置
* @return 包含状态更新的 CompletableFuture
*/
@Override
public CompletableFuture<Map<String, Object>> apply(OverAllState state, RunnableConfig config) {
@SuppressWarnings("unchecked")
List<Message> messages = (List<Message>) state.value("messages").orElse(List.of());
AgentCommand command = messagesModelHook.afterModel(messages, config);
Map<String, Object> result = new HashMap<>();
if (command.getMessages() != null) {
if (UpdatePolicy.REPLACE == command.getUpdatePolicy()) {
// 使用 ReplaceAllWith 替换所有消息
result.put("messages", ReplaceAllWith.of(command.getMessages()));
} else {
// 默认追加消息
result.put("messages", command.getMessages());
}
}
if (command.getJumpTo() != null) {
// 设置跳转目标
result.put("jump_to", command.getJumpTo().name());
}
return CompletableFuture.completedFuture(result);
}
}