Files
zhuyongxin 38f781b157 feat(harness): complete protocol repair stop and archive ISS-016
Add repairable INVALID_PROGRESS_PROTOCOL observations, independent
PROGRESS_PROTOCOL_VIOLATED saturation, and controlled release paths.
Archive the OpenSpec change after syncing main specs and devflow.
2026-07-27 19:10:07 +08:00

18 KiB
Raw Permalink Blame History

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 协议变更。

Apply Continuation: Task 8 Protocol Repair + Bounded Stop

  • Checkpoint:Apply。
  • Capability source:openspec-apply-change + sm-flow apply 协议。
  • 背景:tasks 1–7 已完成;真实 E2E 暴露连续 INVALID_PROGRESS_PROTOCOL 不会累计 NO_GAIN,可能在硬预算前空转。Task 8 补齐协议修复反馈与独立兜底停止。
  • 实现事实(代码已在工作区,本轮补齐测试与收口):
    • ProgressProtocolViolationType / ProgressProtocolViolationException 覆盖 MISSING_PREVIOUS_OBSERVATION、OUT_OF_ORDER、UNEXPECTED、MISSING_INPUT、INVALID_ENVELOPE。
    • DiagnosisProgressTracker 独立累计连续协议错误,默认阈值 2,达到后 stop_reason=PROGRESS_PROTOCOL_VIOLATED。
    • HarnessToolInterceptor 返回可修正 observation(repair_required、violation_type、missing_field、expected_previous_tool_call_id、allowed_information_gain);达阈一次 STOP_REQUIRED,再请求抛 DiagnosisCollectionStoppedException。
    • DiagnosisReleaseUseCase 支持 PROGRESS_PROTOCOL_VIOLATED:有安全 ProgressSnapshot 发 INSUFFICIENT_EVIDENCE,无进展 fail closed。
    • TOOL_REQUEST_REJECTED 记录 violation_type、repair_prompt_delivered、consecutive_protocol_violations、stop_reason,不记录参数/观察正文/异常。
  • 验证:
    • Focused:DiagnosisProgressTrackerTest、HarnessToolInterceptorTest、DiagnosisReleaseUseCaseTest、DiagnosisAgentUseCaseTest、HarnessChatConfigurationTest 通过。
    • OpenSpec strict validate 通过。
  • 文档:ISS-016 剩余协议停止项勾选完成;ISS-015 阶段 1 标记已完成;架构文档同步协议错误独立停止语义。
  • OpenSpec tasks 8.1–8.6 全部完成。剩余 Apply 工作:无。可进入 Archive checkpoint(需用户确认是否归档 OpenSpec)。

Archive

  • Checkpoint:Archive。
  • Capability source:sm-flow archive 协议 + openspec-archive-change。
  • 用户确认:明确要求“执行(archive),完后提交推送”。
  • devflow 档案:
    • brief.md、evidence.md、decisions.md、acceptance.md
    • 更新 devflow/index.md、devflow/glossary/CONTEXT.md
  • OpenSpec:
    • delta specs 已同步到 main specs(含新建 diagnosis-information-gain-stop-contract)
    • change 归档至 openspec/changes/archive/2026-07-27-diagnosis-information-gain-stop-contract/
  • 不创建独立 ADR:决策已由 OpenSpec/ISS/架构文档承载,且可通过 OpenSpec 回滚。
  • 状态:archived。