feat(harness): add information gain stop and audit
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# MVP 架构文档
|
||||
|
||||
**更新日期**:2026-07-23
|
||||
**更新日期**:2026-07-26
|
||||
**状态**:当前单 Diagnosis Agent + Harness 架构
|
||||
|
||||
当前文档入口:
|
||||
@@ -11,6 +11,7 @@
|
||||
| [agent-orchestration.md](agent-orchestration.md) | 单 Diagnosis ReAct Agent 的职责、执行方式和 reasoning 采集边界 |
|
||||
| [harness-quality-gates.md](harness-quality-gates.md) | Run、Tool、Evidence、Semantic、Release 与 Trace Recorder 门禁 |
|
||||
| [session-trace-lifecycle.md](session-trace-lifecycle.md) | sessionId/runId、SSE、统一 Timeline 和 reasoning audit 生命周期 |
|
||||
| [diagnosis-information-gain-stop-architecture.md](diagnosis-information-gain-stop-architecture.md) | 已实施的信息增益评价、Harness 饱和检测、Draft 合同失败降级与证据不足停止设计 |
|
||||
|
||||
2026-07-22 前的多角色编排、双入口和旧证据链文档已移动到 `archive/2026-07-22-legacy/`,仅用于历史决策追溯,不代表当前运行时。
|
||||
|
||||
|
||||
@@ -0,0 +1,608 @@
|
||||
# Diagnosis Agent 信息增益与停止控制架构
|
||||
|
||||
**更新日期**:2026-07-27
|
||||
**状态**:已实施,待归档
|
||||
**关联 Issue**:[ISS-016](../issues/active/ISS-016-diagnosis-information-gain-stop-contract.md)
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 Diagnosis ReAct Agent 在知识库没有直接答案、日志持续为空或查询条件不足时,可能继续改写查询并反复调用 Tool,直到资源预算耗尽。此时 Run 被发布为技术失败,用户只能看到通用错误,而不是“已完成有限排查,但证据不足”。
|
||||
|
||||
本设计解决两个不同问题:
|
||||
|
||||
1. 查询何时已经不再产生有效信息,必须停止继续调用 Tool。
|
||||
2. 停止后如何避免模型在证据不足时生成无依据结论。
|
||||
|
||||
## 2. 核心原则
|
||||
|
||||
```text
|
||||
Tool:返回客观数据
|
||||
Harness:判断查询过程是否还允许继续
|
||||
Model:判断数据是否推进了当前诊断,并通过实际 Tool Call 或 Draft 表达行为
|
||||
SemanticGuard:判断最终结论是否有证据支撑
|
||||
Release:统一把 Draft 或 Harness 停止投影为安全的 SUCCESS / FALLBACK
|
||||
```
|
||||
|
||||
停止权属于 Harness。模型可以建议继续,但不能绕过 Harness 的饱和状态和硬资源边界。
|
||||
|
||||
“没有找到根因”是合法业务结果,不等于执行失败。模型被允许输出 `conclusion=null` 的 `DiagnosisDraft`;最终仍只使用 `SUCCESS / FALLBACK / FAILED / CANCELLED`,不增加第二套诊断终态。
|
||||
|
||||
## 3. 总体架构
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
U["用户提出诊断问题"] --> M["Diagnosis Agent"]
|
||||
|
||||
P["ReAct Prompt<br/>允许合法放弃"] -.-> M
|
||||
C["会话上下文<br/>有界历史和已检查范围"] -.-> M
|
||||
|
||||
M -->|"Tool Call Envelope"| B["Harness 调用门禁"]
|
||||
B --> A["应用 previous_observation<br/>更新连续 NO_GAIN"]
|
||||
A --> GATE{"是否允许继续"}
|
||||
|
||||
GATE -->|"INFORMATION_SATURATED"| STOP["拒绝调用<br/>注入 STOP_REQUIRED"]
|
||||
GATE -->|"BUDGET_LIMIT_REACHED"| BSTOP["停止调用<br/>记录资源保护原因"]
|
||||
GATE -->|"允许调用"| T["剥离控制字段<br/>执行业务 Tool"]
|
||||
|
||||
T -->|"执行失败"| ERR["Tool 状态:FAILED"]
|
||||
ERR --> EP["技术故障策略<br/>有限重试或 FAILED 结束"]
|
||||
|
||||
T -->|"执行成功"| CR["Canonical Tool Result<br/>完整内部结果"]
|
||||
CR --> CV["Harness Control View"]
|
||||
CR --> AP["Agent Observation Projector"]
|
||||
CR --> STORE["Canonical Store"]
|
||||
AP --> MO["Model Observation<br/>白名单有界结果"]
|
||||
MO --> SM["模型阅读返回内容"]
|
||||
|
||||
CV --> O{"Harness 客观判断"}
|
||||
O -->|"evidence_status=NO_EVIDENCE"| NG1["Harness 标记 NO_GAIN"]
|
||||
O -->|"重复 Tool + 相同 scope"| NG1
|
||||
|
||||
O -->|"其他成功非空结果<br/>包括 REFERENCE"| SM
|
||||
SM --> SD{"语义信息增益"}
|
||||
SD -->|"推进或排除诊断假设"| G["GAINED"]
|
||||
SD -->|"没有可验证的新事实"| NG2["NO_GAIN"]
|
||||
|
||||
G --> M
|
||||
NG1 --> M
|
||||
NG2 --> M
|
||||
|
||||
M -->|"DiagnosisDraft"| SNAP["结束时投影 ProgressSnapshot"]
|
||||
M -->|"空或非法 Draft"| DRAFTERR["丢弃非法内容<br/>投影当前进展"]
|
||||
DRAFTERR -->|"有已验真 observed facts"| SNAP
|
||||
DRAFTERR -->|"无安全进展"| EP
|
||||
STOP -->|"给模型一次合法完成机会"| M
|
||||
STOP -->|"仍无合法 Draft"| SNAP
|
||||
BSTOP --> SNAP
|
||||
STORE --> SNAP
|
||||
|
||||
SNAP --> RELEASE["DiagnosisReleaseUseCase"]
|
||||
RELEASE -->|"conclusion 非空"| GUARD["EvidenceGuard / Repair / SemanticGuard"]
|
||||
RELEASE -->|"conclusion 为空或无 Draft"| SAFE["确定性 SafeFallback<br/>不修复结论"]
|
||||
GUARD -->|"证据支持"| SUCCESS["SUCCESS"]
|
||||
GUARD -->|"无证据推断"| SAFE
|
||||
SAFE --> FALLBACK["FALLBACK"]
|
||||
EP --> FAILED["FAILED"]
|
||||
|
||||
SUCCESS --> APP["ChatApplicationUseCase<br/>持久化"]
|
||||
FALLBACK --> APP
|
||||
FAILED --> APP
|
||||
APP --> SSE["ChatSseSession<br/>发送 content/failure + done"]
|
||||
```
|
||||
|
||||
## 4. 状态模型
|
||||
|
||||
状态按职责分开,不使用一个枚举表达整个生命周期。
|
||||
|
||||
| 维度 | 状态 | 负责人 | 含义 |
|
||||
|---|---|---|---|
|
||||
| Tool 执行 | `SUCCEEDED / FAILED` | ToolBoundary | Tool 是否完成技术执行 |
|
||||
| 信息增益 | `GAINED / NO_GAIN` | Harness 或模型 | 本次结果是否推进当前诊断 |
|
||||
| 收集生命周期 | `COLLECTING / SATURATED` | Harness | 是否允许继续调用 Tool |
|
||||
| 内部停止原因 | `INFORMATION_SATURATED / BUDGET_LIMIT_REACHED` | Harness | 为什么停止继续调用 Tool |
|
||||
| Fallback 原因 | `SafeFallback.type` | DiagnosisReleaseUseCase | 为什么没有发布诊断结论 |
|
||||
| 最终发布 | `SUCCESS / FALLBACK / FAILED / CANCELLED` | Release | 对外发布结果 |
|
||||
|
||||
错误码、停止原因、Fallback 原因和 RAG 相关度是附带字段,不提升为全局生命周期状态。`CONFIRMED / INSUFFICIENT_EVIDENCE / NEED_MORE_INFO` 不作为第二套诊断状态;其中后两种语义由 `SafeFallback.type` 表达。
|
||||
|
||||
当前实现中的兼容映射是:内部 `PROJECTING` 不对外暴露,`READY` 对应目标语义 `SUCCEEDED`,`ERROR` 对应目标语义 `FAILED`。
|
||||
|
||||
不定义独立的模型行动状态。模型发起 Tool Call 表示继续收集,输出正常 `DiagnosisDraft` 表示完成诊断,输出证据不足 Draft 表示主动停止;Harness 直接从实际输出推导行为。
|
||||
|
||||
## 5. Tool 结果与上下文边界
|
||||
|
||||
Tool 不判断业务结论,只提供事实和可机械计算的元信息。
|
||||
|
||||
```text
|
||||
tool_call_id 本次调用的唯一标识
|
||||
execution_status SUCCEEDED | FAILED
|
||||
returned_count 本次实际返回的记录数量
|
||||
scope 本次查询实际覆盖的结构化范围
|
||||
metadata Tool 特有的有限元信息
|
||||
result 提供给模型的有界结果
|
||||
```
|
||||
|
||||
字段说明:
|
||||
|
||||
- `returned_count` 只表达“返回了多少条”,不表示这些内容有诊断价值。
|
||||
- `scope` 用于识别相同 Tool、相同查询范围的重复调用,例如企业、时间窗、服务和过滤条件。
|
||||
- `metadata` 保存 Tool 特有信息,例如 RAG 的 `relevance_level`、是否截断和分页信息。
|
||||
- 第一版不引入 `new_count`。它要求为不同 Tool 建立稳定的结果指纹,而且“新数据”也不等于“有效数据”。
|
||||
|
||||
### 5.1 客观信号来源
|
||||
|
||||
这些信号不是模型推导的:
|
||||
|
||||
| 信号 | 来源 | 含义 |
|
||||
|---|---|---|
|
||||
| `PROJECTING / READY / ERROR` | ToolBoundary | 当前实现的调用生命周期;目标外部语义映射为 `SUCCEEDED / FAILED` |
|
||||
| `EVIDENCE_FOUND / NO_EVIDENCE / ERROR` | Tool Result Projector / ToolBoundary | 是否存在候选结果或发生技术错误 |
|
||||
| `PRECISE / HIGHLY_RELEVANT / REFERENCE / null` | RAG 后处理规则 | RAG 候选内容的相关度 |
|
||||
| 重复 `tool + scope` | Harness | 本次查询范围是否与历史调用等价 |
|
||||
|
||||
`NO_EVIDENCE` 由各 Projector 根据结果集合是否为空确定。`REFERENCE` 由 RAG 根据归一化相似度及查询提示命中情况计算。两者都不是 LLM 生成的状态,但职责不同:`NO_EVIDENCE` 可由 Harness 机械判定为 `NO_GAIN`;`REFERENCE` 只表示候选内容相关度一般,仍由模型判断是否推进当前诊断。
|
||||
|
||||
改造前 `RagResultProjector` 只根据 `evidence` 是否为空生成 `EVIDENCE_FOUND / NO_EVIDENCE`,没有保留上游 `relevanceLevel`,因此会出现:
|
||||
|
||||
```text
|
||||
relevanceLevel = REFERENCE
|
||||
evidenceBlocks 非空
|
||||
-> RagResultProjector
|
||||
-> evidence_status = EVIDENCE_FOUND
|
||||
```
|
||||
|
||||
这不是两个判断冲突,而是 Projector 丢失了相关度维度。当前实现已兼容读取上游 `relevanceLevel` 或 `relevance_level`,并统一保留为 `relevance_level`。
|
||||
|
||||
### 5.2 Tool 双视图架构
|
||||
|
||||
内部控制信息和模型观察不能继续共用一个无差别 JSON。标准化结果产生两个明确视图:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Tool 原始返回"] --> B["结果标准化"]
|
||||
B --> C["Canonical Tool Result<br/>完整内部结果"]
|
||||
|
||||
C --> D["Harness Control View"]
|
||||
C --> E["Agent Observation Projector"]
|
||||
|
||||
D --> F["Harness<br/>重复检测、低收益计数、预算、饱和状态"]
|
||||
D --> G["Canonical Store"]
|
||||
|
||||
E --> H["Model Observation<br/>最小必要字段"]
|
||||
H --> I["ToolCallResponse.content"]
|
||||
I --> J["模型上下文"]
|
||||
```
|
||||
|
||||
改造前会把完整 `agent_result` 作为 `ToolCallResponse.content` 交给模型。当前实现已改为白名单投影,Harness 控制字段默认不进入模型上下文。
|
||||
|
||||
字段边界:
|
||||
|
||||
| 字段 | Harness | 模型 | 说明 |
|
||||
|---|---:|---:|---|
|
||||
| `tool_call_id` | 是 | 是 | 引用与归属校验 |
|
||||
| 实际查询 `scope` | 是 | 是 | 重复检测与有界负向观察 |
|
||||
| 有界 `evidence` | 是 | 是 | 模型语义判断所需内容 |
|
||||
| `evidence_status` | 是 | 是 | 候选结果是否为空或失败 |
|
||||
| `relevance_level` | 是 | 是 | 只暴露粗粒度标签,不暴露原始分数 |
|
||||
| `truncated` | 是 | 是 | 提醒模型结果并不完整 |
|
||||
| `returned_count` | 是 | 否 | Harness 客观统计 |
|
||||
| 原始相似度和检索轨迹 | 是 | 否 | 内部质量与审计信息 |
|
||||
| 规范化 scope、重复指纹 | 是 | 否 | Harness 控制信息 |
|
||||
| 低收益计数、阈值、预算 | 是 | 否 | Run 内部状态 |
|
||||
| 原始 Tool Response | 是 | 否 | 不进入模型上下文 |
|
||||
|
||||
只有当 Harness 必须改变模型行为时,才注入有界控制指令,例如:
|
||||
|
||||
```json
|
||||
{
|
||||
"stop_required": true,
|
||||
"reason": "INFORMATION_SATURATED",
|
||||
"checked_scopes": ["本 Run 已检查范围的有界摘要"]
|
||||
}
|
||||
```
|
||||
|
||||
计数器、阈值和剩余预算不随控制指令进入模型上下文。
|
||||
|
||||
### 5.3 Tool 调用与投影流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Model as Diagnosis Agent
|
||||
participant Interceptor as Harness Tool Interceptor
|
||||
participant Gate as Harness Gate
|
||||
participant Adapter as Tool Adapter
|
||||
participant Backend as Tool Backend
|
||||
participant Projector as Result Normalizer / Projector
|
||||
participant Progress as Progress Tracker
|
||||
participant Store as Canonical Store
|
||||
|
||||
Model->>Interceptor: Tool Call Envelope
|
||||
Interceptor->>Progress: 校验并应用 previous_observation(如有)
|
||||
Progress-->>Interceptor: 更新后的收集状态
|
||||
Interceptor->>Gate: 校验 Run、授权、预算和饱和状态
|
||||
alt 已经 SATURATED
|
||||
Gate-->>Interceptor: STOP_REQUIRED
|
||||
Interceptor-->>Model: 有界停止指令
|
||||
else 达到硬预算
|
||||
Gate-->>Interceptor: BUDGET_LIMIT_REACHED
|
||||
else 允许调用
|
||||
Gate-->>Interceptor: ALLOW
|
||||
Interceptor->>Interceptor: 剥离 previous_observation
|
||||
Interceptor->>Adapter: input 中的 typed request
|
||||
Adapter->>Backend: 执行只读查询
|
||||
Backend-->>Adapter: raw response
|
||||
Adapter->>Projector: 标准化原始结果
|
||||
Projector-->>Progress: Harness Control View
|
||||
Projector-->>Store: Canonical Tool Result
|
||||
Projector-->>Interceptor: Model Observation
|
||||
Interceptor-->>Model: 有界 Tool Observation
|
||||
end
|
||||
```
|
||||
|
||||
重复查询由 Harness 根据 `tool_name + normalized_scope` 判断,不属于 Tool 返回状态。规范化只处理确定性的业务参数,例如时间格式、无序数组和非业务字段;第一版不判断自然语言改写是否语义等价。
|
||||
|
||||
### 5.4 结束时进展投影
|
||||
|
||||
每次 Tool 调用完成后只追加 Canonical Tool Result,不在每轮维护另一份用户可见摘要。Tool Loop 因 Agent 输出 Draft、信息饱和或预算限制结束时,一次性生成内部 `ProgressSnapshot`:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
T["每次 Tool 完成"] --> C["Canonical Tool Result"]
|
||||
C --> S["Canonical Store"]
|
||||
S -->|"Tool Loop 结束时只读投影"| P["ProgressSnapshot"]
|
||||
P --> R["DiagnosisReleaseUseCase"]
|
||||
R --> O["SafeFallback.observed_facts"]
|
||||
```
|
||||
|
||||
`ProgressSnapshot` 不是新的真理源,也不进入模型上下文。它只包含来源、实际 scope、客观结果摘要、截断标记和 Harness `stop_reason`;原始 Tool Response、Prompt、内部 thought、计数器和剩余预算不进入发布内容。
|
||||
|
||||
现有 `SafeFallback` 和前端已经支持 `observed_facts / verified_sources / limitations / next_steps`,因此不新增前端协议。`FallbackType` 增加 `INSUFFICIENT_EVIDENCE / MISSING_REQUIRED_CONTEXT`,分别表达有限排查后证据不足和缺少有效查询条件。
|
||||
|
||||
## 6. 信息增益契约
|
||||
|
||||
信息增益只保留两个值:
|
||||
|
||||
```text
|
||||
information_gain = GAINED | NO_GAIN
|
||||
```
|
||||
|
||||
### 6.1 GAINED
|
||||
|
||||
满足以下任一条件:
|
||||
|
||||
- 返回了与当前问题直接相关、可验证的新事实。
|
||||
- 新事实确认了一个当前诊断假设。
|
||||
- 新事实排除了一个当前诊断假设。
|
||||
- 新事实实质性缩小了故障范围。
|
||||
|
||||
“排除假设”也是信息增益。信息增益不要求得到最终根因。
|
||||
|
||||
### 6.2 NO_GAIN
|
||||
|
||||
满足以下任一条件:
|
||||
|
||||
- 内容为空、重复或只有通用参考资料。
|
||||
- 数据虽然非空,但与当前问题没有直接关系。
|
||||
- 没有新增可验证事实,也没有改变任何诊断假设。
|
||||
- 只是建议继续查询另一个位置,没有提供新的诊断事实。
|
||||
- 模型无法判断结果是否推进诊断。
|
||||
|
||||
不增加 `UNKNOWN`。无法判断时归入 `NO_GAIN`,避免它成为无限继续查询的出口。
|
||||
|
||||
### 6.3 谁负责赋值
|
||||
|
||||
```text
|
||||
FAILED
|
||||
-> 不产生 information_gain,进入技术故障流程
|
||||
|
||||
SUCCEEDED + evidence_status=NO_EVIDENCE
|
||||
或重复的 tool_name + normalized_scope
|
||||
-> Harness 直接赋 NO_GAIN
|
||||
|
||||
其他 SUCCEEDED 非空结果,包括 relevance_level=REFERENCE
|
||||
-> 模型必须赋 GAINED 或 NO_GAIN
|
||||
```
|
||||
|
||||
模型只判断“本次结果是否推进当前诊断”,不评价 Tool 产品质量,也不输出 0 到 100 的主观分数。
|
||||
|
||||
## 7. 模型步骤契约
|
||||
|
||||
模型收到未被 Harness 客观标记为 `NO_GAIN` 的成功非空结果后,如果要继续调用 Tool,必须在下一次 Tool Call 中给出上一调用的信息增益。该设计不要求模型显式声明下一步行动。
|
||||
|
||||
```json
|
||||
{
|
||||
"previous_observation": {
|
||||
"tool_call_id": "call-123",
|
||||
"information_gain": "NO_GAIN"
|
||||
},
|
||||
"input": {
|
||||
"query": "下一次查询参数"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
1. `previous_observation` 只在上一轮存在待模型评价的 Tool Observation 时必填;首次调用以及上一轮已由 Harness 客观赋值时省略。
|
||||
2. `tool_call_id` 必须指向当前 Run 中最后一个尚未评价的成功 Tool 调用。
|
||||
3. Harness 必须先校验并应用 `information_gain`,再判断是否允许执行本次 Tool 调用。
|
||||
4. Harness 消费并剥离 `previous_observation`,业务 Tool 只接收 `input` 中的原有业务参数。
|
||||
5. 上一个待语义评价的结果未被评价时,Harness 不接受新的 Tool 调用。
|
||||
6. Harness 已经进入 `SATURATED` 时,新的 Tool Call 被拒绝,并向模型注入 `STOP_REQUIRED`。
|
||||
7. 模型主动判断没有合理查询方向时,应直接输出证据不足 Draft,不必等待 Harness 强制停止,也无需为放行下一次调用而回传最后一轮评价。
|
||||
|
||||
模型行为直接从实际输出推导:
|
||||
|
||||
```text
|
||||
Tool Call -> 继续收集
|
||||
正常 DiagnosisDraft -> 完成诊断
|
||||
证据不足 DiagnosisDraft -> 主动停止
|
||||
```
|
||||
|
||||
该 Envelope 是模型侧 Tool 调用协议。服务端为动态注册的 Tool Schema 统一增加 Envelope,Harness 在边界处消费控制字段,业务 Tool 的入参协议保持不变。这是一项有意的协议变更,影响模型 Tool Schema 生成、Tool Call 解析、Harness 拦截和相关测试,不影响 Tool Backend。
|
||||
|
||||
## 8. Harness 饱和规则
|
||||
|
||||
第一版使用连续低收益计数,不维护复杂进展账本。硬调用上限是独立资源保护,不作为信息饱和条件。
|
||||
|
||||
```text
|
||||
evidence_status=NO_EVIDENCE -> consecutiveLowYield + 1
|
||||
重复 Tool + 相同 scope -> consecutiveLowYield + 1
|
||||
成功非空结果(包括 REFERENCE)+ 模型 NO_GAIN
|
||||
-> consecutiveLowYield + 1
|
||||
其他成功非空结果 + 模型 GAINED -> consecutiveLowYield = 0
|
||||
FAILED -> 技术故障流程,不计入低收益
|
||||
|
||||
consecutiveLowYield >= stopAfterConsecutiveNoGain
|
||||
-> SATURATED
|
||||
-> stop_reason=INFORMATION_SATURATED
|
||||
达到单 Tool 或 Run 硬调用上限 -> stop_reason=BUDGET_LIMIT_REACHED
|
||||
```
|
||||
|
||||
配置项 `stop-after-consecutive-no-gain` 默认值为 `2`、必须大于等于 `1`,只在 Run 启动时读取并固定,不进入模型上下文。默认值需要通过固定 E2E 和评测集校准,不作为不可调整的业务真理。
|
||||
|
||||
伪代码:
|
||||
|
||||
```text
|
||||
onToolResult(result):
|
||||
if result.execution_status == FAILED:
|
||||
handleTechnicalFailure(result)
|
||||
return
|
||||
|
||||
if isEmpty(result) or isRepeatedScope(result):
|
||||
applyInformationGain(NO_GAIN)
|
||||
return
|
||||
|
||||
requireModelAssessment(result.tool_call_id)
|
||||
|
||||
applyInformationGain(gain):
|
||||
if gain == GAINED:
|
||||
consecutiveLowYield = 0
|
||||
else:
|
||||
consecutiveLowYield += 1
|
||||
|
||||
if consecutiveLowYield >= stopAfterConsecutiveNoGain:
|
||||
collectionState = SATURATED
|
||||
stopReason = INFORMATION_SATURATED
|
||||
|
||||
onHardLimitReached():
|
||||
stopReason = BUDGET_LIMIT_REACHED
|
||||
stopToolCollection()
|
||||
```
|
||||
|
||||
`SATURATED` 是当前 Run 的 Tool 收集终态。模型不能在同一 Run 中把它恢复为 `COLLECTING`。用户补充新范围或新事实后,应开启新的诊断 Run。
|
||||
|
||||
## 9. 控制与发布边界
|
||||
|
||||
### 9.1 Harness 层:物理停止
|
||||
|
||||
Harness 使用 `NO_EVIDENCE`、重复的 `tool_name + normalized_scope` 和连续 `NO_GAIN` 检测信息饱和。RAG `REFERENCE` 与其他成功非空结果一样,由 Diagnosis Agent 通过下一次 Tool Call Envelope 回传语义评价。硬调用上限只产生 `BUDGET_LIMIT_REACHED`,不伪装成信息饱和。
|
||||
|
||||
### 9.2 SemanticGuard 层:无证据推断兜底
|
||||
|
||||
只有 `DiagnosisDraft.conclusion` 非空时才进入完整 EvidenceGuard、EvidenceRepair 和 SemanticGuard 链路。SemanticGuard 在最终发布前检查:
|
||||
|
||||
- 正常诊断中的关键结论是否绑定已验证证据。
|
||||
- `REFERENCE` 和通用资料是否被错误当作当前故障事实。
|
||||
- scoped `NO_EVIDENCE` 是否被错误解释为“故障不存在”。
|
||||
- 证据不足时是否生成了确定性根因。
|
||||
|
||||
发现无证据推断时,不继续 Tool Loop,而是转换为有界的证据不足结果。
|
||||
|
||||
`conclusion=null` 是合法业务结果,不触发 EvidenceRepair,也不要求额外调用 SemanticGuard 来证明“没有结论”。已有 analysis 时只校验其 Tool 引用真实性;Release 从 `ProgressSnapshot` 和 `limitations` 生成安全 Fallback。零次 Tool 调用且 `limitations.missing_info` 非空时,直接生成 `MISSING_REQUIRED_CONTEXT` Fallback。
|
||||
|
||||
### 9.3 ReAct Prompt 层:合法放弃许可证
|
||||
|
||||
Prompt 必须明确:
|
||||
|
||||
- 不要求模型必须得出诊断结论或根因,但必须输出一个诚实、有界的完整 Draft。
|
||||
- 无法得到足够证据时,`conclusion=null` 是成功完成,不是失败。
|
||||
- Tool 调用次数可以为零。缺少企业、时间范围、服务或错误信息,且不存在预期能产生新诊断信息的明确查询时,直接在 `limitations.missing_info` 中列出缺口。
|
||||
- 不得为了表现“已经排查”而调用没有明确目标的 Tool。
|
||||
- Tool 返回内容事实正确、表述完整或结果非空,不代表它对当前诊断有信息增益。
|
||||
- 通用知识、背景说明、重复内容或不能改变当前判断的内容属于 `NO_GAIN`。
|
||||
- `NO_GAIN` 后不得通过改写相似关键词或重复相同 scope 继续尝试。
|
||||
- 如果仍存在范围明确、可能产生新诊断信息的不同查询,可以继续;否则应主动停止。
|
||||
- 不得为了表现“尽力”而重复或扩大无明确目标的查询。
|
||||
- 收到 `STOP_REQUIRED` 后不得继续调用 Tool。
|
||||
|
||||
Prompt 不包含 Tool 名称、Tool Schema 或需要展开 Schema 的文本占位符。Tool 只通过服务端的模型原生 Tool Calling 通道注册;通用 Envelope 由服务端包装动态 Tool Schema。
|
||||
|
||||
### 9.4 会话上下文层:历史边界认知
|
||||
|
||||
每条 Tool 结果只以白名单 `Model Observation` 进入模型上下文一次。Harness 不在后续步骤中重复注入完整 Tool 结果、原始响应或内部进展账本。
|
||||
|
||||
只有 Harness 必须改变模型行为时,才注入有界停止指令:
|
||||
|
||||
```text
|
||||
stop_required
|
||||
stop_reason
|
||||
已检查 Tool 和 scope 的有界摘要
|
||||
```
|
||||
|
||||
不向模型注入 `consecutiveLowYield`、阈值、剩余预算、重复指纹、原始相似度、完整 Tool 原始载荷或内部 thought。
|
||||
|
||||
### 9.5 Release 层:统一安全发布
|
||||
|
||||
`DiagnosisReleaseUseCase` 是诊断业务发布的唯一决策入口:
|
||||
|
||||
```text
|
||||
DiagnosisDraft.conclusion 非空
|
||||
-> EvidenceGuard
|
||||
-> 必要时 EvidenceRepair
|
||||
-> SemanticGuard
|
||||
-> SUCCESS 或 FALLBACK
|
||||
|
||||
DiagnosisDraft.conclusion 为空
|
||||
-> 不执行 EvidenceRepair
|
||||
-> 校验已有 Tool 引用(如有)
|
||||
-> ProgressSnapshot + limitations
|
||||
-> FALLBACK
|
||||
|
||||
Harness 已停止且没有 DiagnosisDraft
|
||||
-> stop_reason + ProgressSnapshot
|
||||
-> 确定性 FALLBACK
|
||||
|
||||
最终 Draft 为空或违反严格 JSON/Schema 合同
|
||||
-> 丢弃非法 Draft,不做宽松提取或模型修复
|
||||
-> ProgressSnapshot 有已验真 observed facts:INSUFFICIENT_EVIDENCE
|
||||
-> 无安全进展:FAILED
|
||||
```
|
||||
|
||||
Draft 合同失败不是新的 `stop_reason`。Trace 只记录固定失败类别、输出字节数和是否存在可发布进展,不记录模型原文、字段值或解析异常文本。
|
||||
|
||||
`ChatApplicationUseCase` 只负责编排、持久化,以及把无法形成安全业务内容的不可恢复故障映射为 `FAILED / CANCELLED`。预算 Fallback 不再由它单独构造。`ChatSseSession` 只发送 `content|failure + done`,不判断诊断语义。
|
||||
|
||||
## 10. 生命周期
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> COLLECTING
|
||||
|
||||
COLLECTING --> COLLECTING: GAINED / 低收益清零
|
||||
COLLECTING --> COLLECTING: NO_GAIN / 未达到阈值
|
||||
COLLECTING --> SATURATED: 连续 NO_GAIN 达到阈值
|
||||
COLLECTING --> BUDGET_STOP: 达到硬调用上限
|
||||
COLLECTING --> TECHNICAL_FAILURE: 不可恢复的 Tool 或基础设施故障
|
||||
COLLECTING --> DRAFT_READY: 模型输出 DiagnosisDraft
|
||||
COLLECTING --> DRAFT_INVALID: 最终 Draft 为空或违反合同
|
||||
COLLECTING --> CANCELLED: 用户取消
|
||||
|
||||
SATURATED --> RELEASE_INPUT: INFORMATION_SATURATED + ProgressSnapshot
|
||||
BUDGET_STOP --> RELEASE_INPUT: BUDGET_LIMIT_REACHED + ProgressSnapshot
|
||||
DRAFT_READY --> RELEASE_INPUT: Draft + ProgressSnapshot
|
||||
DRAFT_INVALID --> RELEASE_INPUT: 有已验真 observed facts
|
||||
DRAFT_INVALID --> FAILED: 无安全进展
|
||||
|
||||
RELEASE_INPUT --> EVIDENCE_GUARD: conclusion 非空
|
||||
RELEASE_INPUT --> FALLBACK: conclusion 为空或无 Draft
|
||||
|
||||
EVIDENCE_GUARD --> SEMANTIC_GUARD: 引用有效或修复成功
|
||||
EVIDENCE_GUARD --> FALLBACK: 引用无法安全验证
|
||||
SEMANTIC_GUARD --> SUCCESS: 证据支持最终结论
|
||||
SEMANTIC_GUARD --> FALLBACK: 无证据推断
|
||||
TECHNICAL_FAILURE --> FAILED
|
||||
|
||||
SUCCESS --> [*]
|
||||
FALLBACK --> [*]
|
||||
FAILED --> [*]
|
||||
CANCELLED --> [*]
|
||||
```
|
||||
|
||||
## 11. 示例
|
||||
|
||||
查询:`诊断切换企业失败的问题`
|
||||
|
||||
```text
|
||||
第 1 次 lookup_knowledge
|
||||
execution_status = SUCCEEDED
|
||||
returned_count = 5
|
||||
relevance_level = REFERENCE
|
||||
information_gain = NO_GAIN(模型)
|
||||
consecutiveLowYield = 1
|
||||
|
||||
第 2 次 query_logs
|
||||
execution_status = SUCCEEDED
|
||||
returned_count = 0
|
||||
information_gain = NO_GAIN(Harness)
|
||||
consecutiveLowYield = 2
|
||||
|
||||
Harness
|
||||
collectionState = SATURATED
|
||||
拒绝新的 Tool 调用
|
||||
注入 STOP_REQUIRED
|
||||
|
||||
最终结果
|
||||
stop_reason = INFORMATION_SATURATED
|
||||
release_outcome = FALLBACK
|
||||
SafeFallback.type = INSUFFICIENT_EVIDENCE
|
||||
展示已检查范围、客观结果、限制和需要补充的信息
|
||||
不发布 INTERNAL_FAILURE
|
||||
```
|
||||
|
||||
如果第二次查询返回了能够排除某个假设的日志,模型应标记 `GAINED`,低收益计数清零,允许继续进行有目标的诊断。
|
||||
|
||||
如果用户首次请求即缺少企业、时间范围等有效查询条件,模型可以零次 Tool 调用直接输出 `conclusion=null`,把缺口写入 `limitations.missing_info`。Release 将其发布为 `FALLBACK + MISSING_REQUIRED_CONTEXT`,不执行 EvidenceRepair 或 SemanticGuard。
|
||||
|
||||
## 12. 非目标
|
||||
|
||||
- 不通过简单增加 Tool、Token 或超时预算解决空转。
|
||||
- 不让 Tool 或 Harness 判断业务根因。
|
||||
- 不增加多级质量分数、`UNKNOWN` 或复杂状态矩阵。
|
||||
- 不引入 `new_count` 和跨 Tool 通用内容指纹。
|
||||
- 不新增独立 Progress Judge 模型调用。
|
||||
- 不重新引入 Planner/Executor/Verifier/Composer 多角色 Graph。
|
||||
|
||||
## 13. 模型 Token 与 Tool 拒绝审计
|
||||
|
||||
Run 使用 `RunBudget` 作为 Token 总账,`diagnosis_trace_event` 作为模型调用明细账,`AgentStep.token_count` 只作为 Diagnosis Agent 轮次摘要,不新增独立 Token 表。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
R["Router / System / Knowledge / Repair / Semantic"] --> G["GuardModelCall"]
|
||||
A["Diagnosis ReAct round"] --> I["HarnessModelInterceptor"]
|
||||
G --> U["Provider Usage"]
|
||||
I --> U
|
||||
U --> L["Run ModelCallLedger"]
|
||||
U --> B["RunBudget Token 总账"]
|
||||
U --> T["MODEL_TOKEN_USAGE Trace"]
|
||||
I --> S["AgentStep.token_count"]
|
||||
L --> F["RUN_FINISHED 对账摘要"]
|
||||
B --> F
|
||||
```
|
||||
|
||||
每个 `MODEL_TOKEN_USAGE` 只记录:
|
||||
|
||||
- `component` 与 `component_round`;
|
||||
- `usage_available`;
|
||||
- Usage 可用时的 `input_tokens / output_tokens / total_tokens`。
|
||||
|
||||
Usage 不可用时不写 Token 字段,不把未知值记成零;Run 结束以 `usage_unavailable_count` 和 `tokens_reconciled=false` 暴露缺口。`RUN_FINISHED` 同时记录预算总量与审计明细合计,便于 exact Run 对账。
|
||||
|
||||
Tool 请求进入 `HarnessToolInterceptor` 后若被进展协议、重复 scope、信息饱和或观察合同门禁拒绝,写入 `TOOL_REQUEST_REJECTED`。事件只含安全 Tool Call ID、Tool name 和稳定错误码,不含业务参数、normalized scope 正文、原始响应或内部异常。实际执行仍只由 `TOOL_INVOCATION` 表达,因此两类数量不能混用。
|
||||
|
||||
真实审计发现:`INVALID_PROGRESS_PROTOCOL` 已可完整观察,但当前不会增加 `NO_GAIN` 或触发信息饱和。连续协议拒绝可能产生高 Token 空转,这属于待确认的停止行为改造,不属于 Token 审计本身。
|
||||
|
||||
## 14. 验收标准
|
||||
|
||||
- [x] Tool 执行状态、信息增益、收集状态和发布状态职责分离。
|
||||
- [x] RAG `REFERENCE` 不再自动等同于诊断证据。
|
||||
- [x] `NO_EVIDENCE` 和重复的 `tool_name + normalized_scope` 被 Harness 确定性标记为 `NO_GAIN`;`REFERENCE` 由模型评价。
|
||||
- [x] 模型继续调用 Tool 前,对上一轮待评价结果给出 `GAINED` 或 `NO_GAIN`。
|
||||
- [x] 下一次 Tool Call Envelope 能回传上一轮语义评价;Harness 应用评价后才决定是否放行,并在调用业务 Tool 前剥离控制字段。
|
||||
- [x] 不引入独立 `next_action`;Harness 从 Tool Call 或 Draft 等实际输出推导模型行为。
|
||||
- [x] `stop-after-consecutive-no-gain` 可配置且默认值为 `2`;连续低收益达到阈值后 Harness 阻止新的 Tool 调用。
|
||||
- [x] 只对 `tool_name + normalized_scope` 做确定性去重,第一版不承诺自然语言语义去重。
|
||||
- [x] `INFORMATION_SATURATED` 与 `BUDGET_LIMIT_REACHED` 分开,硬预算不进入 `SATURATED`。
|
||||
- [x] `FAILED` 不被统计为 `NO_GAIN`,技术故障与证据不足保持区分。
|
||||
- [x] 模型可以零次 Tool 调用输出 `conclusion=null + limitations.missing_info`,收到 `STOP_REQUIRED` 后必须停止。
|
||||
- [x] `conclusion=null` 不触发 EvidenceRepair;只有存在结论时才执行完整 EvidenceGuard、EvidenceRepair 和 SemanticGuard 链路。
|
||||
- [x] SemanticGuard 能把无证据确定性结论转换为安全的证据不足结果。
|
||||
- [x] `DiagnosisReleaseUseCase` 统一处理 Draft、信息饱和和预算终止,并复用 `SafeFallback.observed_facts` 展示 `ProgressSnapshot`。
|
||||
- [x] 空或非法 Draft 仅在有已验真进展时降级为 `INSUFFICIENT_EVIDENCE`,无安全进展继续 fail closed。
|
||||
- [x] 不引入 `CONFIRMED / INSUFFICIENT_EVIDENCE / NEED_MORE_INFO` 第二套生命周期状态;后两种语义由 `SafeFallback.type` 表达。
|
||||
- [x] Tool Schema 只通过模型原生 Tool Calling 通道注册,不拼接进 Prompt。
|
||||
- [x] 原始复现 Query 在预算耗尽前正常收敛,不再发布通用 `INTERNAL_FAILURE`。
|
||||
- [x] Trace 能解释每轮信息增益和停止原因,但不保存原始 Tool 载荷或内部 thought。
|
||||
- [x] 模型调用 Token 可按组件和轮次对账,Run 总账与明细账差异显式可见。
|
||||
- [x] Tool 请求拒绝与实际 Tool invocation 分开审计,且拒绝事件不泄露 payload。
|
||||
@@ -1,6 +1,6 @@
|
||||
# Harness 与质量门禁
|
||||
|
||||
**更新日期**:2026-07-23
|
||||
**更新日期**:2026-07-27
|
||||
**状态**:当前可运行架构
|
||||
|
||||
## 1. Harness 定位
|
||||
@@ -57,6 +57,16 @@ SemanticGuard 使用隔离的单轮模型调用,只接收原始 query、完整
|
||||
|
||||
Trace 写入失败只记录警告,不应改变业务执行结果;`details` 禁止包含 Prompt、Thought、Draft 正文或 raw Tool payload。普通 Trace API 按 `sequence_no, id` 返回 Timeline。
|
||||
|
||||
### 6.1 Token 对账
|
||||
|
||||
所有 Harness 模型入口统一从 Provider Usage 记录 `MODEL_TOKEN_USAGE`:Router、System Chat、Knowledge Answer、Diagnosis Agent、Evidence Repair 和 SemanticGuard。事件按组件和组件轮次记录 input/output/total Token;Usage 不可用时只记录 unavailable,不估算消耗。
|
||||
|
||||
`RUN_FINISHED` 同时记录 RunBudget 总账、模型调用明细合计、不可用 Usage 数量和 `tokens_reconciled`。Diagnosis Agent 的每轮 total Token 还会回填 `AgentStep.token_count`;其他模型组件不创建伪 AgentStep。
|
||||
|
||||
### 6.2 Tool 拒绝
|
||||
|
||||
`TOOL_INVOCATION` 只表示进入业务 ToolBoundary 的实际执行。协议错误、重复 scope、信息饱和或观察合同拒绝使用独立 `TOOL_REQUEST_REJECTED`,只记录 Tool Call ID、Tool name 和稳定错误码。预算 Tool 计数、实际执行次数和 Harness 拒绝次数是三个不同观察维度。
|
||||
|
||||
## 7. Audit 安全
|
||||
|
||||
AgentStep 不保存 Prompt、消息正文、模型正文、Tool arguments 或 Thought。ToolInvocation 不保存完整 request、SQL/日志 query、raw response 或 Agent projection。Provider reasoning 仅写入独立 `agent_reasoning_audit`,不进入普通 Trace 或发布结果;无 Provider 内容时必须记录 unavailable,不能伪造。应用日志不得打印这些字段。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# MVP Issues 索引
|
||||
|
||||
**更新日期**:2026-07-23
|
||||
**更新日期**:2026-07-26
|
||||
**状态**:按活跃问题、设计笔记、RAG 问题集和已归档问题整理
|
||||
|
||||
## 目录约定
|
||||
@@ -17,6 +17,7 @@
|
||||
| 名称 | 标题 | 严重程度 | 状态 | 文件 |
|
||||
|---|---|---|---|---|
|
||||
| ISS-015 | 诊断运行质量与 Reasoning 审计收敛 | 高 | 待实施 | [active/ISS-015-diagnosis-runtime-quality-and-reasoning-audit.md](active/ISS-015-diagnosis-runtime-quality-and-reasoning-audit.md) |
|
||||
| ISS-016 | 诊断 Agent 缺少基于信息增益的停止契约 | 高 | 已实施,待归档 | [active/ISS-016-diagnosis-information-gain-stop-contract.md](active/ISS-016-diagnosis-information-gain-stop-contract.md) |
|
||||
|
||||
## 设计笔记
|
||||
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
# ISS-016 诊断 Agent 缺少基于信息增益的停止契约
|
||||
|
||||
**状态**:已实施;审计发现协议拒绝空转,暂不归档
|
||||
**严重程度**:高
|
||||
**发现时间**:2026-07-26
|
||||
**来源**:知识库无直接答案场景的真实 E2E 与停止策略复盘
|
||||
**关联**:ISS-015、ISS-012、ISS-002、ISS-009
|
||||
**架构设计**:[Diagnosis Agent 信息增益与停止控制架构](../../architecture/diagnosis-information-gain-stop-architecture.md)
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前单体 Diagnosis ReAct Agent 已具备 Tool 调用、Run 预算、EvidenceGuard、SemanticGuard、Trace 和安全发布边界,但停止条件仍主要依赖模型自行结束或 Harness 资源预算耗尽。
|
||||
|
||||
这使系统能够限制一次 Run 最多消耗多少资源,却不能稳定判断“继续查询是否还可能增加有效诊断信息”。当知识库只有通用参考资料、日志查询持续为空或用户缺少必要查询条件时,Agent 可能不断改写关键词和扩大尝试,最终由预算被动终止。
|
||||
|
||||
该问题不能简化为“Prompt 没写好”或“预算太大”:Prompt、模型终态契约、Tool 客观结果、模型语义评价和 Harness 强制停止之间缺少完整闭环。
|
||||
|
||||
## 2. 真实复现
|
||||
|
||||
用户 Query:
|
||||
|
||||
```text
|
||||
诊断切换企业失败的问题
|
||||
```
|
||||
|
||||
真实 E2E:
|
||||
|
||||
- `sessionId=iss015-enterprise-switch-e2e-20260726000222`
|
||||
- `runId=054afbb1-0bb3-48d2-8cb8-ddb31d20b2b6`
|
||||
- SSE:`metadata -> ROUTING -> DIAGNOSIS_RUNNING -> failure(INTERNAL_FAILURE) -> done(FAILED)`
|
||||
- Run 耗时约 48 秒。
|
||||
- Agent 已完成多轮模型和 Tool 调用。
|
||||
- `query_logs` 多次返回 `NO_EVIDENCE`。
|
||||
- `lookup_knowledge` 的检索层结果主要为 `REFERENCE`,但 Harness 投影仍表现为 `EVIDENCE_FOUND`。
|
||||
- 最后一次 Tool 调用触发 `BUDGET_EXHAUSTED`,Run 未生成最终 Draft,也未进入安全校验阶段。
|
||||
- 数据库和 Trace 中存在执行过程,但用户只能看到“当前暂时无法处理该请求,请稍后重试”。
|
||||
|
||||
这不是“没有执行过程”,而是 Agent 未能在无新增信息时主动结束,预算终止又被发布链路映射成了技术失败。
|
||||
|
||||
### 2.1 实施后验证
|
||||
|
||||
同一 Query 的最终 named SSE E2E:
|
||||
|
||||
- `sessionId=iss016-final-20260726-a`
|
||||
- `runId=3ab22ed7-d0ed-45d8-b928-dce5790c0542`
|
||||
- SSE 发布 `SAFE_FALLBACK`,`type=MISSING_REQUIRED_CONTEXT`,最终 `done.outcome=FALLBACK`。
|
||||
- 数据库记录 `status=SUCCESS`、`intent=DIAGNOSIS`、`release_outcome=FALLBACK`、`tool_call_count=0`、`total_token_count=2890`,answer 非空。
|
||||
- Trace 为 `RUN_STARTED -> ROUTING_ATTEMPT -> ROUTING_DECISION -> AGENT_MODEL_STEP -> EVIDENCE_GUARD_INITIAL -> RELEASE_DECISION/FALLBACK -> RUN_FINISHED/FALLBACK`。
|
||||
|
||||
本次零 Tool 是合法行为:原 Query 缺少企业、时间范围和错误信息,模型直接报告缺失上下文。另有 focused tests 固定“非法 Draft + 已验真 ProgressSnapshot -> INSUFFICIENT_EVIDENCE”和“非法 Draft + 无安全进展 -> FAILED”边界,避免模型随机返回非 JSON 时再次丢失已完成过程。
|
||||
|
||||
### 2.2 Token 与拒绝审计补充验证
|
||||
|
||||
2026-07-27 增加按模型组件/轮次的 Token 明细、AgentStep Token 回填、Run 对账摘要和 Tool 请求拒绝 Trace。审计事件不保存 Prompt、模型正文、Tool 参数或原始响应。
|
||||
|
||||
缺少上下文 E2E:
|
||||
|
||||
- `sessionId=audit-e2e-20260727-001`
|
||||
- `runId=87f38bea-108b-482a-8b72-d0890cc825f2`
|
||||
- Router Token `248`,Diagnosis Agent Token `2635`,Run 总 Token `2883`。
|
||||
- `step_count=1`,对应 `AgentStep.token_count=2635`;Tool 预算计数和实际执行均为 `0`。
|
||||
- `RUN_FINISHED.tokens_reconciled=true`,Trace 序列 `1..9` 连续。
|
||||
|
||||
有界 Tool E2E:
|
||||
|
||||
- `sessionId=audit-e2e-20260727-003`
|
||||
- `runId=3f8f4a0c-3b96-4942-be1e-61cc1431b337`
|
||||
- 5 次 Tool 实际执行:`lookup_knowledge=4`、`query_logs=1`。
|
||||
- 9 次 Tool 请求被 `INVALID_PROGRESS_PROTOCOL` 拒绝,均有独立 `TOOL_REQUEST_REJECTED`,没有伪装成 canonical invocation。
|
||||
- 13 个 Diagnosis Agent 轮次加 1 个 Router 调用,总 Token `68469`;各轮 AgentStep Token 之和加 Router Token与 Run 总量一致。
|
||||
- `RUN_FINISHED.tokens_reconciled=true`,Trace 序列 `1..52` 连续。
|
||||
|
||||
该 E2E 证明审计可观察、可对账、可按组件和轮次重放,也暴露了新的停止缺口:连续协议拒绝当前不产生 `NO_GAIN`,模型可在 Harness 拒绝后继续消耗模型轮次。这个 Run 的 Token 消耗不合理;后续需要单独确认“连续 `INVALID_PROGRESS_PROTOCOL` 是否进入确定性停止”的行为设计,不能仅靠提高或降低预算掩盖。
|
||||
|
||||
## 3. 核心问题
|
||||
|
||||
### 3.1 成功定义只有“完成诊断”
|
||||
|
||||
如果模型的唯一合法输出是完整 `DiagnosisDraft`,那么“证据不足”和“需要用户补充信息”不是一等终态。即使模型已经知道当前信息无法支持根因,它仍可能认为任务尚未完成,并继续调用 Tool。
|
||||
|
||||
### 3.2 Tool 客观结果与诊断价值混淆
|
||||
|
||||
Tool 可以可靠说明查询是否成功、范围、数量、检索分数、是否为空和是否截断,但不能判断结果是否支持当前诊断假设。
|
||||
|
||||
例如:
|
||||
|
||||
- 检索到三篇相似文档,只能说明存在候选内容,即当前兼容状态 `EVIDENCE_FOUND`;
|
||||
- 文档是直接证据、背景材料还是无关内容,需要模型结合当前假设判断;
|
||||
- 当前范围没有日志,只能说明 scoped `NO_EVIDENCE`,不能说明故障不存在。
|
||||
|
||||
把检索层 `REFERENCE` 直接表达为诊断层 `EVIDENCE_FOUND`,会向模型发送“仍在取得进展”的错误信号。
|
||||
|
||||
### 3.3 模型没有显式的信息价值判断义务
|
||||
|
||||
当前系统主要通过“模型是否继续调用 Tool”推测它是否认为查询有价值。对于无法由代码确定质量的成功非空结果,模型没有被要求给出最小、机器可读的信息增益判断:
|
||||
|
||||
```text
|
||||
information_gain = GAINED | NO_GAIN
|
||||
```
|
||||
|
||||
因此 Harness 无法区分“合理继续收集证据”和“换关键词重复尝试”。
|
||||
|
||||
### 3.4 Harness 只有资源预算,没有无进展契约
|
||||
|
||||
模型调用次数、Tool 调用次数、Token、字节和超时属于资源边界。它们是最后的安全保护,不应承担正常停止策略。
|
||||
|
||||
Harness 当前缺少以下可观察状态:
|
||||
|
||||
- 连续 `NO_GAIN` 次数;
|
||||
- 重复或等价查询;
|
||||
- 当前收集状态 `COLLECTING / SATURATED`;
|
||||
- 模型对需要语义评价的结果给出的 `GAINED / NO_GAIN`。
|
||||
|
||||
## 4. 顶层设计原则
|
||||
|
||||
### 4.1 不要求模型必须找到根因
|
||||
|
||||
诊断任务的首要目标是避免无依据结论,而不是每次都输出根因。模型只输出 `DiagnosisDraft`,不产生额外的诊断生命周期状态:
|
||||
|
||||
```text
|
||||
有受证据支持的 conclusion
|
||||
-> ReleaseOutcome.SUCCESS
|
||||
|
||||
conclusion=null,且形成了诚实、有界的结果
|
||||
-> ReleaseOutcome.FALLBACK
|
||||
|
||||
不可恢复的 Tool、模型或基础设施故障
|
||||
-> ReleaseOutcome.FAILED
|
||||
```
|
||||
|
||||
证据不足和缺少查询条件是 `FALLBACK` 的不同原因,不是与 `SUCCESS / FALLBACK / FAILED / CANCELLED` 平行的第二套状态机。现有 `SafeFallback.type` 负责表达 `INSUFFICIENT_EVIDENCE / MISSING_REQUIRED_CONTEXT` 等发布原因。
|
||||
|
||||
### 4.2 Tool 提供事实,模型判断价值
|
||||
|
||||
| 组件 | 职责 |
|
||||
|---|---|
|
||||
| Tool / Adapter | 返回客观执行状态、查询范围、数量、来源和有界结果 |
|
||||
| Diagnosis Agent | 判断结果对当前假设的语义价值 |
|
||||
| Harness | 记录进展、识别重复和不一致、执行确定性停止 |
|
||||
| EvidenceGuard / SemanticGuard | 校验最终引用真实性和结论支持度 |
|
||||
| Release | 把正常停止与技术失败发布为不同用户结果 |
|
||||
|
||||
不应让 Tool 声称业务证据价值,也不应让 Harness 通过规则替代模型完成诊断推理。
|
||||
|
||||
### 4.3 模型主动停止,Harness 确定性兜底
|
||||
|
||||
模型应优先根据 Prompt 和显式进展状态主动选择结束;模型未能收敛时,Harness 必须根据可验证规则停止后续 Tool 调用。
|
||||
|
||||
只加强 Prompt 不足以形成系统保证;只增加硬预算则会继续产生高成本、低信息量的失败 Run。
|
||||
|
||||
## 5. 目标交互契约
|
||||
|
||||
### 5.1 已确认的 Tool 侧决策
|
||||
|
||||
1. Tool 由服务端通过模型原生 Tool Calling 通道动态注册;Prompt 不写死 Tool 名称或 Schema,也不重复插入 Tool Schema 占位符。
|
||||
2. Tool 和 Projector 只产生客观结果,不判断业务根因和语义信息增益。
|
||||
3. `NO_EVIDENCE` 由 Projector 根据结果集合为空确定,不是 LLM 推导。
|
||||
4. `REFERENCE` 由 RAG 后处理规则根据归一化相似度及查询提示命中情况确定,不是 LLM 推导。
|
||||
5. `RagResultProjector` 必须兼容读取上游 `relevanceLevel` 或 `relevance_level`,并统一保留为 `relevance_level`。
|
||||
6. `EVIDENCE_FOUND` 暂时保留,但只表示存在候选内容,不表示存在能够支持诊断结论的证据。
|
||||
7. 第一版不引入 `new_count`,不建立跨 Tool 通用内容指纹。
|
||||
8. 不引入显式 `next_action`。模型发起 Tool Call 表示继续,输出 Draft 表示结束。
|
||||
|
||||
### 5.2 Tool 结果双视图
|
||||
|
||||
Tool 原始结果经过标准化后产生内部 Canonical Tool Result,再分别提供:
|
||||
|
||||
| 视图 | 消费者 | 内容 |
|
||||
|---|---|---|
|
||||
| Harness Control View | Harness / Canonical Store | 执行状态、数量、规范化 scope、相关度、重复指纹、预算与饱和控制信息 |
|
||||
| Model Observation | Diagnosis Agent | `tool_call_id`、实际 scope、有界 evidence、`evidence_status`、粗粒度 `relevance_level`、`truncated` |
|
||||
|
||||
原始相似度、检索轨迹、重复指纹、低收益计数、阈值、预算和原始 Tool Response 不进入模型上下文。只有 Harness 必须改变模型行为时,才注入有界 `STOP_REQUIRED` 指令和已检查范围摘要。
|
||||
|
||||
详细 Tool 架构图和调用流程图见架构文档的“Tool 结果与上下文边界”章节。
|
||||
|
||||
### 5.3 信息增益与停止
|
||||
|
||||
信息增益只保留:
|
||||
|
||||
```text
|
||||
information_gain = GAINED | NO_GAIN
|
||||
```
|
||||
|
||||
赋值规则:
|
||||
|
||||
```text
|
||||
FAILED
|
||||
-> 技术故障流程,不产生 information_gain
|
||||
|
||||
evidence_status=NO_EVIDENCE
|
||||
或重复的规范化 tool + scope
|
||||
-> Harness 直接赋 NO_GAIN
|
||||
|
||||
其他成功非空结果,包括 relevance_level=REFERENCE
|
||||
-> Diagnosis Agent 判断 GAINED / NO_GAIN
|
||||
```
|
||||
|
||||
Harness 使用连续 `NO_GAIN` 维护 `COLLECTING / SATURATED`。连续次数达到配置项 `stop-after-consecutive-no-gain` 后进入 `SATURATED`;默认值为 `2`,只对新 Run 生效,且不进入模型上下文。
|
||||
|
||||
硬调用上限不等于信息饱和。Harness 分别记录:
|
||||
|
||||
```text
|
||||
连续 NO_GAIN 达到阈值 -> stop_reason=INFORMATION_SATURATED
|
||||
达到 Tool 或 Run 硬预算 -> stop_reason=BUDGET_LIMIT_REACHED
|
||||
```
|
||||
|
||||
`SATURATED` 只表示信息饱和;预算限制属于独立的资源保护终止原因。
|
||||
|
||||
模型不显式声明行动状态。Harness 从模型实际发起 Tool Call 或输出 Draft 推导其行为。
|
||||
|
||||
### 5.4 Tool Call Envelope 协议
|
||||
|
||||
模型通过下一次 Tool Call 的通用 Envelope 回传上一轮语义评价:
|
||||
|
||||
```json
|
||||
{
|
||||
"previous_observation": {
|
||||
"tool_call_id": "call-123",
|
||||
"information_gain": "NO_GAIN"
|
||||
},
|
||||
"input": {
|
||||
"query": "下一次查询参数"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
协议约束:
|
||||
|
||||
- `previous_observation` 只在上一轮存在待模型评价的 Tool Observation 时必填;首次调用以及上一轮已由 Harness 客观赋值时省略。
|
||||
- Harness 在执行新 Tool 前校验 `tool_call_id`,应用 `information_gain` 并更新 `COLLECTING / SATURATED`。
|
||||
- 如果应用评价后进入 `SATURATED`,Harness 拒绝本次 Tool 调用并返回 `STOP_REQUIRED`。
|
||||
- Harness 消费并剥离 `previous_observation`;业务 Tool 只接收 `input` 中原有的业务参数。
|
||||
- 模型选择直接输出 DiagnosisDraft 时,不存在需要放行的下一次 Tool Call,因此无需额外回传最后一轮评价;Harness 将其记录为模型主动结束。
|
||||
- 上一个待评价结果未完成评价时,Harness 不接受新的 Tool 调用。
|
||||
|
||||
这是模型侧 Tool 调用协议变更,但不改变业务 Tool 的入参协议,也不增加独立 Progress Judge 调用,不把 Harness 内部状态暴露给模型,不把 `information_gain` 混入最终用户可见的 DiagnosisDraft。
|
||||
|
||||
### 5.5 已确认的 Prompt 原则
|
||||
|
||||
Prompt 使用中文,并保持最小职责,不解释 Projector、计数器、阈值、预算或 Harness 状态机。Tool 名称和 Schema 不写死在正文中,也不拼接进 Prompt,由服务端通过模型原生 Tool Calling 通道动态注册。
|
||||
|
||||
Prompt 必须明确:
|
||||
|
||||
- 模型不必须得出诊断结论或根因,但必须完成一个诚实、有界的 `DiagnosisDraft`;
|
||||
- `conclusion=null` 的证据不足结果是合法完成,不是失败;
|
||||
- Tool 调用次数可以为零;缺少有效查询所需的企业、时间、服务或错误信息时,应直接在 `limitations.missing_info` 中列出缺口;
|
||||
- 不得为了表现“已经排查”而执行没有明确范围、预期不会产生新诊断信息的 Tool 调用;
|
||||
- Tool 返回内容事实正确、表述完整或结果非空,不代表它对当前诊断有信息增益;
|
||||
- 只有新增事实确认、排除或缩小当前诊断假设时,才属于 `GAINED`;
|
||||
- 通用知识、背景说明、重复内容或不能改变当前判断的内容属于 `NO_GAIN`;
|
||||
- `NO_GAIN` 后不得通过改写相似关键词或重复相同 scope 继续尝试;
|
||||
- 如果仍存在范围明确、并可能产生新诊断信息的不同查询,可以继续;否则应主动停止并完成证据不足结果;
|
||||
- 收到服务端 `STOP_REQUIRED` 后必须停止调用 Tool。
|
||||
|
||||
Prompt 不要求模型输出 `next_action`。模型发起 Tool Call 或输出 Draft 即表达其实际选择。
|
||||
|
||||
## 6. 实施阶段(已完成)
|
||||
|
||||
### 阶段 1:终态与 Prompt 契约
|
||||
|
||||
- 保持 `conclusion=null` 作为合法的证据不足 Draft,并明确“有依据地停止”属于成功完成。
|
||||
- 按 5.5 的最小原则重写中文 Prompt,Tool 定义只通过模型原生 Tool Calling 通道动态注册。
|
||||
- 允许零次 Tool 调用;查询条件不足时使用现有 `limitations.missing_info`,不新增 `required_context`。
|
||||
- 明确正确但不能推进当前诊断的 Tool 内容属于 `NO_GAIN`,不得触发等价重试。
|
||||
- 保持 Tool 预算作为最终安全保护。
|
||||
|
||||
### 阶段 2:Tool 状态与模型评价解耦
|
||||
|
||||
- 标准化 Canonical Tool Result,并拆分 Harness Control View 与 Model Observation。
|
||||
- `RagResultProjector` 兼容并保留 `relevance_level`。
|
||||
- 不把检索命中、候选文档或 `REFERENCE` 自动等同于诊断证据。
|
||||
- 通过白名单控制进入模型上下文的 Tool 字段。
|
||||
|
||||
### 阶段 3:Run 级最小进展状态
|
||||
|
||||
- 在 RunContext 中维护已检查 `tool + scope`、连续 `NO_GAIN` 和 `COLLECTING / SATURATED`。
|
||||
- 只对 `tool_name + normalized_scope` 做确定性参数去重;第一版不做自然语言语义去重。
|
||||
- 使用配置项 `stop-after-consecutive-no-gain` 控制连续低收益阈值,默认值为 `2`。
|
||||
- 实现通用 Tool Call Envelope,由下一次 Tool Call 回传上一轮 `information_gain`,并在进入业务 Tool 前剥离控制字段。
|
||||
- 不把 Prompt、内部 thought 或原始 Tool 载荷混入普通 Trace 和用户结果。
|
||||
|
||||
### 阶段 4:Harness 确定性停止
|
||||
|
||||
- 拒绝相同 `tool_name + normalized_scope` 的重复查询。
|
||||
- 达到无进展条件时阻止新的 Tool 调用。
|
||||
- 分别产生 `INFORMATION_SATURATED / BUDGET_LIMIT_REACHED`,不把硬预算终止伪装成信息饱和。
|
||||
|
||||
### 阶段 5:安全发布与 E2E
|
||||
|
||||
- 每次 Tool 完成后只保存 Canonical Tool Result;Tool Loop 结束时一次性投影有界 `ProgressSnapshot`。
|
||||
- `DiagnosisReleaseUseCase` 同时接受正常 Draft 和 `stop_reason + ProgressSnapshot`,统一产生 `SUCCESS / FALLBACK`;`ChatApplicationUseCase` 不再单独决定预算 Fallback 的业务内容。
|
||||
- 复用现有 `SafeFallback.observed_facts / limitations / next_steps` 和前端渲染协议,并为 `FallbackType` 增加 `INSUFFICIENT_EVIDENCE / MISSING_REQUIRED_CONTEXT`。
|
||||
- `conclusion=null` 不进入 EvidenceRepair;有 Tool 引用时只验证引用真实性,并从 `ProgressSnapshot` 生成 Fallback。只有存在结论时才执行完整 EvidenceGuard、EvidenceRepair 和 SemanticGuard 链路。
|
||||
- 不依赖预算耗尽后的额外模型调用来生成 Fallback。
|
||||
- 前端明确区分证据不足、需要补充信息和技术故障。
|
||||
|
||||
## 7. 非目标
|
||||
|
||||
- 不通过简单增加 Tool/Token 预算解决问题。
|
||||
- 不把“降低最大 Tool 调用次数”当作信息增益策略。
|
||||
- 不引入 `new_count`、多级质量评分或显式 `next_action`。
|
||||
- 不增加独立 Progress Judge 模型调用。
|
||||
- 不依赖 Prompt 作为唯一控制手段。
|
||||
- 不让 Tool 或规则代码判断业务根因。
|
||||
- 不重新引入已由 ISS-014 删除的 Planner/Executor/Verifier/Composer 旧业务 Graph。
|
||||
- 不在本 Issue 中解决 Reasoning 原文审计治理;该内容仍由 ISS-015 跟踪。
|
||||
|
||||
## 8. 验收标准
|
||||
|
||||
- [x] 不引入 `CONFIRMED / INSUFFICIENT_EVIDENCE / NEED_MORE_INFO` 第二套诊断生命周期状态;最终状态只使用 `SUCCESS / FALLBACK / FAILED / CANCELLED`。
|
||||
- [x] Tool 客观命中状态与模型诊断价值评价在契约和 Trace 中明确分离。
|
||||
- [x] Tool 结果拆分为 Harness Control View 和白名单 Model Observation,Harness 内部计数、阈值和预算不进入模型上下文。
|
||||
- [x] `RagResultProjector` 保留 `relevance_level`,且模型与 Harness 都能获得各自所需的有界视图。
|
||||
- [x] `NO_EVIDENCE` 和重复的规范化 `tool + scope` 被 Harness 确定性标记为 `NO_GAIN`;RAG `REFERENCE` 由模型判断 `GAINED / NO_GAIN`。
|
||||
- [x] 模型继续调用 Tool 前,对上一轮待评价结果返回 `GAINED / NO_GAIN`,不返回 `next_action`。
|
||||
- [x] 下一次 Tool Call Envelope 能回传上一轮语义评价;Harness 应用评价后才决定是否放行,并在调用业务 Tool 前剥离控制字段。
|
||||
- [x] Prompt 明确不要求模型必须得出根因,`conclusion=null` 是合法完成结果。
|
||||
- [x] Prompt 允许零次 Tool 调用;缺少必要查询条件时使用 `limitations.missing_info`,不强制执行无明确目标的查询。
|
||||
- [x] Prompt 明确正确但不能推进诊断的 Tool 内容属于 `NO_GAIN`,并禁止相似关键词或相同 scope 的等价重试。
|
||||
- [x] `REFERENCE`、候选文档或非空结果不会自动成为支持根因的证据。
|
||||
- [x] 相同 `tool_name + normalized_scope` 的重复查询能够被 Harness 识别并阻止;第一版不承诺自然语言语义去重。
|
||||
- [x] `stop-after-consecutive-no-gain` 可配置且默认值为 `2`;`GAINED` 清零连续计数。
|
||||
- [x] `INFORMATION_SATURATED` 与 `BUDGET_LIMIT_REACHED` 在 Trace 和 Release 输入中保持区分。
|
||||
- [x] 连续无新增信息时,Run 在资源预算耗尽前收敛为正常业务结果。
|
||||
- [x] 模型主动停止和 Harness 强制停止都能输出已检查来源、查询范围、客观结果、信息缺口和下一步。
|
||||
- [x] 无证据不被解释为故障不存在,通用参考资料不被解释为当前故障事实。
|
||||
- [x] 真正取得新证据的多轮诊断不会被无进展策略过早终止。
|
||||
- [x] 原始复现 Query 不再以 `INTERNAL_FAILURE` 结束,也不再通过大量改写查询运行到 `BUDGET_EXHAUSTED`。
|
||||
- [x] Tool Loop 结束时由 Canonical Tool Result 一次性投影 `ProgressSnapshot`,并映射到现有 `SafeFallback.observed_facts`,无需新增前端协议。
|
||||
- [x] `DiagnosisReleaseUseCase` 统一处理正常 Draft、信息饱和和预算终止;只有不可形成安全业务结果的技术故障发布为 `FAILED`。
|
||||
- [x] `conclusion=null` 不触发 EvidenceRepair;有结论时才执行完整 EvidenceGuard、EvidenceRepair 和 SemanticGuard 链路。
|
||||
- [x] 最终 Draft 合同失败时丢弃非法内容;仅在当前 Run 有已验真 ProgressSnapshot 时发布 `INSUFFICIENT_EVIDENCE`,否则保持 `FAILED`。
|
||||
- [x] 原始复现 Query 在缺少企业、时间和错误信息时返回 `FALLBACK + MISSING_REQUIRED_CONTEXT`,或在有限排查后返回 `FALLBACK + INSUFFICIENT_EVIDENCE`,并展示已经完成的检查。
|
||||
- [x] exact `sessionId + runId` Trace 能解释每轮继续或停止的原因,但不泄露 Prompt、内部 thought、原始 Tool 载荷或凭据。
|
||||
- [x] 每个 Provider Usage 可按模型组件和组件轮次审计,Diagnosis Agent Usage 回填 AgentStep,Run 结束显式记录 Token 是否对账。
|
||||
- [x] Harness 拒绝的 Tool 请求与实际 `TOOL_INVOCATION` 分开记录,拒绝 Trace 不包含 Tool 参数或原始响应。
|
||||
- [ ] 连续 `INVALID_PROGRESS_PROTOCOL` 拒绝应在硬模型/Token 预算前确定性停止;当前真实 E2E 仍出现 9 次拒绝和 13 个 Agent 轮次。
|
||||
|
||||
## 9. 已确认的首版边界
|
||||
|
||||
1. 连续 `NO_GAIN` 阈值由 `stop-after-consecutive-no-gain` 配置,默认值为 `2`,后续通过固定 E2E 和评测集校准。
|
||||
2. 重复检测只比较 `tool_name + normalized_scope`,不做自然语言语义去重。
|
||||
3. 模型可以在首次 Tool 调用前以 `conclusion=null + limitations.missing_info` 合法结束。
|
||||
4. Harness 产生内部 `stop_reason`;Release 产生 `SafeFallback.type` 和最终 `release_outcome`。
|
||||
5. Tool Schema 只通过模型原生 Tool Calling 通道注册,不拼接进 Prompt。
|
||||
|
||||
## 10. 相关文件
|
||||
|
||||
- `mvp/issues/active/ISS-015-diagnosis-runtime-quality-and-reasoning-audit.md`
|
||||
- `mvp/issues/archived/ISS-002-executor-unconstrained-lookup.md`
|
||||
- `mvp/issues/archived/ISS-012-executor-token-budget-and-context-growth.md`
|
||||
- `mvp/issues/archived/ISS-009-negative-observation-no-evidence-reference.md`
|
||||
- `mvp/architecture/agent-orchestration.md`
|
||||
- `mvp/architecture/harness-quality-gates.md`
|
||||
- `mvp/architecture/diagnosis-information-gain-stop-architecture.md`
|
||||
Reference in New Issue
Block a user