feat(harness): add information gain stop and audit
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
# Diagnosis 信息增益停止契约 Decisions
|
||||
|
||||
## Discover Status
|
||||
|
||||
- Checkpoint:Discover。
|
||||
- Capability source:`sm-flow` 内置 Discover 协议;grill 使用 `grill-with-docs`,代码可证问题通过源码、测试和引用搜索处理。
|
||||
- Scale:`complex`。变更跨越 Agent、Tool 协议、Run 生命周期、Release、Guard、配置、Trace 和 E2E。
|
||||
- 接口影响:L3。模型可见 Tool Schema 发生有意协议变更,公开 HTTP/SSE 和业务 Tool backend 协议不变。
|
||||
- 工具降级:当前没有 `codebase-retrieval` 和 LSP 工具;以 `rg`、源码和测试引用核查替代。用户已明确“可以忽略gitnexus”。
|
||||
|
||||
## Question Pool
|
||||
|
||||
| # | 维度 | 问题 | 模式 | 状态 |
|
||||
|---|---|---|---|---|
|
||||
| Q1 | 术语 | Tool 客观状态、信息增益、收集状态、停止原因和最终发布状态是否应合并为一个枚举? | user-interview | 已解决 |
|
||||
| Q2 | 语义 | Tool 返回价值由谁判断,是否需要多级质量分数? | user-interview | 已解决 |
|
||||
| Q3 | 协议 | 模型继续调用 Tool 时如何回传上一轮信息增益,是否需要 `next_action`? | user-interview | 已解决 |
|
||||
| Q4 | 边界 | Tool Schema 应来自 Prompt 还是服务端原生 Tool Calling 注册? | user-interview | 已解决 |
|
||||
| Q5 | 边界 | Harness 能确定性判定哪些 `NO_GAIN`,RAG `REFERENCE` 由谁判定? | user-interview | 已解决 |
|
||||
| Q6 | 范围 | 首版是否需要 `new_count` 或自然语言语义去重? | user-interview | 已解决 |
|
||||
| Q7 | 配置 | 连续无增益阈值是否可配置,默认值与生效时机是什么? | user-interview | 已解决 |
|
||||
| Q8 | 发布 | 信息饱和、预算终止和 `conclusion=null` 由谁转换为用户可见结果? | user-interview | 已解决 |
|
||||
| Q9 | 验收 | 如何证明未知问题不再以通用内部错误结束,同时不放过无证据结论? | evidence-driven | 已解决 |
|
||||
| Q10 | 技术 | 当前 Tool schema 是否能直接容纳 `previous_observation`? | evidence-driven | 已解决 |
|
||||
| Q11 | 技术 | 进展控制状态应扩展 Redis Store 还是放入 RunContext handle? | evidence-driven | 已解决 |
|
||||
| Q12 | 技术 | RAG `relevance_level` 在哪一层丢失,前端是否已有过程展示能力? | evidence-driven | 已解决 |
|
||||
|
||||
## Evidence-driven
|
||||
|
||||
| 结论 | 证据来源 | 是否已汇报用户 |
|
||||
|---|---|---|
|
||||
| 当前三个 Agent-facing Tool 直接使用 `RagToolRequest`、`QueryLogsRequest`、`MysqlToolRequest` 生成 Schema;要增加 `previous_observation + input` 必须显式演进 Tool Schema,不能只改 interceptor。 | `HarnessEvidenceTools`、三个 request records、`DiagnosisAgentFactory` | 已汇报 |
|
||||
| `HarnessToolInterceptor` 当前把完整 `agentResult` 放入 `ToolCallResponse.content`,控制视图与模型观察尚未分离。 | `HarnessToolInterceptor` | 已汇报 |
|
||||
| `RunContext` 已采用结构不可变、可变状态存在线程安全 handle 的模式;进展 tracker 放入 RunContext 比扩展 Redis 按 Run 枚举更符合现有所有权。 | `RunContext`、`DiagnosisHarnessCore.startRun` | 已汇报 |
|
||||
| `CanonicalInvocationStore` 只有 begin/find/markReady/markError,扩展按 Run 枚举会影响 Redis 实现和多组 fake store;首版可由 tracker 保存完成调用 key,在结束时按 key 读取 canonical 记录。 | `CanonicalInvocationStore` 及其引用测试 | 已汇报 |
|
||||
| `RagResultProjector` 只按 evidence 是否为空生成 `EVIDENCE_FOUND / NO_EVIDENCE`,没有读取上游 `relevanceLevel / relevance_level`。 | `RagResultProjector`、`LookupResult`、`KnowledgeEvidencePostProcessor` | 已汇报 |
|
||||
| `DiagnosisReleaseUseCase.execute` 当前强制 draft 非空并对所有 Draft 运行 EvidenceGuard;`EvidenceGuard` 又把空 analysis 判为 `ANALYSIS_MISSING`,与合法无结论结果冲突。 | `DiagnosisReleaseUseCase`、`EvidenceGuard` | 已汇报 |
|
||||
| `ChatApplicationUseCase.recoverBudgetExhaustion` 已有未提交预算 Fallback,但它绕过 Diagnosis Release,需迁移而不是丢弃用户价值。 | `ChatApplicationUseCase`、`SafeFallbackFactory`、现有测试 diff | 已汇报 |
|
||||
| 前端已渲染 `observed_facts / verified_sources / limitations / next_steps`,不需要新增公开展示协议。 | `src/main/resources/static/app.js` | 已汇报 |
|
||||
| 验收必须同时覆盖主动无结论、Harness 饱和、预算终止、无证据结论被 Guard 拦截,以及原始未知 Query 的 live SSE、日志和 exact run 数据。 | 当前事故现象、ISS-016 验收项、现有 E2E 工具 | 已汇报 |
|
||||
|
||||
## User-interview
|
||||
|
||||
| 问题原文 | 用户原话 | 确认状态 | OpenSpec 回写 |
|
||||
|---|---|---|---|
|
||||
| 是否精简状态而不建立第二套生命周期? | “这里我觉得设计得太混乱了,怎么简化”以及对最终简化架构“我觉得可以” | 已确认 | 已回写 |
|
||||
| 是否只保留 `GAINED / NO_GAIN`? | “质量状态只要 information_gain = GAINED \| NO_GAIN 就够了?”后确认“我觉得可以” | 已确认 | 已回写 |
|
||||
| 是否需要 `new_count`? | “好那就去掉new_count” | 已确认 | 已回写 |
|
||||
| 是否需要 `next_action`? | “那就去掉next_action,我觉得由llm自己去判断就好了,不用显示的指定” | 已确认 | 已回写 |
|
||||
| Tool 如何注入? | “首先 tool注入,由服务端注入,而不是写死在提示词中” | 已确认 | 已回写 |
|
||||
| Prompt 是否强调合法放弃和正确但无用的内容? | “需要说明 模型不必须要给出一个答案”以及“如果你发现工具返回的是正确但对推导无用的废话,请停止调用” | 已确认 | 已回写 |
|
||||
| 阈值是否可配置? | “我觉得这个可以暴露出一个配置来控制” | 已确认 | 已回写 |
|
||||
| 首版重复检测做到什么程度? | “也就是说这一版只是做参数的去重校验”后确认“可以” | 已确认 | 已回写 |
|
||||
| 是否按当前简化方案进入实施? | “可以用更简单的方式”“可以。修正一下文档”“用sm-flow开始实施把” | 已确认 | 已回写,并授权完成 Commit 后进入 Apply |
|
||||
|
||||
## 关键取舍
|
||||
|
||||
- 决策:停止权归 Harness,语义价值判断由模型与确定性规则共同产生。
|
||||
- 原因:Tool 只能知道客观返回,模型才能判断内容是否推进当前假设;但空结果和完全重复 scope 可由代码零 Token 判定。
|
||||
- 影响:Harness 只消费二值信息增益,不引入独立 Judge 或质量分数。
|
||||
- 决策:使用下一次 Tool Call Envelope 回传上一轮模型评价。
|
||||
- 原因:模型只有看到 Tool Observation 后才能评价,下一次真实行为正好提供受 Schema 约束的回传边界。
|
||||
- 影响:这是 L3 Agent-facing Tool Schema 变更,业务 request 在 interceptor 内解包后保持不变。
|
||||
- 决策:进展 tracker 是 RunContext handle,Canonical Store 保持 Tool 真相源。
|
||||
- 原因:停止决策需要低延迟 Run 内状态,完整证据仍应由 canonical 记录提供;两者职责不同。
|
||||
- 影响:tracker 保存计数、scope、待评价调用和 canonical keys,不复制 raw payload。
|
||||
- 决策:不创建 ADR。
|
||||
- 原因:这些是 ISS-016 范围内可通过 OpenSpec 回滚的内部协议演进,已有架构文档详细记录取舍,尚不满足独立 ADR 的必要性。
|
||||
|
||||
## OpenSpec 回写
|
||||
|
||||
- 必须进入 proposal/design/spec/tasks:Tool Envelope、L3 影响、Run tracker、确定性 `NO_GAIN`、RAG `relevance_level`、双视图、STOP_REQUIRED、ProgressSnapshot、统一 Release、Prompt、配置和 E2E。
|
||||
- 必须保留非目标:无 `new_count`、无 `next_action`、无 Judge、无语义去重、无第二套诊断生命周期、无公开 SSE 协议新增。
|
||||
- 当前没有未确认的 user-interview 问题,也没有 devflow/OpenSpec 冲突。
|
||||
|
||||
## Cross-Artifact 对齐检查
|
||||
|
||||
| 上游 → 下游 | 检查内容 | 状态 |
|
||||
|---|---|---|
|
||||
| ISS-016/架构文档 → proposal | 未知问题、合法放弃、信息增益、饱和停止、双视图、统一 Release、非目标和 E2E | 已对齐 |
|
||||
| proposal → design | L3 Envelope、Run tracker、scope、STOP_REQUIRED、ProgressSnapshot、预算终态、Prompt 和迁移方案 | 已对齐 |
|
||||
| design → specs/tasks | 状态机、Tool 门禁、RAG relevance、无结论 Guard、Release 所有权、Trace 和兼容边界 | 已对齐 |
|
||||
| specs → tasks | 每个可观察行为均有 contract/state/loop/release/config/E2E 可执行切片 | 已对齐 |
|
||||
|
||||
### Gap 详情
|
||||
|
||||
- 无。
|
||||
|
||||
## Architecture Audit
|
||||
|
||||
- Capability source:`zoom-out`。按 glossary 的 Diagnosis Agent、Diagnosis Harness、RunContext、Evidence Status、Invocation Status、Release Outcome 术语审计。
|
||||
- 顶层链路:`ChatApplicationUseCase -> DiagnosisChatExecutor -> DiagnosisAgentUseCase -> ReactAgent/interceptors -> ToolBoundary/canonical store -> ProgressSnapshot -> DiagnosisReleaseUseCase -> SSE/persistence`。
|
||||
- 所有权:Agent 负责诊断语义;Tool/Projector 负责客观结果;Run tracker 负责停止控制;Canonical Store 负责 Tool 真相;Guard 负责引用与结论安全;Release 负责用户可见 SUCCESS/FALLBACK;Application 只负责编排和持久化。
|
||||
- `RunContext` 的生产代码构造点只有 `DiagnosisHarnessCore.startRun`,大量测试通过该工厂获取;新增 tracker 不需要扩散手工构造。
|
||||
- `DiagnosisAgentUseCase`、`HarnessToolInterceptor`、`HarnessEvidenceTools` 和 `DiagnosisReleaseUseCase` 的直接消费者均已由配置类和 focused tests 覆盖,任务清单包含所有构造调用更新。
|
||||
- `FallbackType` 新语义只通过通用 SafeFallback JSON/前端渲染消费,没有前端枚举 switch;公开协议不新增字段。
|
||||
- 最大框架风险是 Tool Envelope Schema 和 STOP_REQUIRED 后异常传播;design 要求三个具体 record、真实 callback schema 测试和 scripted framework-loop 测试在 Release 迁移前锁定行为。
|
||||
- 最大生命周期风险是预算已把 RunLifecycle 置为 `BUDGET_EXHAUSTED` 后 Application 再次 `checkActive`;design 将其限制为“Diagnosis Release 已处理的预算 Fallback”窄分支,并禁止 Application 重建业务内容。
|
||||
- 审计结论:模块职责没有形成新的循环依赖或第二真相源;L3 风险已进入 specs 和 tasks,可进入 commit gate。
|
||||
|
||||
## Commit Gate Preflight
|
||||
|
||||
- `proposal.md`、`design.md`、六份 capability delta specs 和 `tasks.md` 均存在。
|
||||
- `openspec status --change diagnosis-information-gain-stop-contract --json` 返回 `isComplete=true`。
|
||||
- `openspec validate diagnosis-information-gain-stop-contract --strict` 通过。
|
||||
- question pool 全部已解决;evidence-driven 结论已汇报;user-interview 决策均有用户原话和确认状态。
|
||||
- 接口影响已判为 L3,并有独立 Interface Impact、兼容、迁移、回滚和验收说明。
|
||||
- Cross-artifact 检查无 gap;架构风险均已进入 design/tasks。
|
||||
- 用户已通过“用sm-flow开始实施把”明确授权 Commit 后进入 Apply。
|
||||
|
||||
## Pre-apply Research
|
||||
|
||||
### 参考实现
|
||||
|
||||
- `HarnessEvidenceTools`:现有三类 `FunctionToolCallback` 注册点和 adapter bridge,继续作为 Agent-facing Schema 唯一入口。
|
||||
- `HarnessToolInterceptor`:可获得 exact framework Tool Call ID,适合消费 Envelope 和执行 progress gate。
|
||||
- `ToolBoundary`:Tool 预算、Run 校验、canonical 写入和安全错误的单点,不在 interceptor 重复 reserve。
|
||||
- `RunContext` / `DiagnosisHarnessCore.startRun`:结构不可变 + 可变 handle 模式和唯一生产构造点。
|
||||
- `RagResultProjector` / `QueryLogsResultProjector` / `MysqlResultProjector`:bounded canonical agent result 的现有标准化模式。
|
||||
- `EvidenceGuard` / `DiagnosisReleaseUseCase`:当前结论验证链和无结论冲突位置。
|
||||
- `ChatApplicationUseCase.recoverBudgetExhaustion`:保留用户价值、需要迁移所有权的临时预算 Fallback。
|
||||
- `DiagnosisAgentUseCaseTest.ScriptedChatModel`:真实框架 model -> Tool -> model loop 回归模式。
|
||||
|
||||
### 技术栈清单
|
||||
|
||||
- Tool Schema:三个具体 record 交给 Spring AI `FunctionToolCallback.inputType`,共享 `PreviousObservation`,不使用泛型擦除或 JsonNode Schema。
|
||||
- JSON:继续使用项目 `ObjectMapper` 严格解析/序列化;控制字段在 interceptor 消费后只传业务 input。
|
||||
- Run 状态:新增线程安全 tracker handle,由 `DiagnosisHarnessCore.startRun` 创建,不使用 ThreadLocal。
|
||||
- Canonical 真相:继续使用 `ToolCallKeyFactory + CanonicalInvocationStore.find`;tracker 只记录 identity。
|
||||
- 视图:从 bounded canonical `agent_result` 白名单投影 Model Observation,不读取 raw response。
|
||||
- 异常:受控停止使用专用异常和 cause-chain 分类;未知异常保持 fail closed。
|
||||
- 测试:JUnit 5、scripted ChatModel、现有 fake store/adapter fixture;不增加 Maven 依赖。
|
||||
|
||||
### 新建基础设施
|
||||
|
||||
- `harness.progress`:信息增益、收集状态、停止原因、tracker、scope、snapshot/projector。
|
||||
- `harness.agent`:三个 Agent-facing Envelope、白名单 observation projector、受控停止异常和执行结果。
|
||||
- 不新增数据库表、Redis 数据结构、HTTP DTO、SSE event 或外部依赖。
|
||||
|
||||
## Apply 期间设计补充:Draft 合同失败
|
||||
|
||||
- 真实 E2E `runId=4e667111-524e-4407-87ab-b4b262952017` 已完成一次 READY RAG 调用,第二轮模型返回文本后在 Draft/Release 边界失败;后续三次同 Query 均走零 Tool 的 `MISSING_REQUIRED_CONTEXT`,证明模型输出存在随机分支。
|
||||
- 用户确认采用窄化降级:非法 Draft 自身不被接受;已有当前 Run 的安全 ProgressSnapshot 时发布 `INSUFFICIENT_EVIDENCE`,没有安全过程时继续 `FAILED`。
|
||||
- 这是有意行为变更:从“所有非法 Draft 都发布技术失败”调整为“非法 Draft + 已验真过程可发布过程型 Fallback”;公开 SSE 字段、Tool 协议和最终生命周期枚举不变。
|
||||
- 不新增 stop reason,不把 Draft 解析失败伪装成 `INFORMATION_SATURATED` 或 `BUDGET_LIMIT_REACHED`;使用 Agent 输出异常携带有界 snapshot,并以脱敏 Trace 区分输出合同失败。
|
||||
|
||||
## Apply Verification
|
||||
|
||||
- Focused tests:`DiagnosisAgentUseCaseTest`、`DiagnosisReleaseUseCaseTest`、`DiagnosisChatExecutorTest`、`HarnessChatConfigurationTest` 通过。
|
||||
- 完整回归:`mvn -q -Dtest='!MilvusConnectionTest' test` 退出码为 `0`;本轮 Surefire 报告汇总 `Tests=292, Failures=0, Errors=0, Skipped=3`。
|
||||
- 外部凭据边界:未排除时唯一失败为 `MilvusConnectionTest.connect`,原因是当前测试进程未设置 `MILVUS_TOKEN`;这不是本变更回归。
|
||||
- OpenSpec:`openspec.cmd validate diagnosis-information-gain-stop-contract --strict` 通过。
|
||||
- 格式与清理:`git diff --check` 通过;未发现临时 E2E JSON、DEBUG 或 tmp 文件。
|
||||
- named SSE E2E:Query `诊断切换企业失败的问题`,`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`。
|
||||
- 兼容性:公开 HTTP/SSE 字段、前端 SafeFallback 消费结构、数据库表和业务 Tool request 均未新增字段;模型侧 Tool Envelope 是本变更已确认的 L3 协议变更。
|
||||
Reference in New Issue
Block a user