Files
SuperBizAgent-java/mvp/engineering/harness/Harness application+audit 学习笔记-从 Run 编排到可回放审计.md

167 lines
9.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` |