6.8 KiB
02 Agent 接入与收敛:让推理可以工作,也可以停止
这一篇只回答一个问题:怎样保留 Agent 的自主推理,同时阻止它重复查询或无限空转?
1. 先看一个具体问题
Diagnosis Agent 第一次查询 10:00 到 10:10 的支付日志,没有发现异常;第二次换了关键词,仍然没有新信息;第三次又提交了与第一次等价的查询。
仅设置“最多调用 10 次 Tool”只能限制最坏损失,却回答不了:
- 上一次结果有没有推进诊断?
- 这次查询是否已经做过?
- 连续多少次没有新信息后应该停止?
- Agent 已经收到停止要求,为什么还能继续调用 Tool?
- 停止后,怎样安全地向用户说明已经检查过什么?
这就是 Agent 接入层和 Progress 组件共同解决的问题。
2. 一轮 ReAct 怎样经过 Harness
sequenceDiagram
participant A as Diagnosis Agent
participant MI as Model Interceptor
participant TI as Tool Interceptor
participant P as Progress Tracker
participant T as Tool Boundary
A->>MI: 发起一轮模型调用
MI->>MI: 检查 Run、预留预算、记录 Token
A->>TI: 请求调用 Tool
TI->>P: 评价上一轮是否有信息增益
P-->>TI: 可继续 / 重复 / 已饱和 / 协议错误
alt 允许继续
TI->>T: 执行业务 Tool
T-->>TI: Control View + Model Observation
TI->>P: 登记已完成调用和 scope
TI-->>A: 只返回 Model Observation
else 必须停止
TI-->>A: STOP_REQUIRED
end
第一次阅读可以把它们理解为:
- Agent 接入层是检查站:把框架原生 ReAct loop 接入预算、Tool 和审计边界。
- Progress Tracker 是收敛记录器:判断是否重复、是否连续无增益、是否必须停止。
3. 为什么复用框架 ReAct,而不是自己重写循环
Diagnosis Agent 需要模型原生 Tool Calling 和多轮 ReAct。项目没有再写一套 while 循环,而是通过 Factory、Model Interceptor、Tool Interceptor 和 Audit Hook 接入框架。
这样做的决策依据是:
- ReAct 的业务推理由框架和模型负责;
- 预算、Tool 授权、事实保存和停止协议由 Harness 负责;
- 两者通过明确边界连接,不互相复制实现。
如果 Harness 自己维护另一套 ReAct 状态机,就会同时出现“框架认为的下一步”和“Harness 认为的下一步”,调试时很难确定谁才是真理源。
主要代码入口:DiagnosisAgentFactory、DiagnosisAgentUseCase、HarnessModelInterceptor、HarnessToolInterceptor、HarnessEvidenceTools。
4. Model Interceptor:每一轮模型调用都必须记账
一个 Diagnosis Agent 执行不等于一次模型调用。ReAct 可能经历多轮思考和 Tool 返回,因此每一轮都要:
- 检查 Run 是否仍然 active;
- 在调用前预留模型预算;
- 调用后记录 Provider 返回的 Token usage;
- 再次检查取消或迟到结果。
Interceptor 不负责重试模型,也不判断输出是否支持根因。它只确保框架内部的每轮调用无法绕过 Harness。
5. Tool Interceptor:先检查进展,再允许查询
模型提交的 Tool Call 不只是业务参数,还携带对上一轮结果的评价。Tool Interceptor 会依次检查:
- previous observation 是否完整、顺序是否正确;
- 上一轮是
GAINED还是NO_GAIN; - 当前 Tool scope 是否已经完成过;
- 收集状态是否已经
SATURATED; - 通过检查后,才把业务请求交给 ToolBoundary。
它不执行后端查询,也不再次扣 Tool 预算。实际执行属于 ToolBoundary;收敛状态属于 ProgressTracker。Interceptor 只是两者与 ReAct 框架之间的接合点。
6. 为什么“有结果”不等于“有信息增益”
Tool 可以客观判断是否返回候选内容,却不知道这些内容是否推进了当前假设。例如同一条超时日志再次出现:
InvocationStatus = READY
EvidenceStatus = EVIDENCE_FOUND
InformationGain = NO_GAIN
前三个状态回答不同问题:查询是否完成、是否有候选内容、内容是否推进当前诊断。把它们合成一个 SUCCESS 会让系统无法正常收敛。
当前由 Agent 在下一次 Tool Call 中评价上一轮的信息增益。这样评价发生在它真正做出下一步行动时,Harness 也能机械检查调用顺序,而不需要再增加一个 Progress Judge 模型。
7. Progress Tracker 保存什么
Tracker 只保存控制所需的最小状态:
- 已完成的
tool_call_id和规范化 scope; - 哪个结果仍等待 Agent 评价;
- 连续
NO_GAIN次数; - 收集状态和停止原因;
- 停止指令是否已经交付。
它不保存 raw response,也不判断日志是否证明支付线程池耗尽。业务价值判断仍由 Diagnosis Agent 负责,事实内容仍由 canonical store 负责。
主要代码入口:DiagnosisProgressTracker、ToolScopeNormalizer、DiagnosisProgressProjector。
8. 为什么停止不直接等于失败
连续无增益后停止,是一次正常的受控收敛,不是技术异常。Harness 会从已经完成的 canonical Tool 记录中投影 ProgressSnapshot,只保留可验真的检查范围、观察事实和限制,再形成安全 Fallback。
flowchart LR
N["连续 NO_GAIN 或重复查询"] --> S["CollectionState = SATURATED"]
S --> X["拒绝新的证据 Tool"]
X --> P["生成安全 ProgressSnapshot"]
P --> F["发布证据不足的 Fallback"]
因此,“没有找到足够证据”可以正常结束;只有违反进展协议、预算耗尽且没有安全进展等情况,才可能升级为失败。
9. 为什么没有采用其他方案
| 方案 | 没有采用的原因 |
|---|---|
| 只设 Tool 次数上限 | 只能止损,不能识别查询已经没有价值 |
| Harness 根据结果条数判断增益 | 条数是客观统计,不代表是否推进业务假设 |
| 增加 Progress Judge Agent | 增加模型成本和新的非确定性判断点 |
| 用自然语言相似度判断重复 | 首版难以稳定解释误判,当前只做 typed 参数级 scope 规范化 |
| 把剩余预算告诉模型 | 容易让模型围绕阈值博弈,硬限制应由 Harness 保持 |
10. 先记住这些
- 框架负责 ReAct loop,Harness 通过 Interceptor 接入控制能力。
- 预算回答“还能不能查”,信息增益回答“继续查有没有价值”。
- Tool 是否有结果与结果是否推进诊断是两件事。
- ProgressTracker 只保存控制状态,不保存完整证据。
- 信息饱和可以正常发布 Fallback,不等于 Run 执行失败。
下一篇:03 Tool 事实边界。
需要深入停止协议时,阅读信息增益停止专题。