# 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`