Spring AI Alibaba 1.x 系列【18】Hook 接口和四大抽象类

文章目录

  • [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. 概述

HookSpring 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 执行位置

HookAgent 执行流程中应该执行的位置。

执行顺序规则:如果实现类有 HookPositions 注解,使用其值。

否则,根据 Hook 类型使用默认位置:

  • AgentHookHookPosition#BEFORE_AGENTHookPosition#AFTER_AGENT
  • ModelHookHookPosition#BEFORE_MODELHookPosition#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 可以通过实现 beforeAgentafterAgent 方法,在 Agent 执行的开始和结束时进行预处理和后处理。

默认会在以下位置执行:

  • HookPosition#BEFORE_AGENTAgent 循环开始前
  • HookPosition#AFTER_AGENTAgent 循环结束后

典型应用场景:

  • 初始化 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 可以通过实现 beforeModelafterModel 方法, 在模型调用的前后进行预处理和后处理。

继承自 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 整体生命周期层面执行(beforeAgentafterAgent),而非在每次模型调用前后执行(beforeModelafterModel)。

主要功能:

  • 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 的执行流程。

MessagesModelHookbeforeModelafterModel 方法返回,包含以下信息:

  • 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 直接操作消息列表,提供更细粒度的消息处理能力。子类可以通过实现 beforeModelafterModel 方法来修改、过滤或增强消息。

主要功能:

  • 在模型调用前预处理消息列表
  • 在模型调用后后处理消息列表
  • 支持消息替换或追加策略
  • 支持流程跳转控制

典型应用场景:

  • 消息过滤:移除敏感信息或无用消息
  • 消息增强:添加上下文信息或系统提示
  • 消息压缩:压缩历史消息以控制 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);
        }
    }
相关推荐
yaoxin52112319 小时前
384. Java IO API - Java 文件复制工具:Copy 示例完整解析
java·开发语言·python
NotFound48620 小时前
实战指南如何实现Java Web 拦截机制:Filter 与 Interceptor 深度分享
java·开发语言·前端
Rubin智造社20 小时前
安全先行·自主编程|Claude Code Opus 4.7深度解读:AI开发进入合规量产时代
人工智能·anthropic·claude opus 4.7·mythos preview·xhigh努力等级·/ultrareview命令·自主开发ai
xinlianyq20 小时前
全球 AI 芯片格局生变:英伟达主导训练,国产算力崛起推理
人工智能
ShineWinsu20 小时前
AI训练硬件指南:GPU算力梯队与任务匹配框架
人工智能
范桂飓20 小时前
精选 Skills 清单
人工智能
码农的日常搅屎棍21 小时前
AIAgent开发新选择:OpenHarness极简入门指南
人工智能
AC赳赳老秦21 小时前
OpenClaw生成博客封面图+标题,适配CSDN视觉搜索,提升点击量
运维·人工智能·python·自动化·php·deepseek·openclaw
萝卜小白21 小时前
算法实习Day04-MinerU2.5-pro
人工智能·算法·机器学习
geneculture21 小时前
从人际间性到人机间性:进入人机互助新时代——兼论融智学视域下人类认知第二次大飞跃的理论奠基与实践场域
人工智能·融智学的重要应用·哲学与科学统一性·融智时代(杂志)·人际间性·人机间性·人际间文性