team - TeamInterceptor 拦截器
在多智能体协作(Multi-Agent System)中,流程的透明度与可控性至关重要。TeamInterceptor 提供了对 TeamAgent 全生命周期的观察与干预能力。它不仅是一个日志记录点,也是实现合规审计、动态权限控制、成本熔断及内容安全的扩展点。
1、内置拦截器
| 拦截器 | 名称 | 描述 |
|---|---|---|
| LoopingTeamInterceptor | 协作死循环拦截器 | 通过回溯 TeamTrace 识别并阻断成员间的无效重复。支持「单点停滞」(同一 Agent 连续产出高度相似内容)与「序列嵌套」(A-B-A-B 型踢皮球)两类检测,命中后在 shouldSupervisorContinue 返回 false 熔断。 |
LoopingTeamInterceptor 的判定参数(最小检测长度、相似度阈值、回溯窗口、允许重复次数)目前为类内私有字段,使用默认值即可:相似度阈值 0.95、回溯窗口 10 步、不允许重复。它基于归一化编辑距离(Levenshtein)计算相似度,且只审计 Agent 产出记录,不统计系统开销步骤。
2、注册与启停
拦截器可在构建期通过 Builder 注册,支持指定顺序(index 越小越先执行):
TeamAgent agent = TeamAgent.of(chatModel)
.defaultInterceptorAdd(new LoopingTeamInterceptor())
.defaultInterceptorAdd(new CostLimitInterceptor(), 100)
.build();
也可在调用期通过 options 动态追加:
agent.prompt("...").options(o -> o.interceptorAdd(new MyAuditInterceptor())).call();
自定义拦截器建议继承 AbsTeamInterceptor(4.0.0+),它实现了 isEnabled() / setEnabled(),可在运行期动态开关:
public class CostLimitInterceptor extends AbsTeamInterceptor {
@Override
public boolean shouldSupervisorContinue(TeamTrace trace) {
//协作回合超过 10 轮则熔断
return trace.getTurnCount() < 10;
}
}
若直接实现 TeamInterceptor 接口,isEnabled() 默认返回 true,setEnabled() 为空实现(即无法关闭)。内置的 LoopingTeamInterceptor 即属这种情况。
关于 enabled 的生效范围:Team 生命周期各时机点(onTeamStart、onTeamEnd、shouldSupervisorContinue、onModelStart、onModelEnd、onSupervisorDecision、shouldAgentContinue、onAgentEnd)在调用前都会检查
isEnabled()。但 FlowInterceptor 的节点钩子是在 Flow 启动前一次性快照注册的,运行中途改 enabled 不会影响本轮的onNodeStart/onNodeEnd。
3、核心治理维度
TeamInterceptor 通过三个互补的切面维度构建治理体系。下表按一次协作的真实执行顺序排列:
| 层级 | 拦截方法 | 触发时机 | 典型应用场景 |
|---|---|---|---|
| 团队级 | onTeamStart | 提示词就绪、Talent 部署后,Flow 引擎启动前 | 初始化外部 TraceId、分配全局上下文、记录审计起点 |
| 流程级 | onNodeStart / onNodeEnd | 协作图中每个节点的起止(继承自 FlowInterceptor) | 节点粒度的耗时统计、图执行链路观测 |
| 决策级 | shouldSupervisorContinue | 主管节点入口,早于 maxTurns 与协议判定 | 准入熔断:步数超限、Token 余额不足时返回 false |
| onModelStart | 主管的 ChatRequest 组装完成、发起 LLM 调用前 | 动态微调 Temperature / MaxTokens、注入约束 | |
| onModelEnd | LLM 返回后、决策文本解析前(已完成 Usage 累计) | 内容安全审计、原始 Token 统计 | |
| onSupervisorDecision | 决策文本解析完成、提交物理路由前 | 路径追踪,观察任务被派发给谁 | |
| 成员级 | shouldAgentContinue | 成员 Agent 运行前(已回填 lastAgentName) | 权限校验。返回 false 则跳过该成员 |
| onAgentEnd | 成员执行完毕、轨迹记录已写入后 | 统计单个 Agent 的耗时与资源消耗 | |
| 团队级 | onTeamEnd | 最终答案收敛、Session 快照更新后 | 将 TeamTrace 持久化到数据库或推送监控平台 |
几个容易踩的行为差异:
- onTeamEnd 不是强闭环。它位于成功返回路径上,协作过程抛异常时不会被调用。若需要「无论成败都执行」的清理,应放在协议的
onTeamFinished(它在 finally 块中,不受 onTeamEnd 异常影响)。 - shouldSupervisorContinue 早于 maxTurns 检查。拦截器是主管节点的第一道关卡,可以在框架的回合数熔断之前先行介入。返回 false 时会写入一条
[Skipped] Intercepted by ...记录,并在当前路由指向主管时转向 END。 - shouldAgentContinue 返回 false 只跳过该成员。框架写入一条
[Skipped] Cancelled by ...记录后结束该节点,协作流程继续按图流转(通常回到主管重新决策),并非中止整个团队。 - onModelStart / onModelEnd / onSupervisorDecision 之后都有挂起检查。若拦截器在这些时机点里把会话置为 Pending(如触发人工介入),后续步骤会立即中断,不会提交路由。
- onAgentEnd 在轨迹写入之后触发,所以此时
trace.getLastAgentContent()、getLastAgentDuration()已包含该成员本次的产出。协议的同名钩子先于拦截器执行。
由于 TeamInterceptor 具备多重身份,还可以覆盖以下底层方法实现更精细的控制:
| 继承自 | 拦截方法 | 作用描述 |
|---|---|---|
| FlowInterceptor | doFlowIntercept | 包裹整个协作图的执行,可做全局 try/catch 或上下文透传。 |
| ChatInterceptor | onPrepare | 在构建 ChatModel 请求之前触发,可动态调整 ChatOptions。 |
| ChatInterceptor | interceptCall / interceptStream | 作用于最底层的 ChatModel 同步 / 流式调用。 |
| ToolInterceptor | interceptTool | 拦截工具执行链,可改写工具返回值。 |
| ToolInterceptor | isEnabled / setEnabled | 拦截器启停开关。 |
4、TeamTrace 常用观测入口
拦截器的所有判断都围绕 TeamTrace 展开,常用方法:
| 方法 | 说明 |
|---|---|
| getTurnCount() | 当前协作回合数(框架 maxTurns 熔断即基于此) |
| getRecordCount() | 轨迹记录条数(含系统记录) |
| getRecords() | 全部执行足迹,TeamRecord.isAgent() 可筛出成员产出 |
| getMetrics() | 指标聚合,含 Token 用量与总耗时 |
| getLastAgentName() / getLastAgentContent() / getLastAgentDuration() | 最近一个成员的名称、产出与耗时 |
| getRoute() / getLastDecision() | 当前物理路由目标与最近一次决策文本 |
| getFinalAnswer() | 最终答案(收敛后可用) |
| getOptions().getMaxTurns() | 本次运行的最大回合上限 |
注意:
TeamTrace没有getStepCount()方法。计步请用getTurnCount()(回合)或getRecordCount()(记录条数)。
5、应用场景示例
场景 A:全局成本与回合熔断
public class CostLimitInterceptor extends AbsTeamInterceptor {
@Override
public boolean shouldSupervisorContinue(TeamTrace trace) {
//回合超过 10 轮,或 Token 消耗超预算,则熔断
if (trace.getTurnCount() >= 10) {
return false;
}
return trace.getMetrics().getTotalTokens() < 100_000;
}
}
场景 B:敏感成员的权限准入
public class PermissionInterceptor extends AbsTeamInterceptor {
@Override
public boolean shouldAgentContinue(TeamTrace trace, Agent agent) {
if ("RefundAgent".equals(agent.name())) {
//无审批权限时跳过该成员,协作流程会回到主管重新决策
return currentUserHasRole("refund:approve");
}
return true;
}
}
场景 C:协作轨迹持久化
public class TraceArchiveInterceptor extends AbsTeamInterceptor {
@Override
public void onTeamEnd(TeamTrace trace) {
//仅成功路径触发;需要覆盖异常场景请用协议的 onTeamFinished
archiveService.save(trace.getRunId(), trace.getRecords(), trace.getMetrics());
}
}
场景 D:决策路径观测
public class RoutingLogInterceptor extends AbsTeamInterceptor {
@Override
public void onSupervisorDecision(TeamTrace trace, String decision) {
LOG.info("turn={}, decision={}", trace.getTurnCount(), decision);
}
}
6、TeamInterceptor 接口参考
TeamInterceptor 同时继承了 AgentInterceptor、FlowInterceptor 和 ChatInterceptor(而 ChatInterceptor 又继承 ToolInterceptor),所以它除了 Team 协作生命周期,还可以拦截流程节点、聊天模型与工具的执行。
TeamInterceptor
package org.noear.solon.ai.agent.team;
import org.noear.solon.ai.agent.Agent;
import org.noear.solon.ai.agent.AgentInterceptor;
import org.noear.solon.ai.chat.ChatRequestDesc;
import org.noear.solon.ai.chat.ChatResponse;
import org.noear.solon.ai.chat.interceptor.ChatInterceptor;
import org.noear.solon.flow.intercept.FlowInterceptor;
import org.noear.solon.lang.Preview;
/**
* 团队协作拦截器 (Team Interceptor)
* <p>核心职责:提供对 TeamAgent 协作全生命周期的观察与干预能力。支持团队、决策、成员三个维度的切面注入。</p>
*
* @author noear
* @since 3.8.1
*/
@Preview("3.8.1")
public interface TeamInterceptor extends AgentInterceptor, FlowInterceptor, ChatInterceptor {
// --- [维度 1:团队级 (Team Level)] ---
/**
* 团队协作开始
*/
default void onTeamStart(TeamTrace trace) {}
/**
* 团队协作结束
*/
default void onTeamEnd(TeamTrace trace) {}
// --- [维度 2:决策级 (Supervisor Level)] ---
/**
* 决策准入校验(主管发起思考前)
*
* @return true: 继续执行; false: 熔断并中止协作
*/
default boolean shouldSupervisorContinue(TeamTrace trace) {
return true;
}
/**
* 模型请求前置(LLM 调用前)
* <p>常用于动态调整 Request 参数(如 Temperature, MaxTokens 等)。</p>
*/
default void onModelStart(TeamTrace trace, ChatRequestDesc req) {}
/**
* 模型响应后置(LLM 返回后,解析前)
* <p>常用于内容安全审计或原始 Token 统计。</p>
*/
default void onModelEnd(TeamTrace trace, ChatResponse resp) {}
/**
* 决策结果输出(指令解析后)
*
* @param decision 经解析确定的目标 Agent 名称或终结指令
*/
default void onSupervisorDecision(TeamTrace trace, String decision) {}
// --- [维度 3:成员级 (Agent Level)] ---
/**
* 成员执行准入校验(Agent 运行前)
*
* @param agent 即将运行的智能体
* @return true: 允许运行; false: 跳过并回滚至决策层
*/
default boolean shouldAgentContinue(TeamTrace trace, Agent agent) {
return true;
}
/**
* 成员执行结束
*/
default void onAgentEnd(TeamTrace trace, Agent agent) {}
}
AbsTeamInterceptor
package org.noear.solon.ai.agent.team;
/**
*
* @author noear
* @since 4.0.0
*/
public abstract class AbsTeamInterceptor implements TeamInterceptor {
private volatile boolean enabled = true;
@Override
public void setEnabled(Boolean enabled) {
if (enabled != null) {
this.enabled = enabled;
}
}
@Override
public boolean isEnabled() {
return enabled;
}
}