# Harness progress 代码学习笔记:从拦截器五道门到唯一发布点 **更新日期**:2026-08-03 **主题**:Progress 子系统的代码落地——拦截器五道门、Tracker 状态机、canonical 生命周期、执行门禁、投影与发布闭环 **术语与状态**:[CONTEXT.md](CONTEXT.md) · [Harness生命周期与状态.md](Harness生命周期与状态.md) **设计视角**:[Harness 信息增益停止-让无证据诊断正常收敛](Harness信息增益停止-让无证据诊断正常收敛.md)(讲「为什么这样设计」) **本文视角**:代码里怎么落地(类地图、调用链、生命周期、状态机、门禁、易错点、面试话术) ## 1. 定位:双停止机制与三方判断权 ### 1.1 双停止机制(progress 存在的根本理由) 预算管「能不能花」,progress 管「值不值得继续查」。让预算充当正常停止策略,会把「当前证据不足」错误表达成「系统执行失败」——这是语义错误,不是资源问题。 | 机制 | 问的问题 | 归属 | |---|---|---| | RunBudget | 这次 Run 最多允许消耗多少? | core 域 | | Progress | 继续查询是否仍可能推进当前诊断? | progress 域 | ### 1.2 三方判断权(信任边界) ```mermaid flowchart LR T["Tool / Projector
客观结果"] -->|"evidence_status:空不空(代码判)"| H M["Diagnosis Agent
语义价值"] -->|"information_gain:有没有用(模型判)"| H H["Harness
最终停止权"] -->|"scope 重复 / 连续 NO_GAIN / 协议合规"| R["停止裁决"] ``` - **谁判空**:`EVIDENCE_FOUND / NO_EVIDENCE` 是 Harness 代码判(证据数组空不空),Projector 投影时客观计算,不需要模型。 - **谁判价值**:`GAINED / NO_GAIN` 的语义价值是模型判——非空结果「有没有用」只有结合诊断上下文才能判断。 - **谁决定停止**:Harness。模型可以主动结束(输出 Draft),但不能用继续发 Tool Call 绕过 Harness 已定的饱和状态。 **两层信任边界分开**:模型输出乱来(瞎报增益)时,Harness 手里仍有 evidenceStatus 这个与模型无关的事实层(canonical 记录、审计、验真都建立在它之上)。 ## 2. 类地图与调用链 ### 2.1 14 个文件分类 | 类别 | 类 | 作用 | |---|---|---| | 状态机核心 | `DiagnosisProgressTracker` | 记账(双计数/pending/identity)+ 停止裁决 | | 判重 | `ToolScopeNormalizer` + `ToolScopeIdentity` | 业务输入 → 稳定 scope 指纹 | | 投影 | `DiagnosisProgressProjector` + `DiagnosisProgressProjection` | identity → 回读 canonical → 有界快照 | | 枚举 | `InformationGain` / `DiagnosisCollectionState` / `DiagnosisStopReason` / `ProgressProtocolViolationType` | GAINED·NO_GAIN / COLLECTING·SATURATED / 三种停止原因 / 五类违规 | | record | `PreviousObservation` / `CompletedToolCall` / `DiagnosisProgressSnapshot` / `DiagnosisProgressSnapshotState` | 协议字段 / 完成 identity / 对外快照 / 内部快照 | | 异常 | `ProgressProtocolViolationException` | 受控协议违规 | ### 2.2 一次 Tool Call 的完整链路 ```mermaid sequenceDiagram participant I as HarnessToolInterceptor(per-Run) participant ET as HarnessEvidenceTools(注册表) participant AD as Adapter(接线员) participant TB as ToolBoundary(门禁) participant S as CanonicalInvocationStore(真相) participant P as Projector(投影) I->>ET: invoke(context, toolName, toolCallId, args) ET->>AD: bridge 包装的 invoker AD->>TB: boundary.execute(context, envelope, executor, projector) TB->>S: begin(PROJECTING) TB->>TB: executor.execute(requestJson)(backend raw) TB->>P: projector.project(raw) → agent_result + evidenceStatus TB->>S: markReady(READY) 或 markError(ERROR) TB-->>AD: ToolBoundaryResult(READY/ERROR) AD-->>I: 一路 return I->>I: 双源交叉验证 → recordCompleted → 返回有界 observation ``` ### 2.3 三层关系 **持有关系**: ``` HarnessToolInterceptor ──持有──▶ RunContext(含 Tracker)/ HarnessEvidenceTools HarnessEvidenceTools ──持有──▶ Map(bridge 注册表) Adapter ──持有──▶ ToolBoundary + 具体 backend + 具体 Projector ToolBoundary ──持有──▶ DiagnosisHarnessCore / CanonicalInvocationStore / ToolCallKeyFactory ``` **接口-实现关系**: | 接口 | 实现 | |---|---| | `EvidenceToolInvoker`(@FunctionalInterface) | `HarnessEvidenceTools.fromAdapters` 的 bridge | | `AdapterCall`(内部 @FunctionalInterface) | 三个 Adapter 的 execute 方法 | | `ToolExecutor`(@FunctionalInterface) | Adapter 里 `ignored -> legacyExecutor.execute(query)` | | `ToolResultProjector` | `RagResultProjector` / `QueryLogsResultProjector` / `MysqlResultProjector` | | `CanonicalInvocationStore` | `JpaCanonicalInvocationStore` 等 | ## 3. 生命周期:单例 vs per-Run ```mermaid flowchart TD subgraph 应用启动(一次) A["Spring 容器装配单例 bean"] A --> B["DiagnosisHarnessCore / ToolBoundary / Adapter / HarnessEvidenceTools / DiagnosisAgentFactory"] end subgraph 每次请求(多次) C["core.startRun() → RunContext(含 new DiagnosisProgressTracker)"] C --> D["DiagnosisAgentFactory.create(context)"] D --> E["new HarnessModelInterceptor / HarnessToolInterceptor(绑定本 Run context)"] E --> F["ReactAgent.call() → ReAct loop"] F --> G["Run 结束:拦截器/ReactAgent 变成垃圾"] end ``` | 对象 | 生命周期 | 原因 | |---|---|---| | core / ToolBoundary / Adapter / EvidenceTools / AgentFactory | 单例 | 无状态,只存依赖与规则 | | RunContext | per-Run | 状态容器(Tracker/Budget/Lifecycle) | | 两个 Interceptor | per-Run | 持有本 Run 的 RunContext | | ReactAgent | per-Run | 框架有状态对象(loop/memory) | **无状态体现**:单例 bean 的字段全是构造注入的依赖/配置(创建后不变);可变状态全部外置到 RunContext 和持久化存储。方法一律以 `context` 参数显式传入——同一个 ToolBoundary 实例可并发服务多个 Run,各算各的账,**状态外置是并发正确性的硬要求**(若在 bean 里存「当前预算计数」,并发 Run 会互相覆盖)。 ## 4. 拦截器五道门(代码核心) ### 4.1 门卫 + 执行者合一 证据工具的 `ToolCallback` 被**故意定义为直接抛异常**(`"Harness evidence Tools require the framework Tool interceptor"`)——如果框架绕过拦截器执行 `handler.call()`,直接爆炸。这从结构上保证:**执行不可绕过门禁,校验+执行是原子操作**。 ```mermaid flowchart LR REQ["Tool Call 到达"] --> CHK{"evidenceTools.supports?"} CHK -->|"非证据工具"| HANDLER["handler.call() 透传"] CHK -->|"证据工具"| GATES["五道门(校验+执行+记账)"] ``` **为什么不在 definition 里写**(四个原因): 1. 拦截器接管执行时**根本不经过 ToolCallback**——写在 definition 里永远不会执行; 2. ToolCallback 是纯函数(input→output),**拿不到 RunContext**,调不了 `context.progress()`; 3. ToolCallback 本身就是执行,**没有「执行前」时机**——判重必须在 backend 前,只有拦截器同时拥有执行前/后两个观察点; 4. progress 逻辑横跨所有证据工具,写在每工具 callback 会**复制漂移**。 ### 4.2 五道门流程 ```mermaid flowchart TD A["① 已停止检查"] -->|"SATURATED && stopInstructionDelivered"| B["抛 DiagnosisCollectionStoppedException(硬停止)"] A --> C["② 解析 + 协议校验"] C -->|"违规"| D["recordProgressProtocolViolation → 可修复反馈 / 达阈值饱和"] C -->|"评价导致饱和"| E["stopRequired(软停止)"] C --> F["③ 判重:isDuplicate(toolName, normalizedScope)"] F -->|"重复"| G["recordDuplicateScope(直接 NO_GAIN)→ 饱和? stopRequired : 返回 DUPLICATE_SCOPE 观察"] F --> H["④ ToolBoundary 执行(预算/canonical/审计/checkActive)"] H -->|"READY"| I["双源交叉验证 → recordCompleted → ⑤ 收尾(NO_EVIDENCE 立即 NO_GAIN / FOUND 挂 pending / 饱和交付 stop_required)"] H -->|"非 READY"| J["error observation(BUDGET_EXHAUSTED 额外 markBudgetLimitReached)"] ``` ### 4.3 协议三类违规(第二道门) 校验的是 **Envelope 与 Tracker pending 状态的协议关系**(不是 JSON 结构——JSON 结构在 parse 用严格反序列化做了): | 违规 | 含义 | 判定条件 | |---|---|---| | `UNEXPECTED_PREVIOUS_OBSERVATION` | 没欠账却带评价 | pending == null 且 observation != null | | `MISSING_PREVIOUS_OBSERVATION` | 欠账不还 | pending != null 且 observation == null | | `OUT_OF_ORDER_PREVIOUS_OBSERVATION` | 还错账 | observation.toolCallId() != pendingToolCallId | 另两类(parse 阶段):`MISSING_INPUT`(缺业务输入)、`INVALID_ENVELOPE`(JSON/字段非法)。 **可修复反馈**:首次违规(未达阈值)返回 `repair_required:true` 观察——带 `violation_type` / `missing_field` / `expected_previous_tool_call_id` / `allowed_information_gain` / `instruction`,让模型下一轮自愈;**连续**违规达独立阈值才饱和(PROGRESS_PROTOCOL_VIOLATED)。 ### 4.4 双源交叉验证(第四道门,READY 后) ```text 源1:result.evidenceStatus() ← Projector 投影时计算并携带的声明值 源2:controlView.evidenceStatus() ← 从 agent_result 内容里解析 "evidence_status" 字段 比较:一致 ? 通过 : OBSERVATION_CONTRACT_MISMATCH 拒绝 ``` 本质是「自洽性防线」:同一状态被两处描述(结果对象字段 vs 内容 JSON 字段),必须一致——防止投影 bug / 数据损坏 / 构造不一致导致 progress 基于错误状态决策(如声明 FOUND 但内容空 → 挂 pending 等评价不存在的证据;声明 NO_EVIDENCE 但内容有证据 → 误记 NO_GAIN)。 ### 4.5 三副 observation 面孔 | 面孔 | 触发 | 关键字段 | |---|---|---| | 正常执行结果 | READY 且未饱和 | 有界观察 + 可选 `stop_required` | | `STOP_REQUIRED` | 饱和后交付一次 | `stop_required:true` + `reason` | | 可修复协议错误 | 协议违规未达阈值 | `repair_required:true` + violation_type/missing_field/expected id/instruction | ## 5. Tracker 状态机 ### 5.1 11 个字段 | 字段 | 含义 | |---|---| | `stopAfterConsecutiveNoGain` | 连续 NO_GAIN 阈值(默认 2,yml 可配) | | `stopAfterConsecutiveProgressProtocolViolations` | 连续协议违规阈值(默认 2) | | `completedScopes` | `toolName + normalizedScope` 去重集合 | | `completedToolCalls` | 已完成调用 identity 列表(无 payload) | | `consecutiveNoGain` | 连续无增益计数(GAINED 清零) | | `consecutiveProgressProtocolViolations` | 连续协议违规计数(合法评价清零) | | `collectionState` | `COLLECTING` / `SATURATED` | | `stopReason` | `INFORMATION_SATURATED` / `BUDGET_LIMIT_REACHED` / `PROGRESS_PROTOCOL_VIOLATED` | | `pendingToolCallId` | 待评价调用 ID(同一时刻最多一个) | | `stopInstructionDelivered` | STOP_REQUIRED 是否已交付(只一次) | ### 5.2 两条独立计数(重点:协议违规 ≠ NO_GAIN) ```text NO_GAIN 路径(applyGain)—— backend 执行了但没增益: 入口:applyPreviousObservation(NO_GAIN) / recordDuplicateScope() / recordCompleted(NO_EVIDENCE) 阈值:stopAfterConsecutiveNoGain(2) → SATURATED + INFORMATION_SATURATED GAINED 清零连续计数 协议违规路径(recordProgressProtocolViolation)—— backend 根本没执行: 入口:拦截器 catch 分支 阈值:stopAfterConsecutiveProgressProtocolViolations(2) → SATURATED + PROGRESS_PROTOCOL_VIOLATED 一次合法评价(applyPreviousObservation 通过)清零 ``` **为什么分开**:`NO_GAIN` 表示「Tool 执行了但没推进诊断」;协议错误表示「模型没守契约,Tool 根本没执行」。混淆会让真实空转无法归因(真实 E2E:9 次协议拒绝 + 13 轮模型调用空转)。 ### 5.3 pending 协议(锚点) ```mermaid flowchart LR A["recordCompleted(EVIDENCE_FOUND)"] --> B["pendingToolCallId = toolCallId(挂账)"] B --> C["模型下一轮回带 previous_observation"] C --> D{"ID == pending ?"} D -->|"是"| E["清 pending → applyGain"] D -->|"否"| F["OUT_OF_ORDER 违规"] ``` - 只对**非空成功**结果设 pending;NO_EVIDENCE 不设(Harness 已自己判 NO_GAIN)。 - pending 是「唯一欠账」——保证协议逐轮、不乱序、不重评。 - 模型读完 observation 直接输出 Draft → pending 不消费也合法(不需要评价最后一轮)。 ### 5.4 收集状态机 ```mermaid stateDiagram-v2 [*] --> COLLECTING COLLECTING --> COLLECTING: GAINED / consecutiveNoGain=0 COLLECTING --> COLLECTING: NO_GAIN 未达阈值 COLLECTING --> SATURATED: NO_GAIN 达阈值 / 协议违规达阈值 SATURATED --> SATURATED: claimStopInstruction 交付一次(软停止) SATURATED --> [*]: 模型再请求 Tool → DiagnosisCollectionStoppedException(硬停止) ``` **软/硬停止两段式**(易错点:stopReason 设置 ≠ 软停止): ```text 饱和瞬间 → stopReason 已设置(状态事实) 软停止 = 第一次交付 stop_required 观察(claimStopInstruction 返回 true,流程不中断) → 给模型合法输出 Draft 的机会 硬停止 = 模型无视指令再次请求 Tool → 入口检查 SATURATED && delivered → 抛 DiagnosisCollectionStoppedException 穿出框架 loop ``` ### 5.5 为什么需要 Tracker(三层) 1. **progress 本身需要**:预算只止损,不能当正常停止策略; 2. **必须单一所有者**:「连续无增益」是跨轮次、跨入口(模型评价/代码判空/重复检测)的全局判断,分散记账会状态漂移; 3. **最小状态**:不存 payload(真相在 Canonical Store),避免第二份真相。 ## 6. canonical 生命周期(tool 域衔接) ### 6.1 状态机 ```mermaid stateDiagram-v2 [不存在] --> PROJECTING: store.begin(preflight+预算通过后) PROJECTING --> READY: store.markReady(backend 成功+投影成功) PROJECTING --> ERROR: store.markError(任何异常) note right of PROJECTING: 仅身份+request+startedAt note right of READY: raw + agent_result + evidence + completedAt note right of ERROR: raw + error_code + completedAt(禁 agent_result) ``` ### 6.2 为什么分 begin/markReady/markError 三段 | 动机 | 说明 | |---|---| | 崩溃恢复 | backend 执行可能耗时数秒,一次写入会丢「已开始」的事实;begin 先留痕(PROJECTING = WAL) | | 审计耗时 | 需要 startedAt(begin 记)和 completedAt(迁移记)算调用耗时 | | 状态合法 | 每种状态只允许合法字段组合,由构造校验兜底 | | 防篡改 | record 不可变,迁移生成新实例——READY 一旦写入不可改(证据可验真的前提) | | 幂等 | begin 抛 `DuplicateInvocationException`(同 key 重复 begin 拒绝)→ `DUPLICATE_TOOL_CALL` | **两类失败不落库**:requestBytes 超限、preflight 非法、重复 begin——都发生在 begin 之前,canonical 里**无记录**。 ### 6.3 Store vs Tracker 分层 ```text Store(事实层):request / raw_response / agent_result / 状态 / 时间戳 —— 完整真相 Tracker(判断层):identity(引用)+ 双计数 + pending + 状态 —— 决策状态 NO_GAIN 计数不是「第二份真相」:它无法从 Store 重建 (模型评价是瞬时的、不落 Store),是独立增量状态 ``` ## 7. ToolBoundary 执行门禁 ### 7.1 executeCanonical 五阶段 ```mermaid flowchart TD A["阶段一:preflight + Tool 预算 + request bytes 预留 → begin(PROJECTING)"] B["阶段二:executor.execute(requestJson)(backend raw)"] C["阶段三:raw 大小校验 + Run bytes 预留"] D["阶段四:projector.project(raw) → 有界 agent_result + evidenceStatus"] E["阶段五:agent_result 校验 + Run bytes 预留 → markReady(READY)"] A --> B --> C --> D --> E ``` **三笔 bytes 预留**:request(阶段一)/ raw(阶段三)/ agent_result(阶段五)——Run 的 bytes 预算分三个时间点消耗,任何一笔超限触发对应错误码。 ### 7.2 失败路径错误码 | 失败点 | 是否已 begin | canonical | 错误码 | |---|---|---|---| | requestBytes 超限 / preflight 非法 / 重复 begin / Run 终态 | 否 | 无记录 | `RESULT_TOO_LARGE` / 分类错误码 / `DUPLICATE_TOOL_CALL` / `BUDGET_EXHAUSTED`·`RUN_INACTIVE` | | executor 抛错 | 是 | ERROR | `TOOL_EXECUTION_ERROR` | | raw 过大 / 预算 / 取消 | 是 | ERROR | `RESULT_TOO_LARGE` / `BUDGET_EXHAUSTED` / `RUN_INACTIVE` | | 投影失败 | 是 | ERROR | `PROJECTION_ERROR` | | agent_result 过大 / store 失败 | 是 | ERROR | `RESULT_TOO_LARGE` / `PROJECTION_ERROR` / `STORE_ERROR` | ### 7.3 checkActive 在哪 progress 拦截器流程里看不到显式 checkActive——它在 **ToolBoundary/core 层**隐式执行:`core.beforeToolCall` / `core.reserveRunBytes` 内部若 Run 已终态,抛 `RunAbortedException` → 映射为 `RUN_INACTIVE` 错误码。 ## 8. 投影与发布闭环 ### 8.1 停止后的数据流 ```mermaid flowchart LR T["Tracker(identity 列表)"] --> P["DiagnosisProgressProjector"] P --> S["CanonicalInvocationStore(回读 READY)"] S --> P P --> SN["ProgressSnapshot(verifiedSources + observedFacts + limitations + stopReason)"] SN --> R["DiagnosisReleaseUseCase"] R --> O["SUCCESS / FALLBACK(INSUFFICIENT_EVIDENCE 等)"] ``` ### 8.2 Projector 有界投影 - 三重校验(`isReferencableBy` / toolCallId / toolName)通过才发布;无法验真/不可读/格式非法 → limitation,**绝不输出 raw**; - 空结果也投影为有界事实(「该范围内未发现」)——限定范围的空查询是「已检查」的证明; - 硬截断:`MAX_FACTS=12`、summary/scope 320 字符、source 160 字符; - 去重:`sourceKey = type + source + scope`,`factKey = sourceKey + summary`(LinkedHashMap 保序)。 ### 8.3 Release 决策树 ```mermaid flowchart TD EX["execute(execution)"] --> D1{"draft == null?"} D1 -->|"是"| CS["releaseControlledStop
需 hasObservedFacts → INSUFFICIENT_EVIDENCE"] D1 -->|"否"| D2{"conclusion == null?"} D2 -->|"是"| NC["releaseNoConclusion
有事实 → INSUFFICIENT_EVIDENCE
missing_info → MISSING_REQUIRED_CONTEXT
都没有 → fail closed"] D2 -->|"否"| C["releaseConclusion
EvidenceGuard 验引用 → repair → SemanticGuard 裁决
SUPPORTED ? SUCCESS : FALLBACK"] ``` ## 9. 易错点清单 | 易错 | 正确 | |---|---| | EVIDENCE_FOUND 累计 NO_GAIN | **NO_EVIDENCE**(空结果)才立即累计;FOUND 挂 pending 等模型评价 | | 重复调用计入协议违规 | 重复走 **NO_GAIN** 路径(recordDuplicateScope),不是违规 | | 软停止 = 设置 stopReason | stopReason 饱和时就设了;软停止是**交付 stop_required 观察** | | checkActive 应该在拦截器 | 在 ToolBoundary/core 层,`RUN_INACTIVE` 错误码 | | 拦截器直接 invoke = 绕过设计 | 是**唯一执行通道**(证据工具 handler 必炸) | | 数据都在 Tracker | 判断层在 Tracker,**事实层在 Canonical Store** | | 判重对比裸入参 | 对比的是**规范化指纹**(字段顺序/格式/缺省值统一后) | | 首次协议违规直接终止 | 首次返回**可修复反馈**,连续违规才饱和 | ## 10. 面试话术合集(30 秒) ### 10.1 双停止机制 > "Harness 有两套停止机制。预算是资源门禁,管『能不能花』;Progress 是收敛门禁,管『继续查有没有价值』。核心设计是:Tool 只提供客观结果,模型负责判断语义增益,但停止权归 Harness。信息增益故意只保留 GAINED/NO_GAIN 两值,靠模型在下次 Tool Call 里用 previous_observation 回传评价;连续两次 NO_GAIN 就进入饱和,交付一次 STOP_REQUIRED 给模型合法收尾的机会,再纠缠就抛受控异常穿出框架。" ### 10.2 为什么需要 Tracker(单一所有者) > "ProgressTracker 是 Run 内收敛控制的单一决策者:它不存证据内容,只保存最小账——已完成调用的 identity、连续 NO_GAIN 和协议违规两条计数、一个待评价的 pending ID、以及 COLLECTING/SATURATED 状态。三路输入(模型评价、代码判空、重复检测)汇入计数,GAINED 清零、NO_GAIN 累加,达阈值进入饱和,交付一次停止指令。所有方法 synchronized,是并发安全的单一所有者。" ### 10.3 拦截器为什么自己执行(必炸路径) > "Harness 的证据工具不是『拦截后放行』——拦截器对它们既是门卫又是执行者。证据工具的 ToolCallback 被故意定义为直接抛异常,使 handler 路径成为必炸路径,从结构上保证执行不可绕过门禁;校验(判重/协议/预算)和执行(ToolBoundary/canonical)内联在同一个流程里,保证『执行前判重、执行后记录』的时机原子性,且模型拿到的永远是有界观察而不是 raw。" ### 10.4 为什么 begin/markReady/markError 三段式 > "Tool 执行是跨门禁、backend IO、校验、投影多个不可靠阶段的流程,所以 canonical 记录用 begin → markReady/markError 的追加式状态机:begin 先落 PROJECTING 保证已开始的调用必有痕迹,backend 成功且投影成功才 markReady 定案为 READY,任何异常 markError 收尾;三个状态各校验合法字段组合,record 不可变保证一旦写入不可篡改——这样崩溃可恢复、审计可对账(startedAt/completedAt 算耗时)、证据可验真(READY 不可造假),重复 begin 还能幂等拦截。" ### 10.5 谁判空谁判价值 > "空不空是客观事实,代码可判,所以 evidenceStatus 归 Harness 的 Projector;有没有用是语义判断,代码判不了(一段通用知识可能看似相关实则无用),所以 information_gain 归模型。Harness 绝不把客观状态交给模型声明,模型也绝不替 Harness 做停止裁决——两层的信任边界是分开的。" ### 10.6 无状态 bean > "Harness 的单例 bean(core/ToolBoundary/Adapter)全部无状态:字段只有构造注入的依赖和配置,创建后不变;所有可变状态外置到每个 Run 的 RunContext(预算计数、进度 Tracker、生命周期)和持久化存储里。方法一律以 context 参数显式传入,所以同一个 bean 实例能并发服务多个 Run 而不串账——状态在参数里,不在实例里。" ## 11. 代码位置索引 | 类 | 文件 | |---|---| | `DiagnosisProgressTracker` | `src/main/java/com/superbiz/agent/harness/progress/DiagnosisProgressTracker.java` | | `ToolScopeNormalizer` | `src/main/java/com/superbiz/agent/harness/progress/ToolScopeNormalizer.java` | | `DiagnosisProgressProjector` | `src/main/java/com/superbiz/agent/harness/progress/DiagnosisProgressProjector.java` | | `DiagnosisProgressProjection` | `src/main/java/com/superbiz/agent/harness/progress/DiagnosisProgressProjection.java` | | progress 枚举/record/异常 | `src/main/java/com/superbiz/agent/harness/progress/`(其余 9 个文件) | | `HarnessToolInterceptor` | `src/main/java/com/superbiz/agent/harness/agent/HarnessToolInterceptor.java` | | `HarnessEvidenceTools` | `src/main/java/com/superbiz/agent/harness/agent/HarnessEvidenceTools.java` | | `DiagnosisAgentFactory` | `src/main/java/com/superbiz/agent/harness/agent/DiagnosisAgentFactory.java` | | `RagToolAdapter` | `src/main/java/com/superbiz/agent/harness/tool/adapter/RagToolAdapter.java` | | `ToolBoundary` | `src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundary.java` | | `ToolBoundaryResult` | `src/main/java/com/superbiz/agent/harness/tool/boundary/ToolBoundaryResult.java` | | `CanonicalToolInvocation` | `src/main/java/com/superbiz/agent/harness/tool/store/CanonicalToolInvocation.java` | | `CanonicalInvocationStore` | `src/main/java/com/superbiz/agent/harness/tool/store/CanonicalInvocationStore.java` | | `DiagnosisReleaseUseCase` | `src/main/java/com/superbiz/agent/harness/release/DiagnosisReleaseUseCase.java` | | 装配 | `src/main/java/com/superbiz/agent/config/HarnessChatConfiguration.java` |