生产级Agent(18):审计账本与可解释执行
文章摘要
前十七篇已经把生产级 Agent 从 Planner、Tool Calling、Memory、Checkpoint、Human-in-the-Loop、多 Agent、安全沙箱、质量门禁、Control Plane、Capability Registry 一直推进到 Agent Identity 与 Delegated Authority。系统现在已经能回答:有哪些 Agent、有哪些能力、谁能调用、代表谁、为什么调用。
但真正发生事故以后,还缺一个更现实的问题:能不能把一次自动操作完整还原出来,并给出可验证证据?
仅保存聊天记录远远不够。一次高风险 Agent Run 可能跨越模型调用、RAG、多个 Tool、审批、重试、外部副作用、Artifact 和人工接管。最终用户只看到一句“任务完成”,而安全团队需要回答:哪个模型版本做了哪个决定?当时看到了哪些输入?用了哪个授权?哪个 Tool 真正成功?哪个请求只是超时但其实已经落库?某个 Artifact 来源于哪些数据?为什么系统在 10:18 决定执行 crm.update?如果今天重放同一 Run,是否还能得到同样的事实链?
本篇建立独立的 Agent Audit Ledger:把 Run Event、Decision、Authority、Tool Intent、Tool Result、Artifact、Approval 和 External Side Effect 组织成不可随意覆盖的证据链;通过 Hash Chain、Sequence、Idempotency Key、Evidence Reference 和 Snapshot 保证事件顺序与来源可验证;再在此基础上构建 Explainable Execution,让用户看到“系统基于哪些证据、经过哪些规则、执行了哪些真实动作”,而不是展示模型私有推理过程。
一、聊天日志为什么不能充当审计账本
假设 Agent 任务:
找到续约风险最高的 20 个客户,
生成跟进方案,
并给负责人创建 CRM Task。
真正执行链可能是:
读取客户列表
→ RAG 拉取合同
→ 查询 Support Ticket
→ 模型评分
→ Reviewer 复核
→ 人工批准
→ CRM 创建 Task
→ 部分请求超时
→ 重试前先对账
→ 生成报告
聊天记录通常只保存:
用户输入
模型最终回答
最多再加:
Tool Name
这无法解释外部世界发生了什么。
二、审计的最小单位应该是 Event
每个事实变化写一个不可变 Event:
public record AuditEvent(
String eventId,
String runId,
long sequence,
AuditEventType type,
String actorType,
String actorId,
String payloadRef,
String payloadHash,
String previousEventHash,
String eventHash,
Instant occurredAt) {
}
Event Type:
public enum AuditEventType {
RUN_CREATED,
CONTEXT_BOUND,
MODEL_REQUESTED,
MODEL_COMPLETED,
DECISION_RECORDED,
CAPABILITY_REQUESTED,
AUTHORITY_GRANTED,
APPROVAL_REQUESTED,
APPROVAL_GRANTED,
TOOL_INTENT_RECORDED,
TOOL_STARTED,
TOOL_COMPLETED,
TOOL_UNKNOWN,
TOOL_RECONCILED,
ARTIFACT_CREATED,
HUMAN_TAKEOVER,
RUN_COMPLETED,
RUN_CANCELLED
}
关键原则:
Append Only
不原地修改过去事件。
三、状态可以改,历史不能改
业务表:
execution_task.status = SUCCEEDED
当然可以 Update。
Audit Ledger 不应该:
update audit_event
set payload = ...
where event_id = ...;
如果需要纠正:
追加 CORRECTION Event
例如:
{
"type": "AUDIT_CORRECTION",
"corrects_event": "evt-91",
"reason": "provider status was reconciled",
"new_status": "SUCCEEDED"
}
这样能看到:
当时知道什么
后来确认了什么
四、为什么要有 Sequence
分布式系统中:
模型完成
Tool 回调
审批
消息队列
事件到达时间可能乱序。
每个 Run 使用:
sequence
形成明确逻辑顺序。
例如:
101 TOOL_INTENT_RECORDED
102 TOOL_STARTED
103 TOOL_UNKNOWN
104 TOOL_RECONCILED
105 ARTIFACT_CREATED
UI 可以按 Sequence 重放。
五、Timestamp 不足以确定顺序
两个节点时间可能差几十毫秒。
甚至 NTP 漂移。
所以:
occurred_at
用来表示真实时间。
sequence
用来表示 Run 内逻辑顺序。
两者都保存。
六、Hash Chain 可以发现历史被篡改
每个 Event 计算:
hash(
event_id
+ run_id
+ sequence
+ type
+ payload_hash
+ previous_event_hash
)
例如:
String eventHash = sha256(
event.eventId()
+ event.runId()
+ event.sequence()
+ event.type()
+ event.payloadHash()
+ event.previousEventHash());
这样修改中间 Event 会让后续 Hash Chain 失效。
它不是区块链。
没有必要引入复杂共识。
只是最简单的:
Tamper Evidence
七、Payload 不要全部塞 Audit 表
模型 Prompt、Tool Output、PDF、截图可能很大。
Ledger 保存:
payload_ref
payload_hash
大内容进入:
Artifact Store
Secure Object Storage
例如:
{
"payload_ref": "artifact://audit/run-82/model-response-19",
"payload_hash": "sha256:..."
}
减少数据库膨胀。
八、敏感内容不能因为“审计”就永久保存
审计不是无限数据保留许可证。
密码、OTP、Access Token、Refresh Token:
永远不进 Ledger
PII、合同、医疗等内容:
保存 Ref / Hash / Redacted Summary
具体原文按数据保留策略存储。
九、我会把审计数据分三层
Metadata
长期保存:
Run ID
Event Type
Actor
Model Version
Capability
Result
Hash
Evidence
中期保存:
Prompt Snapshot
Tool Result
Artifact
Secret
不保存或极短期:
Token
Password
OTP
Session Cookie
十、Decision Event 不是保存 Chain-of-Thought
“可解释执行”最容易走错方向:
把模型私有推理全文保存并展示
这既没必要,也可能包含敏感或不可靠内容。
真正需要的是结构化 Decision Record:
public record DecisionRecord(
String decisionId,
String runId,
String decisionType,
String outcome,
List evidenceRefs,
List policyRefs,
List candidateActions,
String selectedAction,
double confidence,
String modelProfile,
String promptHash) {
}
它回答:
系统看了哪些证据
受哪些规则约束
有哪些候选动作
最后选了什么
这已经足够审计。
十一、不要把“模型说因为...”当审计证据
模型可能生成一段听起来合理的事后解释。
例如:
“因为客户最近有三次投诉,所以风险高。”
真正证据应该是:
support-ticket://191
support-ticket://203
support-ticket://221
解释必须绑定 Evidence Ref。
否则只是:
Narrative
不是证据。
十二、Evidence Binding
每个 Claim:
public record ExplainableClaim(
String claimId,
String text,
List evidenceRefs,
ClaimStatus status) {
}
状态:
SUPPORTED
PARTIALLY_SUPPORTED
UNSUPPORTED
CONFLICTED
用户查看解释时,能跳到真实证据。
十三、Tool Intent 必须先于 Tool Execution
高风险 Tool 不应该先执行,再补日志。
流程:
Record Intent
↓
Commit
↓
Execute Tool
↓
Record Result
Intent:
public record ToolIntent(
String operationId,
String runId,
String capabilityId,
String inputHash,
String idempotencyKey,
String grantId,
String approvalId,
Instant createdAt) {
}
这能处理最重要的故障窗口。
十四、最危险的故障窗口:Tool 成功,回调丢了
时间线:
10:20:01 CRM 创建 Task 成功
10:20:01 网络断开
10:20:02 Agent 收到 Timeout
如果系统只有:
Tool Call Failed
就会重试。
结果:
重复创建 Task
正确 Ledger:
TOOL_INTENT_RECORDED
TOOL_STARTED
TOOL_UNKNOWN
然后:
Reconcile
查业务系统。
确认已经创建:
TOOL_RECONCILED = SUCCEEDED
十五、UNKNOWN 必须是一等状态
public enum ExternalOperationStatus {
INTENT_RECORDED,
EXECUTING,
SUCCEEDED,
FAILED,
UNKNOWN,
RECONCILING
}
不要把 Timeout 自动等于 Failed。
这是生产 Agent 最容易产生重复副作用的根因之一。
十六、Idempotency Key 必须写进 Ledger
例如:
tenant
+ run
+ capability
+ business_object
+ input_hash
生成:
idem-f8ab...
重复请求先查 Ledger:
相同 Key 已经 SUCCEEDED
直接返回原结果。
十七、Approval 也进入证据链
记录:
谁批准
批准什么
批准时的输入 Hash
有效期
public record ApprovalEvidence(
String approvalId,
String approvedBy,
String actionHash,
Instant approvedAt,
Instant expiresAt) {
}
执行时 Action Hash 变化:
APPROVAL_INVALIDATED
而不是继续使用旧批准。
十八、Authority Chain 需要可回放
上一期建立的:
User
→ Connection
→ Purpose
→ Execution Grant
→ Capability
本期 Ledger 必须保存对应 Ref。
Tool Event:
{
"subject": "user-82",
"agent": "renewal-agent",
"purpose": "renewal-review",
"grant_id": "grant-18",
"capability": "crm.customer.update"
}
事故后才能证明:
这次操作确实来自哪条委托链
十九、多 Agent 还要保存 Delegation Chain
Supervisor
→ Research Agent
→ CRM Tool
记录:
parent_run
parent_grant
from_agent
to_agent
不要只看到最终 Worker。
二十、Artifact Provenance
Agent 生成的报告:
report.pdf
必须知道来源:
哪些输入文档
哪些 Tool Result
哪个 Prompt
哪个模型
哪个 Run
Manifest:
public record ArtifactProvenance(
String artifactId,
List inputArtifacts,
List evidenceRefs,
String generatedByRun,
String generatedByStep,
String modelProfile,
String promptHash,
String contentHash) {
}
二十一、为什么 Content Hash 重要
文件名:
report-final.pdf
可以被覆盖。
Hash:
sha256:...
才能证明 Review 的和发布的是同一内容。
Approval 应绑定 Artifact Hash。
二十二、Run Snapshot 用于快速恢复,不替代 Ledger
Ledger 事件可能有几千条。
每次恢复都从 Event 1 重放很慢。
可以定期生成 Snapshot:
public record RunSnapshot(
String runId,
long throughSequence,
String stateRef,
String stateHash,
Instant createdAt) {
}
恢复:
Load Snapshot @ seq 1800
↓
Replay 1801...
但 Ledger 仍然保留。
二十三、Snapshot 也必须能验证
保存:
state_hash
through_sequence
last_event_hash
防止 Snapshot 和事件链不一致。
二十四、Event Store 表结构
create table agent_audit_event (
run_id varchar(128) not null,
sequence bigint not null,
event_id varchar(128) not null,
event_type varchar(64) not null,
actor_type varchar(32) not null,
actor_id varchar(128) not null,
payload_ref varchar(512),
payload_hash varchar(128) not null,
previous_event_hash varchar(128),
event_hash varchar(128) not null,
occurred_at timestamptz not null,
primary key (run_id, sequence),
unique (event_id)
);
索引:
create index idx_audit_event_type
on agent_audit_event(event_type, occurred_at);
二十五、Sequence 怎么安全分配
单库可以:
SELECT FOR UPDATE run_sequence
高吞吐可以:
Run 单分区
Actor/Mailbox
Event Stream Partition by run_id
保证同一 Run 单序列写入。
不要全系统一个 Sequence。
二十六、Audit Event 和业务事务的一致性
最常见错误:
业务状态 Commit
Audit 写失败
最后无证据。
可以用 Transactional Outbox:
Business Update
+
Audit Outbox
同事务 Commit。
Outbox Worker 再写长期 Ledger。
二十七、Outbox Event 自己也要幂等
outbox_id
作为 Ledger Event ID。
重复投递:
UNIQUE(event_id)
直接忽略。
二十八、模型调用记录什么
至少:
Provider
Model Snapshot
Prompt Hash
Input Context Refs
Tool Catalog Version
Token Usage
Latency
Finish Reason
不一定保存完整私有推理。
二十九、Context Manifest 比完整 Prompt Dump 更适合长期审计
例如:
{
"system_prompt_hash": "...",
"skills": [
"risk-analysis-v7"
],
"memory_refs": [
"mem-12"
],
"artifact_refs": [
"contract-83"
],
"tool_catalog_version": "19"
}
需要详细复核时再根据 Ref 找原内容。
三十、Explainable Execution 页面应该展示什么
不是展示:
模型内部思维链
而是展示:
Goal
Facts Used
Policies Applied
Actions Taken
Approvals
Artifacts
Outcome
例如:
为什么创建 CRM Task?
Evidence:
- Contract expires in 31 days
- 3 critical support tickets
- ARR > threshold
Policy:
renewal-risk-v5
Decision:
HIGH_RISK
Action:
crm.task.create
Approved by:
user-82
这才是业务上真正可解释。
三十一、解释必须区分“事实”和“模型判断”
例如:
事实:合同 31 天后到期
判断:续约风险高
UI 不应该都写成同一种文本。
可以:
FACT
INFERENCE
POLICY_DECISION
ACTION
四种标签。
三十二、Inference 需要 Confidence,但不要迷信它
模型自报:
confidence=0.92
不一定校准。
更可靠的 Confidence 来源可以组合:
模型置信信号
证据覆盖率
Evaluator
历史成功率
解释页面可以显示:
Evidence Coverage: 5/5
Policy Match: yes
Judge: pass
比一个单一 92% 更有意义。
三十三、为什么需要 Failure Taxonomy
Run 失败不能都写:
ERROR
至少分类:
MODEL_FAILURE
TOOL_FAILURE
AUTHORITY_DENIED
APPROVAL_EXPIRED
EXTERNAL_UNKNOWN
BUDGET_EXCEEDED
POLICY_VIOLATION
HUMAN_CANCELLED
后续才能统计问题到底在哪。
三十四、用户投诉时可以直接生成“执行收据”
用户问:
为什么系统修改了我的客户记录?
平台可以生成:
Run: run-82
Time: 10:21
Agent: renewal-agent
On behalf of: user-17
Purpose: renewal-review
Capability: crm.customer.update
Approval: approval-91
Resource: customer/12345
Result: SUCCESS
这就是 Authority Receipt + Execution Receipt。
三十五、不是所有内部 Event 都应该给普通用户看
管理员可以看到:
完整技术事件
业务用户只需要:
关键决策
真实动作
证据
批准
结果
要做 View Policy。
三十六、Audit Ledger 自己也需要访问控制
因为里面可能包含:
系统架构
安全规则
用户行为
敏感 Resource Ref
权限:
User:自己的 Run
Team Admin:租户内摘要
Security:高风险事件
Platform:技术诊断
不能所有开发者随便查全库。
三十七、Retention 不能统一一个值
例如:
普通模型 Metadata:180 天
高风险 Tool Ledger:2 年
Approval Evidence:按合规要求
Raw Prompt:30 天
Secrets:0 天
每类 Event 独立 Policy。
三十八、删除用户数据时怎么办
审计和删除权会冲突。
一种做法:
保留必要审计 Metadata
删除/匿名化原始内容
例如:
subject_id → anonymized id
payload_ref → deleted
payload_hash → retain if legally allowed
具体要按企业合规要求设计。
三十九、审计存储要防“管理员静默修改”
可以使用:
Append-only DB Permission
Object Lock
WORM Storage
Hash Chain
Separate Audit Account
至少做到:
普通业务管理员无权修改历史审计数据
四十、跨系统 Evidence 需要统一 Correlation ID
Agent:
run-82
CRM:
request-981
MCP:
tool-call-123
统一传播:
run_id
operation_id
idempotency_key
否则出了事故只能靠时间戳猜是不是同一次调用。
四十一、OpenTelemetry 可以承担 Trace,不等于 Audit Ledger
Trace 很适合:
性能
调用拓扑
错误
但 Trace 可能:
采样
过期
被聚合
高风险审计不能依赖 10% Sampling Trace。
所以:
Observability Trace
和
Authoritative Audit Ledger
应该分开。
四十二、两者通过 ID 关联
Audit Event 保存:
trace_id
span_id
排查时:
Ledger → Trace
看详细性能。
四十三、一个很实用的 Replay API
GET /api/runs/{runId}/timeline
返回:
{
"run_id": "run-82",
"events": [
{
"seq": 101,
"type": "DECISION_RECORDED"
},
{
"seq": 102,
"type": "APPROVAL_GRANTED"
},
{
"seq": 103,
"type": "TOOL_INTENT_RECORDED"
}
]
}
UI 按时间线重放。
四十四、Replay 不等于重新执行
审计 Replay:
重放历史事件
Execution Replay:
重新跑模型和 Tool
后者可能产生新副作用。
默认必须是:
Read-only Historical Replay
四十五、需要重新执行时使用 Simulation Mode
Tool:
Recorded Result
Sandbox
No-op
不能打真实生产。
例如:
crm.update
Replay 时只返回历史 Tool Result。
四十六、为什么 Explainability 也要版本化
解释规则会变化。
例如风险评分 v5 和 v6 权重不同。
所以解释保存:
policy_version
rubric_version
model_profile
否则今天看历史 Run,系统会用今天规则解释昨天决策,产生“历史重写”。
四十七、审计测试不能只测 Happy Path
必须故障注入:
Tool Success + Callback Lost
Outbox Duplicate
Event Writer Crash
Approval Expires Mid-call
Credential Revoked
Audit Object Store Slow
Run Cancel During Tool
然后检查:
历史是否仍然完整
副作用是否可对账
四十八、Hash Chain 验证任务
定时:
Daily Ledger Integrity Check
检查:
sequence 连续
previous_hash 匹配
payload_hash 可验证
发现不一致:
SECURITY ALERT
四十九、指标
agent_audit_event_total{
type
}
agent_audit_write_failure_total
agent_audit_chain_invalid_total
agent_external_unknown_total{
capability
}
agent_reconciliation_total{
result
}
agent_explanation_missing_evidence_total
agent_audit_payload_redacted_total
高基数 Run ID 放 Trace,不放 Metric Label。
五十、SLO
High-risk Tool Audit Coverage = 100%
Missing Authority Chain = 0
Unreconciled UNKNOWN > 1h = 0
Audit Hash Chain Failure = 0
Approval without Action Hash = 0
Secrets persisted in Ledger = 0
五十一、主流程
Run Created
↓
Context Bound
↓
Decision Recorded
↓
Capability Requested
↓
Authority Granted
↓
Approval if needed
↓
Tool Intent Recorded
↓
Tool Executed
↓
Result / UNKNOWN
↓
Reconcile
↓
Artifact Created
↓
Run Completed
↓
Execution Receipt
每一步都能回放。
五十二、和第17篇 Delegated Authority 的关系
第17篇解决:
为什么有权做
第18篇解决:
实际做了什么,如何证明
Authority 没有 Ledger:
权限正确但无法举证
Ledger 没有 Authority:
知道发生了什么,但不知道为什么允许
两者必须合起来。
五十三、和 Quality Gate 的关系
线上失败进入 Hard Case 时,需要完整 Run Evidence。
否则评测只能看到最终回答。
Audit Ledger 可以把:
失败 Tool
错误 Evidence
审批
上下文版本
直接转成 Replay Dataset。
这让生产事故真正反哺 Eval。
五十四、和 Multi-Agent 的关系
多 Agent 最难审计的是:
谁做的决定
谁只是提供信息
谁真正执行了副作用
Ledger 中分别记录:
DECISION_ACTOR
EVIDENCE_PRODUCER
EXECUTION_ACTOR
不要全部归给 Supervisor。
五十五、一个完整 Explainable Execution View
业务界面:
目标
↓
关键事实
↓
决策
↓
审批
↓
真实动作
↓
结果
技术界面:
Run Manifest
Event Timeline
Model Calls
Tool Calls
Authority
Artifact Hash
Trace
安全界面:
Risk
Policy
Revocation
UNKNOWN
Anomaly
不同角色看不同 Projection。
五十六、上线检查清单
□ Audit Event Append-only
□ 每个 Run 有严格 Sequence
□ Event 使用 Hash Chain
□ 大 Payload 使用 Ref + Hash
□ Secret 不进入 Ledger
□ Decision 绑定 Evidence,不保存私有思维链
□ Tool Intent 先于外部执行
□ UNKNOWN 是正式状态
□ Idempotency Key 进入 Ledger
□ Approval 绑定 Action Hash
□ Authority Chain 可回放
□ Artifact 有完整 Provenance
□ Snapshot 不替代 Event History
□ Business + Audit 使用 Outbox 保证一致性
□ Replay 默认只读
□ Execution Replay 使用 Recorded/Sandbox Tool
□ Trace 与 Audit 分离但可关联
□ Retention 按数据类型配置
□ Ledger 定期做 Integrity Check
□ 用户可获得 Execution Receipt
□ 高风险 Tool Audit Coverage 100%
总结
Agent 越自治,越不能只留下最终答案。
生产系统真正需要的是:
谁提出目标
系统看了什么证据
根据哪些规则做了什么决定
谁授予权限
谁批准高风险动作
哪个 Tool 真正执行
外部系统最后发生了什么
产物来自哪些输入
这些信息组合起来,才叫:
Explainable Execution
它不是把模型内部推理全部展示给用户,而是把可验证的事实、权限、决策和副作用组织成一条证据链。
当一次自动操作可以被完整回放、对账、解释和举证以后,Agent 才真正具备进入高价值业务流程的基础。
下一篇继续推进:
生产级Agent(19):Agent SLO与运行可靠性——把成功率、成本、延迟和副作用一起纳入SRE。
更多企业级 AI 应用、Agent、RAG 与大模型工程化内容,我会继续整理在 智元界:
https://www.zyentor.com/