docs(mvp): add harness design and progressive guides

This commit is contained in:
zhuyongxin
2026-07-29 19:04:44 +08:00
parent 584639fa2a
commit 3a7eee8af4
15 changed files with 3550 additions and 0 deletions
+378
View File
@@ -0,0 +1,378 @@
# Harness Context:统一术语与命名边界
**更新日期**:2026-07-29
**状态**:当前实现口径
**适用范围**:`com.superbiz.agent.harness`、Chat SSE、Diagnosis Run 持久化与相关工程文档
> 本文是术语词典,不建议第一次接触 Harness 时顺序阅读。入门请从 [README.md](README.md) 开始,遇到名词歧义时再回到本文查询。
## 1. 为什么需要这份 Context
当前系统同时存在 Run 状态、发布结果、Tool 状态、证据状态、收集状态、停止原因和 Fallback 原因。它们都使用了 `SUCCESS`、`ERROR`、`FAILED`、`READY` 等相近词汇,但回答的是不同问题。
如果把这些词排成一条“大状态机”,会产生错误理解,例如:
- `NO_EVIDENCE` 被理解为 Tool 调用失败;
- `FALLBACK` 被理解为 Run 执行失败;
- `SATURATED` 被理解为预算耗尽;
- `READY` 被理解为证据足以支持根因;
- 数据库 `status=SUCCESS` 被理解为已经找到根因。
本文件是 Harness 工程文档的术语入口。阅读其他文章前,先以这里的定义区分身份、数据、状态和组件责任。代码与现行架构文档仍是最终事实来源;本文件不创建新的运行协议。
## 2. 一句话定义 Harness
**Harness** 是包围非确定性模型执行的确定性控制边界:Agent 负责业务推理和 Draft,Harness 负责 Run 身份、生命周期、预算、取消、Tool 门禁、证据验真、停止控制、发布和审计。
Harness 不是:
- 业务工作流引擎;
- Planner / Executor / Verifier / Composer 编排图;
- 框架 ReAct loop 的第二份实现;
- 判断业务根因的规则引擎;
- 用于存放所有 Agent 相关代码的泛化名称。
## 3. 三层范围:不要把 Harness、Core 和 Application 当成同义词
| 名称 | 定义 | 包含 | 不包含 |
|---|---|---|---|
| Chat Application | 一次 Chat 请求的应用用例 | Session/Run 创建、路由、分支执行、持久化、公开结果 | HTTP/SSE 连接本身、业务推理细节 |
| Diagnosis Harness | Diagnosis Agent 外部的确定性控制系统 | Core、Interceptor、ToolBoundary、Progress、Guard、Release、Audit | 根因推理和 ReAct 规划 |
| Harness Core | 最小运行控制内核 | RunContext、deadline、budget、cancel、lifecycle、retry policy | 路由、Tool backend、Guard、持久化、SSE |
代码包 `com.superbiz.agent.harness` 同时包含 Chat Application 和 Diagnosis Harness 的实现,这是代码组织范围,不代表所有类都属于 Harness Core。
`DiagnosisHarnessCore` 目前也被 System Chat、Knowledge Query 和 Router 复用预算与生命周期能力。类名前缀保留了演进历史,概念上应理解为当前 Chat Run 的 Harness Core。
## 4. 身份术语
```mermaid
flowchart TB
S["Chat Session<br/>sessionId,多轮容器"] -->|"1:N"| R1["Run<br/>runId,一次请求"]
S -->|"1:N"| R2["Run<br/>下一次请求"]
R1 -->|"1:N"| AS["Agent Step<br/>一次模型步骤"]
R1 -->|"1:N"| TC["Tool Call / Invocation<br/>framework tool_call_id"]
R1 -->|"1:N"| TE["Trace Event<br/>sequence_no"]
TC --> CI["Canonical Invocation<br/>Run 内短期 Tool 真相"]
AS --> TR["Diagnosis Trace<br/>按 exact Run 聚合"]
TC --> TR
TE --> TR
R1 -.->|"SUCCESS diagnosis only"| PT["PublishedResult<br/>可投影为下一轮 PreviousTurn"]
```
图中的所有明细都必须绑定 exact runId。Session 只负责组织多轮,不能替代 Run 归属;Trace 是聚合视图,不能反过来成为执行身份。
### 4.1 Chat Session
一次多轮对话容器,由 `sessionId` 标识。同一个 Session 可以包含多次 Run。
Session 用于:
- 组织多轮请求;
- 查找安全的 PreviousTurn;
- 查询历史 Run。
Session 不代表一次执行,不拥有 Tool Call 或模型步骤的终态。
### 4.2 Run
一次独立的 Chat Application 执行,由 `runId` 标识。每次请求创建新 Run,无论 intent 是 System Chat、Knowledge Query 还是 Diagnosis。
代码和表名中仍大量使用 `DiagnosisRun` / `diagnosis_run`,但当前 Chat Application 会为三种 intent 都创建该记录。因此文档中优先使用 **Run**;只有引用 Java 实体或数据库表时才写 `DiagnosisRun`。
### 4.3 Agent Step
Diagnosis ReAct Agent 的一次模型步骤。多个 Agent Step 属于一个 Run,用于记录每轮模型调用的有界元数据和 Token。
Agent Step 不是 Run,也不是 retry attempt。ReAct 的下一轮模型调用是业务循环;retry 是同一技术操作的再次 attempt。
### 4.4 Tool Call / Tool Invocation
- **Tool Call**:模型通过框架产生的调用请求,由 framework `tool_call_id` 标识。
- **Tool Invocation**:该请求进入 ToolBoundary 后的一次实际执行记录。
- **Tool Request Rejection**:在 progress、重复或停止门禁处被拒绝,backend 没有执行,不算 Tool Invocation。
Harness 不生成第二套 Tool Call ID。
### 4.5 Trace
按 exact `sessionId + runId` 聚合的可回放观察视图,包含 Run、Agent Step、Tool metadata 和统一 Timeline。
Trace 是观察结果,不是新的执行上下文或状态所有者。`TraceEventStatus` 只描述单个事件,不能替代 RunState 或 ReleaseOutcome。
## 5. 核心执行术语
### 5.1 RunContext
一次 Run 的显式执行上下文。结构不可变地携带:
```text
sessionId
runId
deadline
RunCancellation
RunBudget
ModelCallLedger
HarnessRetryPolicies
RunLifecycle
DiagnosisProgressTracker
```
“结构不可变”表示 record 字段引用不变化;预算、取消、生命周期和进展通过各自线程安全句柄在 Run 内变化。
### 5.2 Run Lifecycle
Run 的内存执行终态,由 `RunLifecycle` 所有,状态类型是 `RunState`。它采用 first-terminal-wins,后到达的成功、失败或取消不能覆盖第一个终态。
### 5.3 Run Cancellation
请求停止 Run 的协作机制,由 `RunCancellationReason` 记录第一个原因。取消会阻止后续边界和迟到发布,但不承诺一定能立即物理中断已发送给 Provider 的同步请求。
### 5.4 Run Budget
单 Run 的资源账本和门禁,包括模型调用、Tool 调用、单 Tool 次数、输入/输出/总 Token 和 Run bytes。
Budget 只回答“还能不能消耗资源”,不回答“继续诊断是否有价值”。后者属于 Information Gain 和 Collection State。
### 5.5 Retry Attempt
同一个技术操作因允许的技术失败而再次执行。当前 Router 和 SemanticGuard 最多 2 次 attempt,Diagnosis Agent、Tool 和 EvidenceRepair 只有 1 次。
以下不是 retry:
- ReAct Agent 的下一轮思考;
- 改用另一个 Tool;
- `NO_EVIDENCE` 后继续查询;
- 用户发起下一次 Run。
## 6. Agent 与输出术语
### 6.1 Diagnosis Agent
唯一拥有业务 ReAct Tool loop 的 Agent,负责提出假设、选择 Tool、评价非空结果的信息增益并生成 `DiagnosisDraft`。
它不负责 Run 生命周期、Tool 授权、证据物理验真、SemanticGuard 或最终发布。
### 6.2 DiagnosisDraft
Diagnosis Agent 的结构化草稿,是发布链输入,不是已经发布的报告。Draft 可以有结论,也可以 `conclusion=null`。
Draft 中的 Tool 引用和结论必须经过 Release Pipeline 后才能成为公开内容。
### 6.3 Agent Result
当前 Tool 代码中的 `agent_result` 指 Tool-specific Projector 生成、存入 canonical invocation 的标准化有界结果。
它不是:
- Diagnosis Agent 的最终 Draft;
- Chat Application 的最终结果;
- 直接进入模型上下文的完整内容。
更准确的理解是 **Canonical Projected Tool Result**。当前字段名因协议兼容保留。
### 6.4 Model Observation
从 canonical `agent_result` 再次白名单投影后,真正作为 Tool Response 进入 Diagnosis Agent 上下文的内容。
预算、阈值、重复指纹、raw response 和完整 Harness 控制状态不进入 Model Observation。
### 6.5 PreviousTurn 与 PublishedResult
- `PublishedResult`:只有 `DIAGNOSIS + ReleaseOutcome.SUCCESS` 才能持久化的安全诊断结果。
- `PreviousTurn`:从 PublishedResult 生成的有界下一轮上下文。
Fallback、失败、取消、raw evidence 和完整历史不会进入 PreviousTurn。
`PublishedResult` 不等于当前请求直接返回的 `ChatApplicationResult`。
## 7. Tool 与证据术语
### 7.1 ToolBoundary
所有业务 Tool 的统一执行边界,负责 Run/ID/授权/只读校验、预算、bytes、canonical 状态迁移和 metadata audit。
ToolBoundary 不判断信息增益或业务根因。
### 7.2 Canonical Invocation
Redis TTL 内的完整 Tool 调用真相,包含 request、raw response、标准化 `agent_result`、调用状态和证据状态。
它用于当前 Run 的 EvidenceGuard 和 ProgressSnapshot,不是长期审计记录。
### 7.3 Durable Audit
长期保存的有界元数据:Run/Tool identity、状态、耗时、bytes、模型步骤和 Token。它不保存 Prompt、完整 Tool 参数、raw response 或 canonical record。
### 7.4 Evidence
Tool 在特定 scope 下返回、经过 Projector 标准化并能由当前 Run canonical record 验真的事实或负向观察。
`EVIDENCE_FOUND` 只代表存在候选内容,不代表它支持根因。
### 7.5 Negative Observation
`READY + NO_EVIDENCE` 形成的限定范围事实,例如“在时间窗 T、服务 S、查询 Q 下没有匹配日志”。
它不能被解释为“故障不存在”或“系统健康”。Draft 中使用 `AnalysisKind.NEGATIVE_OBSERVATION` 表达这种分析。
### 7.6 VerifiedEvidenceSnapshot
EvidenceGuard 从 canonical invocation 中投影出的已验真、最小证据集合,供 SemanticGuard 使用。它不包含 raw response。
### 7.7 ProgressSnapshot
Tool loop 结束后,根据已完成 Tool Call identity 回读 canonical records 生成的有界过程视图,用于受控停止或无结论 Fallback。
VerifiedEvidenceSnapshot 面向“有结论报告的语义审查”;ProgressSnapshot 面向“没有可发布结论时说明已经检查了什么”,两者用途不同。
## 8. Guard 与发布术语
### 8.1 EvidenceGuard
确定性引用验真器。检查 Draft 结构、analysis 引用闭包、当前 Run 所有权、READY 状态、EvidenceStatus 和 typed projection 自洽性。
不调用模型,不判断结论是否被证据支持。
### 8.2 EvidenceRepair
一次性的模型修复步骤,只修结构和引用。修复前后 `SemanticDraftView` 必须保持用户可见语义一致,之后重新执行 EvidenceGuard。
它不是新的报告作者,也不是 retry Agent。
### 8.3 SemanticGuard
隔离的单轮语义审查器,只判断 verified evidence 是否支持 Draft,输出 `SUPPORTED / UNSUPPORTED`。
它使用模型,但无 Tool、无记忆、无 ReAct loop、无报告改写权,因此文档中不要称它为第二个业务 Agent。
### 8.4 Release Pipeline
从 DiagnosisDraft 或受控停止输入,到 `DiagnosisReleaseResult` 的安全决策链:EvidenceGuard、可选 Repair/Recheck、SemanticGuard 和 SafeFallback。
### 8.5 ReleaseOutcome
应用层最终处理结果:
- `SUCCESS`:发布安全正常内容;
- `FALLBACK`:请求已被安全处理,但没有发布正常诊断结论;
- `FAILED`:无法形成安全业务结果;
- `CANCELLED`:Run 被取消。
ReleaseOutcome 不等于 RunState。尤其 `FALLBACK` 不是 RunState。
### 8.6 SafeFallback 与 FallbackType
SafeFallback 是确定性公开内容;FallbackType 解释为什么没有发布正常诊断结论,例如:
- `EVIDENCE_VALIDATION_FAILED`;
- `SEMANTIC_UNSUPPORTED`;
- `SEMANTIC_UNAVAILABLE`;
- `INSUFFICIENT_EVIDENCE`;
- `MISSING_REQUIRED_CONTEXT`。
FallbackType 是 `ReleaseOutcome.FALLBACK` 的原因,不是新的生命周期状态。
## 9. 状态维度速查
| 类型 | 所有者 | 回答的问题 | 不能回答的问题 |
|---|---|---|---|
| `RunState` | RunLifecycle | Run 的内存执行是否终止、如何终止 | 发布了正常内容还是 Fallback |
| `RunCancellationReason` | RunCancellation | 谁首先请求取消、为什么 | 最终公开结果是什么 |
| `ChatApplicationStatus` | Application Observer | 当前向用户展示哪个处理阶段 | Run 是否已经终止 |
| `InvocationStatus` | Canonical Invocation | Tool 调用记录是否完成 | 是否找到候选证据 |
| `EvidenceStatus` | Tool Projector | 当前 scope 是否有候选证据 | 是否支持根因 |
| `InformationGain` | Harness/Diagnosis Agent | 结果是否推进当前诊断 | Tool 是否技术成功 |
| `DiagnosisCollectionState` | ProgressTracker | 是否允许继续调用证据 Tool | Run 是否终止 |
| `DiagnosisStopReason` | ProgressTracker | 为什么停止继续收集 | 对外发布什么内容 |
| `SemanticVerdict` | SemanticGuard | 已验真证据是否支持 Draft | Run 是否成功执行 |
| `ReleaseOutcome` | Application/Release | 对外处理结果属于成功、降级、失败还是取消 | Tool 或收集过程的内部状态 |
| `FallbackType` | SafeFallbackFactory | FALLBACK 的业务/安全原因 | 整个 Run 的执行终态 |
| `TraceEventStatus` | 单个 Trace Event | 某条事件的局部结果 | 全局生命周期 |
| SSE Session `State` | ChatSseSession | 连接能否继续发送事件 | Harness Run 的业务结果 |
```mermaid
flowchart LR
subgraph Execution["执行控制维度"]
RS["RunState<br/>RUNNING -> terminal"]
CS["CollectionState<br/>COLLECTING / SATURATED"]
SR["StopReason<br/>停止收集原因"]
end
subgraph Tool["单次 Tool 维度"]
IS["InvocationStatus<br/>PROJECTING / READY / ERROR"]
ES["EvidenceStatus<br/>FOUND / NO_EVIDENCE / ERROR"]
IG["InformationGain<br/>GAINED / NO_GAIN"]
end
subgraph Validation["验证维度"]
EV["EvidenceGuardResult<br/>valid / violations"]
SV["SemanticVerdict<br/>SUPPORTED / UNSUPPORTED"]
end
subgraph Publication["发布与观察维度"]
RO["ReleaseOutcome<br/>SUCCESS / FALLBACK / FAILED / CANCELLED"]
FT["FallbackType<br/>FALLBACK 原因"]
SSE["SSE Session State<br/>连接发送状态"]
DB["diagnosis_run.status<br/>持久化通用状态"]
end
IS --> ES
ES --> IG --> CS
CS --> SR
ES --> EV --> SV
SR --> RO
SV --> RO
RS --> RO
RO --> FT
RO --> SSE
RO --> DB
```
箭头表示信息参与后续决策,不表示枚举之间一一转换。例如 `READY + EVIDENCE_FOUND` 仍可能得到 `NO_GAIN`,`RunState.SUCCESS` 也可能对应 `ReleaseOutcome.FALLBACK`。
完整状态转换和跨层映射见[Harness生命周期与状态.md](Harness生命周期与状态.md)。
## 10. 命名规则
后续代码和文档遵守以下用词:
1. 说“Run 成功/失败/取消/超时/预算耗尽”时,明确写 `RunState`。
2. 说“发布正常内容/Fallback/失败/取消”时,明确写 `ReleaseOutcome`。
3. 不单独写“Tool 成功”,改写为 `InvocationStatus=READY`,并同时说明 EvidenceStatus。
4. 不写“找到有效证据”,除非已经说明是候选内容、已验真事实还是足以支持结论。
5. `NO_EVIDENCE` 必须附带 scope,不得写成全局否定。
6. `SATURATED` 只用于 Collection State;预算耗尽使用 `BUDGET_LIMIT_REACHED` 或 `RunState.BUDGET_EXHAUSTED`。
7. `Fallback` 只指安全发布降级,不用来泛指异常兜底代码。
8. `Agent` 默认指 Diagnosis Agent;SemanticGuard 和 EvidenceRepair 分别称“语义审查器”和“引用修复步骤”。
9. `agent_result` 引用字段名时保留原名,概念说明使用“canonical projected Tool result”。
10. `status` 单独出现没有意义,必须注明所属类型或存储字段。
## 11. 已知命名债务
### 11.1 `DiagnosisRun` 名称大于实际诊断范围
当前 Chat Application 对三种 intent 都写入 `diagnosis_run`。文档统一称 Run;是否重命名实体和表属于单独的协议/迁移决策,本次不修改代码。
### 11.2 `SseOutcome` 当前未被运行时使用
`SseOutcome` 枚举存在,但当前 `ChatSseEvent.Done` 直接携带 `ReleaseOutcome`,并禁止公开 `CANCELLED`。因此当前 SSE 真理源是 `ReleaseOutcome`,不要再基于 `SseOutcome` 推导协议。
### 11.3 `FallbackType.BUDGET_EXHAUSTED` 不是当前诊断发布主路径
枚举值仍存在,但当前 Diagnosis Release 对预算受控停止的行为是:有已验真进展时发布 `INSUFFICIENT_EVIDENCE`,无安全进展时保持失败。文档不能仅因枚举存在就声称系统会发布 `BUDGET_EXHAUSTED` Fallback。
### 11.4 数据库 `status=SUCCESS` 不等于找到根因
`JpaChatRunStore` 将 `ReleaseOutcome.SUCCESS` 和 `FALLBACK` 都映射为数据库 `status=SUCCESS`,表示请求被正常处理。是否发布根因必须结合 `release_outcome`、`content_type` 和 FallbackType 判断。
### 11.5 RunState 没有直接作为独立字段持久化
当前持久化主记录保存通用 `status` 和 `release_outcome`,Trace 保存阶段事件;内存 `RunTermination.state/reason` 不是独立数据库字段。排查时不能只靠数据库 `status` 反推 TIMED_OUT 或 BUDGET_EXHAUSTED 的精确内部终态。
## 12. 如何使用本文
本文不是必读的第一章,而是遇到名词歧义时使用的词典。第一次接触 Harness,请先读 [README.md](README.md) 建立最小心智模型;需要理解某个状态或概念时,再回到本文对应章节查询。