生产级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/