Files
SuperBizAgent-java/mvp/engineering/harness/components/02-Agent接入与收敛-让推理可以工作也可以停止.md
T

149 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 02 Agent 接入与收敛:让推理可以工作,也可以停止
这一篇只回答一个问题:**怎样保留 Agent 的自主推理,同时阻止它重复查询或无限空转?**
## 1. 先看一个具体问题
Diagnosis Agent 第一次查询 10:00 到 10:10 的支付日志,没有发现异常;第二次换了关键词,仍然没有新信息;第三次又提交了与第一次等价的查询。
仅设置“最多调用 10 次 Tool”只能限制最坏损失,却回答不了:
- 上一次结果有没有推进诊断?
- 这次查询是否已经做过?
- 连续多少次没有新信息后应该停止?
- Agent 已经收到停止要求,为什么还能继续调用 Tool?
- 停止后,怎样安全地向用户说明已经检查过什么?
这就是 Agent 接入层和 Progress 组件共同解决的问题。
## 2. 一轮 ReAct 怎样经过 Harness
```mermaid
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 返回,因此每一轮都要:
1. 检查 Run 是否仍然 active;
2. 在调用前预留模型预算;
3. 调用后记录 Provider 返回的 Token usage;
4. 再次检查取消或迟到结果。
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 可以客观判断是否返回候选内容,却不知道这些内容是否推进了当前假设。例如同一条超时日志再次出现:
```text
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。
```mermaid
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. 先记住这些
1. 框架负责 ReAct loop,Harness 通过 Interceptor 接入控制能力。
2. 预算回答“还能不能查”,信息增益回答“继续查有没有价值”。
3. Tool 是否有结果与结果是否推进诊断是两件事。
4. ProgressTracker 只保存控制状态,不保存完整证据。
5. 信息饱和可以正常发布 Fallback,不等于 Run 执行失败。
下一篇:[03 Tool 事实边界](03-Tool事实边界-让模型看到必要信息而系统保留真相.md)。
需要深入停止协议时,阅读[信息增益停止专题](../Harness信息增益停止-让无证据诊断正常收敛.md)。