docs(harness): add evidence-chain, application-audit, contract-state and interview review notes; annotate guard/release core classes
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
# Harness application + audit 学习笔记:从 Run 编排到可回放审计
|
||||
|
||||
**更新日期**:2026-08-06
|
||||
**主题**:application 域(Run 全生命周期编排 + 安全落库)+ audit 域(可观测账本)——重点讲清 audit 与 trace 的设计与区别
|
||||
**配套**:[证据安全链笔记](Harness%20证据安全链学习笔记-从收敛控制到唯一发布点.md)、[执行控制笔记](Harness执行控制笔记-终态检查与取消广播.md)
|
||||
|
||||
## 1. 一句话定位
|
||||
|
||||
```text
|
||||
application = Run 应用所有者:创建 Run / 路由意图 / 执行分支 / 持久化 / SSE 输出
|
||||
audit = 可观测账本:Trace 时序回放 + Token 对账 + 各明细审计表(metadata-only)
|
||||
```
|
||||
|
||||
## 2. application 域:ChatApplicationUseCase 六步编排
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Controller → execute(request, observer)"] --> B["① 读会话上下文<br/>RoutingHistory + PreviousTurn"]
|
||||
B --> C["② core.startRun<br/>创建 Run 边界"]
|
||||
C --> D["③ persistStart + observer.onStarted<br/>(SSE metadata + 取消句柄 CoreRunControl)"]
|
||||
D --> E["④ router.route<br/>意图路由(单次模型调用)"]
|
||||
E --> F["⑤ executePath 按 intent 分叉<br/>SYSTEM_CHAT / KNOWLEDGE_QUERY / DIAGNOSIS"]
|
||||
F --> G["⑥ completePath + persistFinish + 返回"]
|
||||
G -.异常.-> H["统一失败出口<br/>terminalOutcome + safeFailure"]
|
||||
```
|
||||
|
||||
### 2.1 关键设计点
|
||||
|
||||
| 设计 | 代码事实 | 意义 |
|
||||
|---|---|---|
|
||||
| 取消句柄 | `observer.onStarted(new CoreRunControl(core, context))` → `core.cancel(context, CLIENT_DISCONNECTED)` | 客户端断连 → 取消广播 → 强杀 in-flight 调用 |
|
||||
| 取消有原因 | `RunCancellationReason.CLIENT_DISCONNECTED` | 区分断连/用户取消,可审计 |
|
||||
| sessionId 白名单 | `SAFE_ID = [A-Za-z0-9][A-Za-z0-9._-]{0,63}` | 信任边界校验 |
|
||||
| 多轮记忆有界 | RoutingHistory(intent+query) + PreviousTurn(PublishedResult 有界化) | 上一轮只传安全摘要,无 tool ids / raw evidence |
|
||||
| 预算终态特殊处理 | `handledBudgetTermination()` 时不二次 completeSuccess | 不破坏 first-terminal-wins |
|
||||
| 统一失败出口 | terminalOutcome → CANCELLED/FAILED + ChatFailureCode(文案安全) | 不暴露内部堆栈 |
|
||||
| 路由输出契约 | `OUTPUT_FIELDS = {intent}`,值必须枚举名 | 防模型夹带 |
|
||||
|
||||
### 2.2 持久化(JpaChatRunStore + PublishedResultPolicy)
|
||||
|
||||
- start / markIntent / finish(@Transactional),finish 写 outcome + safeContentJson + publishedResult;
|
||||
- PublishedResultPolicy:**只有 SUCCESS 且 draft 有结论才构造 PublishedResult**;sanitize 全套有界(query 2000/结论 2000/scope 1000/limitations 10×500/文档 10);
|
||||
- sourceDocuments 只收 RAG 类型证据的 document_id(去重)——MySQL/日志证据不进发布文档列表;
|
||||
- PreviousTurn = sanitize 后的有界摘要(多轮记忆来源)。
|
||||
|
||||
## 3. audit 域:可观测账本的层次
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph 写入侧["写入侧(不阻断主流程)"]
|
||||
H1["HarnessAgentAuditHook<br/>每模型步 → agent_step + agent_reasoning_audit"]
|
||||
H2["ToolInvocationAuditSink<br/>工具 → tool_invocation"]
|
||||
H3["ModelCallAuditor<br/>Token → ledger + core + agent_step 回写"]
|
||||
H4["JpaDiagnosisTraceRecorder<br/>事件 → diagnosis_trace_event"]
|
||||
H5["JpaChatRunStore<br/>Run → diagnosis_run"]
|
||||
end
|
||||
|
||||
subgraph 读取侧["读取侧(回放)"]
|
||||
S["DiagnosisTraceService"]
|
||||
S --> R["DiagnosisTraceResponse<br/>timeline + steps + toolInvocations + run + summary"]
|
||||
end
|
||||
|
||||
H1 --> DB[(MySQL 各表)]
|
||||
H2 --> DB
|
||||
H3 --> DB
|
||||
H4 --> DB
|
||||
H5 --> DB
|
||||
DB --> S
|
||||
```
|
||||
|
||||
### 3.1 各落库点(谁写哪张表)
|
||||
|
||||
| 落库点 | 表 | 内容 |
|
||||
|---|---|---|
|
||||
| HarnessAgentAuditHook | agent_step | 每模型步摘要(stepIndex/耗时/token/工具计划) |
|
||||
| HarnessAgentAuditHook | agent_reasoning_audit | 推理 + assistant_text 正文(受限) |
|
||||
| ToolInvocationAuditSink | tool_invocation | 工具入参/输出预览/检索明细 |
|
||||
| JpaDiagnosisTraceRecorder | diagnosis_trace_event | 全链路事件时序线 |
|
||||
| ModelCallAuditor | agent_step.token_count | Token 回写(仅 DIAGNOSIS_AGENT) |
|
||||
| JpaChatRunStore | diagnosis_run / chat_session | Run 生命周期 + 发布契约 |
|
||||
|
||||
### 3.2 audit 的三个核心边界(fail-safe / metadata-only / 对账)
|
||||
|
||||
1. **审计不阻断主流程**:三个落库点全部 try-catch + log.warn——审计挂了不能让 Run 跟着挂;
|
||||
2. **metadata-only 分层**:正文只允许出现在 agent_reasoning_audit(受限)和 tool_invocation(入参/输出预览),其余全部摘要化;
|
||||
3. **Token 三写闭环**:ledger 分账 → core.recordTokens(Run 预算)→ agent_step.token_count 回写——审计与预算同源可对账。
|
||||
|
||||
## 4. audit vs trace:设计与区别(重点)
|
||||
|
||||
### 4.1 核心区别:包含关系,不是并列
|
||||
|
||||
```text
|
||||
audit = 域(可观测账本的总集合,17 个文件)
|
||||
├─ ★ trace = 域内的事件回放子体系(诊断时序线)
|
||||
├─ ModelCallLedger / Auditor(Token 记账)
|
||||
├─ HarnessAgentAuditHook(模型步审计)
|
||||
├─ ToolInvocationAuditSink(工具审计)
|
||||
├─ RagLookupAuditEnricher(RAG 检索审计)
|
||||
└─ RunConclusionExtractor(结论提取)
|
||||
```
|
||||
|
||||
**常见误解修正**:trace 不是「agent 执行记录」,而是**全链路七阶段时序线**(RUN/ROUTING/AGENT/TOOL/EVIDENCE/SEMANTIC/RELEASE);audit 也不只是「tool 审计」,tool 审计只是其中一小块。
|
||||
|
||||
### 4.2 一张表讲清区别
|
||||
|
||||
| 维度 | trace(diagnosis_trace_event) | audit(各明细账本) |
|
||||
|---|---|---|
|
||||
| 本质 | 时序事件流(按 sequence_no 排序) | 实体化明细记录 |
|
||||
| 回答 | 发生了什么、按什么顺序 | 每个细节落在哪本账上 |
|
||||
| 粒度 | 每帧只带摘要 + 关联键(step_id 等) | 完整字段(入参/输出/token/耗时) |
|
||||
| 结构 | 一张表、一个序列 | 多张表(agent_step/tool_invocation/...) |
|
||||
| 排序保证 | sequence_no 单调(ConcurrentHashMap 分配) | step_index / id / createdAt 排序 |
|
||||
| 典型查询 | `findByRunIdOrderBySequenceNoAscIdAsc` | `findByRunIdOrderByStepIndex` 等 |
|
||||
| 与明细的关系 | 靠 step_id / run_id 互链,不重复存储 | 承载被关联的实体数据 |
|
||||
|
||||
### 4.3 真实数据对照(run 1b584a01)
|
||||
|
||||
```text
|
||||
trace(15 帧时序线):
|
||||
RUN_STARTED → ROUTING_DECISION → AGENT_MODEL_STEP×2 → TOOL_INVOCATION
|
||||
→ EVIDENCE_GUARD_INITIAL → SEMANTIC_GUARD_DECISION → RELEASE_DECISION → RUN_FINISHED
|
||||
|
||||
audit 各账本(同 Run):
|
||||
agent_step 2 行(token 2461/4415,耗时 1418/8542ms)
|
||||
agent_reasoning_audit 2 行(step1 = 完整 Draft JSON)
|
||||
tool_invocation 1 行(lookup_knowledge,step_id=979)
|
||||
diagnosis_run outcome=SUCCESS + published_result 完整 JSON
|
||||
```
|
||||
|
||||
**关联示例**:trace 第 7 帧 TOOL_INVOCATION 的 details 里 `step_id=979` = agent_step.id=979 = tool_invocation.step_id——时序帧与明细账本通过 id 互链。
|
||||
|
||||
## 5. 可回放机制(DiagnosisTraceService)
|
||||
|
||||
```text
|
||||
GET /api/diagnosis/{sessionId}/trace?runId=xxx
|
||||
→ DiagnosisTraceResponse 七块:runId / chatSession / session / run /
|
||||
steps[] / toolInvocations[] / timeline[] / summary
|
||||
|
||||
GET /api/diagnosis/{sessionId}/trace/reasoning?runId=xxx(受限:runId 必填)
|
||||
→ agent_reasoning_audit(reasoning + assistantText)
|
||||
```
|
||||
|
||||
三级回放深度:**时间线(timeline)→ 明细(steps/toolInvocations)→ 推理(reasoning,按需受限读取)**。
|
||||
|
||||
回放能成立的四个保证:
|
||||
1. sequence_no 单调(索引 idx_trace_event_run_sequence);
|
||||
2. 事件与明细靠 step_id 互链;
|
||||
3. summary 的 persisted vs returned 双计数对账(发现落库不完整);
|
||||
4. 双查询入口兼容新旧会话(buildLegacyTraceResponse)。
|
||||
|
||||
## 6. 面试话术(30 秒)
|
||||
|
||||
> "application 域是 Run 的应用所有者:一次请求六步编排——建 Run 边界、意图路由(单次模型调用、输出契约严格为 {intent} 枚举)、按意图分叉执行、路径完成后写终态并持久化发布契约,异常统一走失败出口映射成安全的 ChatFailureCode;取消能力通过 SSE 句柄暴露给客户端(断连即 core.cancel)。**audit 域是可观测账本,trace 是它内部的事件回放子体系**:trace 用一张 diagnosis_trace_event 表按 sequence_no 记录全链路七阶段的事件时序线(每帧只带摘要和关联键),audit 的明细账本(agent_step / tool_invocation / agent_reasoning_audit / diagnosis_run)承载完整字段,两层通过 step_id/run_id 互链不重复存储。三个边界:审计不阻断主流程(fail-safe)、metadata-only(正文只在受限审计表)、Token 三写闭环(ledger 分账 → Run 预算 → 明细回写)。回放由 DiagnosisTraceService 按 runId 聚合排序,三级深度:时间线 → 明细 → 推理。"
|
||||
|
||||
## 7. 代码位置索引
|
||||
|
||||
| 组件 | 文件 |
|
||||
|---|---|
|
||||
| ChatApplicationUseCase / ChatFailureCode / ChatApplicationStatus | `src/main/java/com/superbiz/agent/harness/application/` |
|
||||
| DiagnosisChatExecutor / 其他 executor | `.../application/executor/` |
|
||||
| IntentRouter / IntentRouterPrompt | `.../application/routing/` |
|
||||
| JpaChatRunStore / PublishedResultPolicy / RoutingHistory | `.../application/persistence/` |
|
||||
| Trace 体系(Recorder/Event/Type/Status/AuditEvents) | `src/main/java/com/superbiz/agent/harness/audit/`(前半) |
|
||||
| ModelCallLedger / ModelCallAuditor / HarnessAgentAuditHook / RunConclusionExtractor | `.../audit/`(后半) |
|
||||
| DiagnosisTraceService / DiagnosisTraceController | `src/main/java/com/superbiz/agent/service/` + `controller/` |
|
||||
| V014 建表(diagnosis_trace_event) | `src/main/resources/db/migration/V014__create_diagnosis_trace_event.sql` |
|
||||
Reference in New Issue
Block a user