9.6 KiB
Harness application + audit 学习笔记:从 Run 编排到可回放审计
更新日期:2026-08-06 主题:application 域(Run 全生命周期编排 + 安全落库)+ audit 域(可观测账本)——重点讲清 audit 与 trace 的设计与区别 配套:证据安全链笔记、执行控制笔记
1. 一句话定位
application = Run 应用所有者:创建 Run / 路由意图 / 执行分支 / 持久化 / SSE 输出
audit = 可观测账本:Trace 时序回放 + Token 对账 + 各明细审计表(metadata-only)
2. application 域:ChatApplicationUseCase 六步编排
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 域:可观测账本的层次
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 / 对账)
- 审计不阻断主流程:三个落库点全部 try-catch + log.warn——审计挂了不能让 Run 跟着挂;
- metadata-only 分层:正文只允许出现在 agent_reasoning_audit(受限)和 tool_invocation(入参/输出预览),其余全部摘要化;
- Token 三写闭环:ledger 分账 → core.recordTokens(Run 预算)→ agent_step.token_count 回写——审计与预算同源可对账。
4. audit vs trace:设计与区别(重点)
4.1 核心区别:包含关系,不是并列
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)
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)
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,按需受限读取)。
回放能成立的四个保证:
- sequence_no 单调(索引 idx_trace_event_run_sequence);
- 事件与明细靠 step_id 互链;
- summary 的 persisted vs returned 双计数对账(发现落库不完整);
- 双查询入口兼容新旧会话(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 |