react - ReActInterceptor 拦截器
2026年8月3日 下午3:54:00
1、内置拦截器
| 拦截器 | 名称 | 描述 |
|---|---|---|
| HITLInterceptor | 人工介入拦截器 | 该拦截器通过 ReAct 协议的生命周期钩子实现流程管控 |
| StopLoopInterceptor | 逻辑死循环拦截器 | 该拦截器通过监控 LLM 的输出内容指纹,防止智能体陷入无效的迭代循环 |
| ContextCompressionInterceptor | 智能上下文压缩拦截器 | 该拦截器通过“滑动窗口”机制,在保证 ReAct 逻辑链完整性的前提下,对 ReActTrace 历史消息进行截断压缩。 |
| ToolRetryInterceptor | 工具执行重试拦截器 | 该拦截器为 ReAct 模式下的工具调用提供韧性支持,具备物理重试与逻辑自愈双重机制。 |
| ToolSanitizerInterceptor | 工具结果净化拦截器 | 该拦截器在 ReAct 模式的 Observation 阶段执行,负责对工具返回的原始数据进行加工。 |
2、注册与启停
拦截器通过 options 注册,支持指定顺序(index 越小越先执行):
ReActAgent agent = ReActAgent.of(chatModel)
.options(o -> o
.interceptorAdd(new ContextCompressionInterceptor())
.interceptorAdd(new MyAuditInterceptor(), 100))
.build();
自定义拦截器建议继承 AbsReActInterceptor(4.0.0+),它实现了 isEnabled() / setEnabled(),可在运行期动态开关:
public class MyAuditInterceptor extends AbsReActInterceptor {
@Override
public void onToolCallStart(ReActTrace trace, ToolExchanger toolExchanger) {
//权限预检、参数合法性校验
}
}
若直接实现 ReActInterceptor 接口,isEnabled() 默认返回 true,setEnabled() 为空实现(即无法关闭)。
注意:
isEnabled()由框架在调用各时机点前检查,但覆盖并不完全。onReasonEnd、onThought目前不受 enabled 开关约束,编写时不要把“关闭即完全静默”当作前提。
3、ReActInterceptor 各时机点说明
时机点按一轮 ReAct 循环的真实执行顺序排列:
| 层级 | 拦截方法 | 触发时机 | 典型应用场景 |
|---|---|---|---|
| 智能体级 | onAgentStart | 智能体开始运行(初始化后) | 记录全流程追踪 ID、预加载 Session 数据 |
| onAgentEnd | 智能体完成任务(Final Answer)或达到步数限制 | 统计总 Token 消耗、清理临时资源、持久化轨迹 | |
| 循环级(Reason) | onReasonStart | systemPrompt 构建完成、消息组装之前 | 上下文压缩、工作记忆窗口管理、注入动态指令(可直接改写 systemPromptBuf) |
| onReasonRetry | Reason 请求失败且即将重试前(4.0.4+) | 按异常收紧上下文(如 Token 超限时加大压缩力度);返回 true 表示已改写上下文,下一次尝试会重新组装消息 | |
| onReasonEnd | 接收到 LLM 返回的完整推理消息,尚未拆解为具体 Action/Thought 之前 | 解析自定义标签(如非标准 Action 解析)、手动截断推理过程、提取推理元数据、统计单轮耗时 | |
| onThought | (4.0.4 弃用,改用 onReasonEnd)模型推理结果解析出 Thought 部分时 | UI 打字机效果展示、记录思维链(CoT)日志 | |
| 循环级(Action) | onActionStart | 本轮全部工具调用解析完成、逐个执行之前(4.0.4+) | 批量审批(HITL 批挂起)、整体权限校验、批量改参 |
| onToolCallStart | 单个工具调用执行前 | 单工具权限控制、参数合法性预检 | |
| onPlan | 规划模式下,模型调用 create_plan / update_plan_progress 时(属于工具执行过程) | 审核并修正智能体生成的行动计划,注入强制约束 | |
| onToolCallEnd | 单个工具执行完成后(强闭环,位于 finally 块) | 观测外部系统返回数据、敏感信息脱敏、数据清洗、改写 result | |
| onObservation | (4.0.4 弃用,改用 onToolCallEnd)同上 | 同上 | |
| onActionEnd | 本轮全部工具执行完毕后(4.0.4+;会话挂起时不触发) | 批量结果汇总、清理批次状态 |
关于强闭环:onToolCallEnd 放在 finally 块中,无论成功、失败、挂起、中断都保证被调用。而 onActionEnd 在会话挂起(Pending)时会被跳过,避免前端误判本轮 Action 已执行完毕。
由于 ReActInterceptor 具备多重身份,还可以覆盖以下底层方法实现更精细的控制:
| 继承自 | 拦截方法 | 作用描述 |
|---|---|---|
| ChatInterceptor | onPrepare | 在构建 ChatModel 请求之前触发,可动态调整 ChatOptions、追加系统指令。 |
| ChatInterceptor | interceptCall | 作用于最底层的 ChatModel 同步调用,可直接操作底层的 ChatRequest/Response。 |
| ChatInterceptor | interceptStream | 作用于最底层的 ChatModel 流式调用。 |
| ToolInterceptor | interceptTool | 最实用的扩展点:可以直接拦截工具的执行链。例如:如果某工具返回 404,拦截器可以直接伪造一个“请检查参数”的返回给模型。 |
| ToolInterceptor | isEnabled / setEnabled | 拦截器启停开关。 |
4、ReActInterceptor 拦截器接口参考
ReActInterceptor 同时继承了 AgentInterceptor 和 ChatInterceptor(而 ChatInterceptor 又继承 ToolInterceptor),所以它除了 ReAct 生命周期,还可以拦截聊天模型(ChatModel)与工具(Tool)的执行。
ReActInterceptor
package org.noear.solon.ai.agent.react;
import org.noear.solon.ai.agent.AgentInterceptor;
import org.noear.solon.ai.agent.react.task.ToolExchanger;
import org.noear.solon.ai.chat.ChatResponse;
import org.noear.solon.ai.chat.interceptor.ChatInterceptor;
import org.noear.solon.ai.chat.message.AssistantMessage;
import org.noear.solon.ai.chat.message.ChatMessage;
import org.noear.solon.lang.Nullable;
import org.noear.solon.lang.Preview;
import java.util.Collection;
/**
* ReAct 智能体拦截器
* <p>提供对智能体起止、模型推理、工具执行等全生命周期的监控与干预能力</p>
*
* @author noear
* @since 3.8.1
*/
@Preview("3.8.1")
public interface ReActInterceptor extends AgentInterceptor, ChatInterceptor {
/**
* 智能体生命周期:开始执行前
*/
default void onAgentStart(ReActTrace trace) {
}
/**
* 推理节点:Reason 阶段开始前(在 systemPrompt 构建和消息组装之前触发)
* <p>适合做上下文压缩、工作记忆窗口管理等预处理操作</p>
*/
default void onReasonStart(ReActTrace trace, StringBuilder systemPromptBuf) {
}
/**
* 推理节点:Reason 请求失败且即将再次尝试时触发。
* <p>拦截器可以在此根据异常调整 WorkingMemory。返回 true 表示已为下一次请求
* 修改了请求上下文;ReasonTask 会在下一次尝试中重新组装消息和请求。</p>
*
* @param trace 当前推理追踪
* @param error 本次请求异常
* @param attempt 即将进行的尝试序号(从 1 开始)
* @param systemPrompt 当前 Reason 使用的系统提示词
* @return 是否修改了下一次请求所依赖的上下文
* @since 4.0.4
*/
default boolean onReasonRetry(ReActTrace trace, Throwable error, int attempt, String systemPrompt) {
return false;
}
/**
* 推理节点:接收 LLM 返回的原始推理消息
*/
default void onReasonEnd(ReActTrace trace, ChatResponse resp, AssistantMessage message, long durationMs) {
}
/**
* 计划节点:接收 LLM 返回的原始推理消息
*/
default void onPlan(ReActTrace trace, AssistantMessage message) {
}
/**
* 动作节点:本轮工具调用解析完成、开始逐个执行前触发
*
* @since 4.0.4
*/
default void onActionStart(ReActTrace trace, Collection<ToolExchanger> toolCalls) {
}
/**
* 动作节点:调用功能工具 (Action) 前触发
* <p>可用于权限控制、参数合法性预检</p>
*/
default void onToolCallStart(ReActTrace trace, ToolExchanger toolExchanger) {
}
/**
* 观察节点:工具执行完成后触发(100% 强闭环,放在 finally 块中)
* <p>无论成功、失败、挂起、中断,此方法保证被调用</p>
*
* @param trace ReAct 追踪上下文
* @param toolExchanger 工具交换器(含 toolName、args、result)
* @param observation 观察结果消息(成功时为工具输出,失败时为错误描述;挂起/中断时为空消息)
* @param error 执行异常(成功时为 null)
* @param durationMs 工具执行耗时(毫秒)
*/
default void onToolCallEnd(ReActTrace trace, ToolExchanger toolExchanger,
@Nullable ChatMessage observation,
@Nullable Throwable error,
long durationMs) {
}
/**
* 动作节点:本轮全部工具执行完毕后触发(会话挂起时不触发)
*
* @since 4.0.4
*/
default void onActionEnd(ReActTrace trace, Collection<ToolExchanger> toolCalls) {
}
/**
* 智能体生命周期:任务结束(成功或异常中止)时触发
*/
default void onAgentEnd(ReActTrace trace) {
}
//-------------------
/**
* 思考节点:Reason 阶段完成后触发
* <p>无论是否解析出有效的 thoughtContent,此方法都会被调用</p>
*
* @param trace ReAct 追踪上下文
* @param thoughtContent 提取后的思考内容(可能为空字符串)
* @param assistantMessage 原始 LLM 响应消息(含 toolCalls、content、reasoning 等完整信息)
* @deprecated 4.0.4 {@link #onReasonEnd(ReActTrace, ChatResponse, AssistantMessage, long)}
*/
@Deprecated
default void onThought(ReActTrace trace, String thoughtContent, AssistantMessage assistantMessage) {
}
/**
* 动作节点:调用功能工具 (Action) 前触发
* <p>可用于权限控制、参数合法性预检</p>
*
* @deprecated 4.0.4 {@link #onToolCallStart(ReActTrace, ToolExchanger)}
*/
@Deprecated
default void onAction(ReActTrace trace, ToolExchanger toolExchanger) {
}
/**
* 观察节点:工具执行完成后触发(100% 强闭环,放在 finally 块中)
* <p>无论成功、失败、挂起、中断,此方法保证被调用</p>
*
* @param trace ReAct 追踪上下文
* @param toolExchanger 工具交换器(含 toolName、args、result)
* @param observation 观察结果消息(成功时为工具输出,失败时为错误描述;挂起/中断时为空消息)
* @param error 执行异常(成功时为 null)
* @param durationMs 工具执行耗时(毫秒)
* @deprecated 4.0.4 {@link #onToolCallEnd(ReActTrace, ToolExchanger, ChatMessage, Throwable, long)}
*/
@Deprecated
default void onObservation(ReActTrace trace, ToolExchanger toolExchanger,
@Nullable ChatMessage observation,
@Nullable Throwable error,
long durationMs) {
}
}
ChatInterceptor
package org.noear.solon.ai.chat.interceptor;
import org.noear.solon.ai.chat.ChatOptions;
import org.noear.solon.ai.chat.ChatRequest;
import org.noear.solon.ai.chat.ChatResponse;
import org.noear.solon.ai.chat.ChatSession;
import org.noear.solon.ai.chat.prompt.Prompt;
import reactor.core.publisher.Flux;
import java.io.IOException;
/**
* 聊天拦截器
*
* @author noear
* @since 3.3
*/
public interface ChatInterceptor extends ToolInterceptor {
/**
* 预处理(在构建请求之前触发)
* <p>用于动态调整配置、补充或修改提示词(Prompt)以及注入系统指令</p>
*
* @param session 当前聊天会话(可用于获取历史消息、元数据或状态标记)
* @param options 聊天配置(可修改,影响模型参数等)
* @param originalPrompt 原始提示词(包含用户消息和上下文)
* @param systemMessage 系统指令容器(可追加,将作为 System Message 发送)
*/
default void onPrepare(ChatSession session, ChatOptions options, Prompt originalPrompt, StringBuilder systemMessage){
}
/**
* 拦截 Call 请求
*
* @param req 请求
* @param chain 拦截链
*/
default ChatResponse interceptCall(ChatRequest req, CallChain chain) throws IOException {
return chain.doIntercept(req);
}
/**
* 拦截 Stream 请求
*
* @param req 请求
* @param chain 拦截链
*/
default Flux<ChatResponse> interceptStream(ChatRequest req, StreamChain chain) {
return chain.doIntercept(req);
}
}
ToolInterceptor
package org.noear.solon.ai.chat.interceptor;
import org.noear.solon.ai.chat.tool.ToolResult;
/**
* 工具拦截器
*
* @author noear
* @since 3.8.1
*/
public interface ToolInterceptor {
/**
* 是否启用
*/
default boolean isEnabled() {
return true;
}
/**
* 设置是否启用
*/
default void setEnabled(Boolean enabled) {
}
/**
* 拦截工具调用
*
* @param req 请求
* @param chain 拦截链
*/
default ToolResult interceptTool(ToolRequest req, ToolChain chain) throws Throwable {
return chain.doIntercept(req);
}
}