# 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["① 读会话上下文
RoutingHistory + PreviousTurn"] B --> C["② core.startRun
创建 Run 边界"] C --> D["③ persistStart + observer.onStarted
(SSE metadata + 取消句柄 CoreRunControl)"] D --> E["④ router.route
意图路由(单次模型调用)"] E --> F["⑤ executePath 按 intent 分叉
SYSTEM_CHAT / KNOWLEDGE_QUERY / DIAGNOSIS"] F --> G["⑥ completePath + persistFinish + 返回"] G -.异常.-> H["统一失败出口
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
每模型步 → agent_step + agent_reasoning_audit"] H2["ToolInvocationAuditSink
工具 → tool_invocation"] H3["ModelCallAuditor
Token → ledger + core + agent_step 回写"] H4["JpaDiagnosisTraceRecorder
事件 → diagnosis_trace_event"] H5["JpaChatRunStore
Run → diagnosis_run"] end subgraph 读取侧["读取侧(回放)"] S["DiagnosisTraceService"] S --> R["DiagnosisTraceResponse
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` |