Solon v4.0.5

team - TeamInterceptor 拦截器

</> markdown
2026年8月3日 下午4:22:13

在多智能体协作(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() 默认返回 truesetEnabled() 为空实现(即无法关闭)。内置的 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、注入约束
onModelEndLLM 返回后、决策文本解析前(已完成 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 具备多重身份,还可以覆盖以下底层方法实现更精细的控制:

继承自拦截方法作用描述
FlowInterceptordoFlowIntercept包裹整个协作图的执行,可做全局 try/catch 或上下文透传。
ChatInterceptoronPrepare在构建 ChatModel 请求之前触发,可动态调整 ChatOptions。
ChatInterceptorinterceptCall / interceptStream作用于最底层的 ChatModel 同步 / 流式调用。
ToolInterceptorinterceptTool拦截工具执行链,可改写工具返回值。
ToolInterceptorisEnabled / 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 同时继承了 AgentInterceptorFlowInterceptorChatInterceptor(而 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;
    }
}