工具网关 III — 调用审计 + 网关合龙
工具网关 III — 调用审计 + 网关合龙
日期: 2026-09-24 阶段: Phase 4 - 自建 Agent 平台×求职冲刺 标签: #call-audit #observability #append-only
核心问题
Day 100 建了工具注册表(能发现/调用),Day 101 建了鉴权层(谁能调、用谁的凭证)。今天补第三层、也是合规系统的命脉——调用审计:每次工具调用都把 who / tool / 参数 / 结果 / 延迟落盘,让监管者能在事后重建 agent 干了什么。三件齐了,工具网关合龙。
三个问题:
- 一条工具调用审计记录要包含什么字段? 直觉记个「调了哪个工具」就行。但 2026 的审计 schema 要能支撑「15-30 次工具链的完整重建」,缺字段就重建不出来。
- 审计记录怎么存才算「监管可信」? 直觉是写进数据库。但合规要求 append-only + 哈希链——而且有一条反直觉的硬约束:写日志的 agent 不能读/删自己的日志。
- 审计层和 P1 已建的 OTel 映射层是什么关系? 不能再造一套 trace 系统——必须复用 day74 的四段 span 与
attributeMap,工具调用是其中execute_tool子 span。
关键内容
A. 审计记录 schema:能重建工具链的最小字段集
WebSearch 2026 审计实践(getMaxim/buildmvpfast),事实标准是 JSONL(JSON Lines,一行一条 JSON)——「every observability tool ingests and developers can read without special decoders」。一条工具调用审计记录的字段:
| 字段 | 来源 | 为什么不能省 |
|---|---|---|
caseId | 会话(=case.id,day74 关联键) | 监管按案件 ID 拉全链路 |
runId / step | 编排器(day29 orchestrator) | 工具链可能 15-30 步,靠 step 重建顺序 |
agentId | 鉴权层(day101 caller) | who——谁调的,身份归因 |
toolName | 注册表(day100) | what——调了哪个工具 |
args | 调用入参(可脱敏) | 重建「用什么参数调的」 |
decision | 鉴权层 allow/deny/hitl | 哪个 guardrail 触发、放行还是拦截 |
result / isError | handler 输出(可脱敏) | 结果是什么、是否失败(day100 双通道) |
latencyMs | 调用计时 | 性能 + 异常检测(延迟突变=可疑) |
tokenCost | 计量(day89 CostMeter) | 单位成本归因 |
timestamp | 调用时刻 | when——可重放排序 |
prevHash / hash | 哈希链(B 节) | 防篡改(监管可信) |
反直觉洞察①(审计的字段集由「能否重建」倒推,不是由「记多少算多」决定):直觉是「日志记得越全越好」。但真正的设计原则是可重建性——buildmvpfast 2026 口径:schema 要能「reconstruct the execution path when agents chain 15-30 tool calls per run」。这倒逼出两个常被漏掉的字段:
step(没有它,15 步乱序日志拼不回执行路径)和decision(没有它,看不出「这步被 guardrail 拦了还是放行了」)。少记step和decision,日志再全也重建不出 agent 的真实行为链。 对 AML,监管问「为什么这笔交易没上报 SAR」,答案就藏在某一步的decision: deny里——这是审计的核心价值,不是「记全」。
参数/结果的内容采集要可配置:2026 实践「support disabling content capture per environment while still recording tool name, server, latency, and status」。AML 场景对 PII 更严——args/result 里的客户标识默认脱敏(呼应 day74「prompt/completion 内容 opt-out」纪律),但 toolName/decision/latencyMs 始终全量记。
B. append-only + 哈希链:让审计「可证明未被篡改」
合规审计不能是「可改的数据库行」。2026 技术标准(thebrightbyte/devjournal):append-only 架构 + SHA-256 哈希链。每条记录的 hash = SHA256(prevHash + 本条记录内容),形成链——改任何一条,其后所有 hash 全断,篡改立刻暴露。
audit chain(每条 hash 锚住前一条,改一条断全链)
─────────────────────────────────────────────────
rec[0]: { ...payload, prevHash: GENESIS, hash: H0 = SHA256(GENESIS + payload0) }
rec[1]: { ...payload, prevHash: H0, hash: H1 = SHA256(H0 + payload1) }
rec[2]: { ...payload, prevHash: H1, hash: H2 = SHA256(H1 + payload2) }
│
改 rec[1] 的 payload ──┘ → H1 变 → rec[2].prevHash 对不上 → 验链失败
verify(chain):
h = GENESIS
for rec in chain:
if rec.prevHash != h: return TAMPERED at rec
if rec.hash != SHA256(h + rec.payload): return TAMPERED at rec
h = rec.hash
return OK
反直觉洞察②(写审计的 agent 不能读/删自己的审计——审计系统对它自己也是零信任):直觉是「agent 写日志,自然能读日志」。但合规审计的硬约束恰恰相反——devjournal 2026 口径:「logs... should be restricted so the agent that writes them cannot read or delete them.」原因:如果被 prompt injection 攻陷的 agent 能读/删自己的审计,它第一件事就是抹掉作案记录。审计系统必须假设「被审计者会试图篡改审计」——所以写权限和读/删权限物理分离(append-only:agent 只有 append,没有 read/update/delete)。这和 day101 「per-agent 最小权限」是同一个零信任原则的延伸:连「记录 agent 行为」这件事,都不能信任 agent 本身。 day75 的 immutable trail 已立此规,本日把它落到工具调用粒度。
延迟预算:审计会不会拖慢 agent?2026 基准(Bifrost)「only 11 microseconds of overhead per request at 5,000 RPS」——审计开销可忽略。但本项目跑前端,审计是同步内存追加(无网络),延迟更不是问题。
C. 网关三件合龙:注册 + 鉴权 + 审计的调用流水线
三天的成果在 call() 这一个入口合龙,形成确定性流水线:
PlatformToolRegistry.call(callerAgent, toolName, args, ctx)
│
① [鉴权] auth.resolveCredential(callerAgent, toolName, ctx.userId) ← day101
│ deny → 记 audit(decision=deny) → throw FORBIDDEN(不进 handler)
│ hitl → 挂起等人审(day83)→ 记 audit(decision=hitl)
│ allow → 拿到 JIT 凭证,继续
▼
② [入参校验] validate(spec.inputSchema, args) ← day100
│ fail → 记 audit(isError, INVALID_PARAMS) → throw(协议错通道)
▼
③ [执行] t0=now; raw = handler(args, credential); latency=now-t0
▼
④ [出参校验] validate(spec.outputSchema, raw) ← day100
│ fail → result.isError=true(业务错通道,不接地坏数据)
▼
⑤ [审计] audit.append({ caseId, runId, step, agentId=callerAgent,
│ toolName, args(脱敏), decision, result(脱敏), isError,
│ latencyMs, tokenCost, timestamp, prevHash, hash }) ← 本日
│ → 同步映射为 OTel execute_tool 子 span(复用 P1 attributeMap)
▼
返回 ToolResult
审计层不自己造 trace 系统,而是把每条审计记录映射成 day74 四段 span 里的 execute_tool 子 span:
| 审计字段 | OTel 映射(复用 attributeMap) | 命名空间 |
|---|---|---|
toolName | gen_ai.tool.name | OTel GenAI semconv |
latencyMs | span duration | OTel 核心 |
tokenCost | gen_ai.usage.* | OTel GenAI semconv |
agentId | gen_ai.agent.id | OTel GenAI semconv |
caseId | gen_ai.conversation.id | OTel GenAI semconv |
decision / hash | aml.tool.decision / aml.audit.hash | aml. 私有*(OTel 无对应,自管稳定性) |
这正是 day74 反直觉洞察②的应用:最该稳定的合规字段(decision/hash)恰恰是 OTel 没标准化的,放进自管的 aml.* 命名空间,比依赖实验态的 OTel 更稳。AgentCore Observability 的对照:它把工具调用记为 execute_tool span,emit「OpenTelemetry (OTEL)-compatible format」,built-in metrics 含「latency, duration, token usage, error rates」——我的审计字段与之一一对得上,但 decision/hash 是 AML 私有扩展。
各方案对照:
| 维度 | 本项目审计层 | AgentCore Observability | 通用 JSONL 审计 |
|---|---|---|---|
| 格式 | 内存 JSONL + OTel 映射 | OTEL → CloudWatch | JSONL |
| 防篡改 | SHA-256 哈希链 | CloudWatch(不可改存储) | append-only + 哈希链 |
| 工具调用 | execute_tool 映射 | execute_tool span | 自定义 |
| 合规字段 | aml.* 私有(decision/hash) | metadata tagging | 自定义 |
| 写读分离 | append-only(agent 不可读删) | IAM 控制 | 基础设施级 immutable |
设计要点/决策表
| 要点 | 决策 | 理由 |
|---|---|---|
| schema 设计原则 | 由「能否重建 15-30 步工具链」倒推字段 | 缺 step/decision 则重建不出行为链 |
| 内容采集 | toolName/decision/latency 全量;args/result 默认脱敏 | PII 合规(呼应 day74 内容 opt-out) |
| 防篡改 | append-only + SHA-256 哈希链 | 改一条断全链,监管可证明未篡改 |
| 写读分离 | agent 只能 append,不能 read/update/delete | 假设被审计者会试图篡改审计(零信任) |
| trace 复用 | 审计映射为 execute_tool span,复用 P1 attributeMap | 不造第二套 trace;合规字段走 aml.* 私有 |
| 网关合龙 | 鉴权→入参校验→执行→出参校验→审计 五步流水线 | 三天成果在 call() 一个入口收口 |
对本项目的落地
- 新建
src/agent/platform/gateway/callAudit.ts:导出AuditRecord(A 节字段)、AuditChain(append(record)算hash=SHA256(prevHash+payload)、verify()验链、无 read/delete 方法——写读分离落到 API 层面)、redact(args)(PII 脱敏)。哈希用确定性纯函数(复用或仿semanticCache.ts的 djb2 风格,诚实标注「教学用非加密级 hash,生产用真 SHA-256」)。 PlatformToolRegistry.call合龙:把 day100 的注册表call改造成 C 节五步流水线——调用 day101 的auth.resolveCredential(①)、validate入/出参(②④)、callAudit.append(⑤)。这是 day99 工具网关「注册+鉴权+审计三合一」的最终落地入口。- 接 P1 OTel 映射层:
callAudit的每条记录经src/aml/observability/attributeMap.ts的toAuditAttributes映射为execute_toolspan 属性(gen_ai.tool.name+aml.tool.decision/aml.audit.hash)。扩展而非新建 attributeMap——AML_ATTR加TOOL_DECISION/AUDIT_HASH两个常量。与 day74 四段 span 的execute_tool子 span 对齐。 - 诚实标注:v1 审计链是进程内内存数组(无持久化后端、无真实 KMS/CloudWatch);hash 是教学用确定性 hash,非加密级;写读分离体现为「
AuditChain类只暴露append/verify,不暴露get/delete」——真实部署需基础设施级 append-only 存储(WORM/对象锁)+ IAM 写读分离。retention(EU AI Act Article 12 ≥6 月)是运营策略,v1 仅记字段不实现归档。
参考资料
- AWS — Observe your agent applications on Amazon Bedrock AgentCore Observability(官方文档):trace/debug/monitor,
invoke_agent→chat/execute_toolspan 树;emit「OpenTelemetry (OTEL)-compatible format」→ CloudWatch;built-in metrics「session count, latency, duration, token usage, error rates」;metadata tagging (2026) - buildmvpfast — AI Agent Logging & Audit Trails (2026):JSONL 一行一条;schema 含 run_id/step/tool/input/output/latency_ms/token_cost/timestamp;「reconstruct execution path when agents chain 15-30 tool calls」 (2026)
- thebrightbyte — AI Agent Audit Trail Architecture: HIPAA, GDPR, DORA:append-only + SHA-256 哈希链是技术标准;基础设施级 immutability;EU AI Act Article 12 ≥6 月留存 (2026)
- devjournal — Security Audit Logging for AI Agents: Making What Your Agent Did Provable:日志须 immutable;「the agent that writes them cannot read or delete them」;写读分离 (2026-05)
- getMaxim — AI Agent Audit Logs / MCP Audit Logs:每次工具调用记 timestamp/input/output;guardrail 触发记 allowed/blocked;内容采集 per-environment 可关 (2026)
- 本仓库
src/aml/observability/attributeMap.ts(P1 OTel 映射层,AML_ATTR 扩展点)、docs/aipa/day74-audit-trail-otel.md(四段 span + execute_tool 子 span)、docs/aipa/day75-immutable-trail.md(immutable trail)、docs/aipa/day100-tool-registry.md/day101-gateway-auth.md(网关前两件)、src/agent/orchestrator(runId/step 来源) (2026-06)
SOTA 检查 (2026-06-11)
- 「OTEL execute_tool span + append-only 哈希链」是 2026-06 agent 审计的事实标准:AgentCore Observability、Datadog LLM Observability、Langfuse 都以 OTel GenAI semconv(截至 v1.41 定义 agent/workflow/tool/model span + latency/token 指标)为基础;合规侧 append-only + SHA-256 链是 HIPAA/GDPR/DORA 通用要求。本笔记复用 P1 映射层对齐之。
- EU AI Act Article 12 的留存与可重建是 2026 的硬约束:高风险系统 ≥6 月留存、2026-08-02 起强制——AML Copilot 正是高风险场景,审计可重建性(反直觉洞察①)直接服务于此。
- 「写审计的 agent 不能读删审计」是 2026 升温的零信任共识:多家 2026 文章把它列为审计架构铁律,正因为 prompt injection 攻陷的 agent 会试图抹除作案记录(反直觉洞察②)。本项目用
AuditChain只暴露 append/verify 落地此原则。 - 过时认知警示:「审计记个 tool name 就够」过时——缺 step/decision 重建不出工具链;「日志写进可改数据库」过时——合规要求 append-only + 哈希链 + 写读分离。
- 待跟踪:OTel GenAI semconv 从 Development 转 Stable 后
gen_ai.tool.*属性名是否变(届时只改 attributeMap 一处);EU AI Act Article 12 实施细则对「工具调用审计」的具体字段/格式要求;AgentCore Observability 是否公布其execute_toolspan 的完整属性 schema 以对标aml.*私有扩展;P4 后续是否把内存审计链替换为可复刻的 WORM 存储装置。