审计/链路追踪落地(可回放审计记录 FROZEN v1)(Refs #17)

issue #17 要求「每步 trace 可回放(谁/何模型/何工具/何审批)+ schema 冻结 + 测试」。
本仓已持久化事件流 + 任务谱系 + 每任务 usage(model_id) + 审批;本 PR 把它们规整为
统一、有序、可回放的审计记录并冻结 schema。

代码:
- orchestrator/audit.py(新,纯模块,无 Redis/WS/FastAPI/模型):build_audit_trace(
  events, task_facts, approvals, lineage) 逐事件产审计记录(who/when/model_id/tool_count/
  approval{id,decision}/result/lineage),audit_id 由序位确定(非随机,可字节级复现);
  replay(records) 产人读步骤行。无内容、无密钥、缺信号不伪造(model/tool 缺则 null)。
- orchestrator/main.py:build_audit_trace_for_run(装配器,从 list_events + 任务 usage +
  run.approvals 取数)+ 读接口 GET …/{id}/audit(三别名路由,复用既有鉴权)。

文档:docs/integration/audit-trace-schema.md → FROZEN v1:§3.1 回放装配、§5 冻结记录形;
诚实标注**有意排除**(prompt/代码原文、model 请求响应体刻意不留痕——无内容原则;
无 SK/MCP 工具层故无工具名谱系),按规则 #9 不伪造、不在本次扩展。

测试:scripts/test-audit-trace.py(纯模块 + 集成):逐步 who/model/tool/approval 重建、
result 归类、有序回放、**断言无 secret_ref/azkv/明文泄漏**;接入 CI。

影响范围:仅 agent_swarm(orchestrator 新增只读审计接口 + 纯模块 + 文档 + 测试 + CI)。
- Manager:新增只读 GET …/{id}/audit;不改回调/契约/计费/审批链。
- 密钥/审计:审计记录只含非内容元数据 + secret_ref 永不写入;归因按 user/channelId,不引入 tenant。

Refs #17

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Songhaoz666
2026-06-10 18:06:50 +08:00
co-authored by Claude Opus 4.8
parent 8b5eea296a
commit af4ace4340
5 changed files with 392 additions and 17 deletions
+33 -17
View File
@@ -1,6 +1,8 @@
# 审计与链路追踪 Schema(Audit / Lineage / Trace)
> 状态:**DRAFT / 待对齐 Audit & Compliance Team**。
> 状态:**FROZEN v1(可回放审计记录已落地)** —— 回应 issue #17。每步 trace(谁/何时/何模型/何工具/何审批/结果/lineage)可由 `orchestrator/audit.py` 的纯装配器从已持久化的事件流 + 任务谱系 + 审批重建并按序回放;读接口 `GET …/{id}/audit`。契约测试 `scripts/test-audit-trace.py`。
>
> **诚实边界(规则 #9,本次不扩展)**:审计记录**只含非内容元数据**——prompt 原文/代码/对话**刻意不留痕**(与回调脱敏同源原则);model trace = `model_id` + token 计数(不留请求/响应体);tool trace = 文件/Git 资源动作计数(本运行时**无 SK/MCP 工具层**,故无工具名谱系)。这些为有意取舍/后续层,不在本次冻结内。
>
> 依据:`heicode-mananger/docs/heicode.md §五/§九`、`docs/heicode-runtime-auth-newapi-secret-design.md §三`。配套:[`event-schema.md`](./event-schema.md)、[`security-boundary.md`](./security-boundary.md)。
@@ -40,36 +42,50 @@
| 回调投递审计 | `callback_attempts`、`/diagnostics` |
| 用量归因 | `usage` + `X-Agent-*` 归因头(见 usage-billing) |
| 脱敏 | `_redact_sensitive`(保留 `secret_ref`,脱敏明文密钥) |
| **可回放审计装配 + 回放** | `orchestrator/audit.py`:`build_audit_trace(events, task_facts, approvals, lineage)` 纯函数把事件流 + 任务(含 `usage.model_id`/工具计数)+ 审批规整为有序审计记录(§5 冻结形);`replay(records)` 产人读步骤行。读接口 `GET …/{id}/audit`(`build_audit_trace_for_run`)。**纯净、确定性、无内容/无密钥**。 |
## 4. 缺口
### 3.1 回放装配(issue #17)
`audit.build_audit_trace` 对每条事件产一条审计记录,按存储顺序排列,`audit_id` 由序位确定(`aud_000001…`,非随机,**同一 run 字节级可复现**)。逐步可答:
- **谁**:`actor.agent_instance_id`(事件携带)/ `actor.agent_role`(任务谱系回填)。
- **何模型**:`model.model_id`(取自任务 `usage.model_id`,缺则 `null`,不伪造)。
- **何工具**:`tool.count`(取自任务 `tool_calls`/`tool_count`;无工具层时为 `null`)。
- **何审批**:`approval.{approval_id, decision}`(取自 payload + `run.approvals`,**绝不含 `secret_ref`**)。
- **结果**:`result` 由 event_type 归类(completed→success / failed→failed / blocked|approval.rejected→blocked / stopped→stopped / 其余→info)。
- **lineage**:四 ID 全量。每条 `redacted: true`。
## 4. 覆盖与边界
| 追踪项 | 状态 |
|---|---|
| Task lineage / execution trace | ✅ 已实现(事件流 + 任务字段) |
| Approval trace | ✅ 已实现 |
| Prompt trace(每次模型输入提示留痕) | 🔴 未实现(仅记 `model_id`/用量,不留 prompt 原文) |
| Model trace(请求/响应、参数、provider) | 🟡 部分(`model_id`/tokens;无完整请求响应留痕) |
| Tool trace(工具调用谱系) | 🔴 未实现(无 SK/MCP 工具计量,`sk_tool.*` 仅在 schema 预留) |
| 统一审计查询字段 / 留存策略 | 🟡 事件可查;标准查询字段与留存期未定义 |
| 交付回放(replay) | 🟡 事件流可顺序回放生命周期;无独立 replay 接口/快照格式 |
| Approval trace | ✅ 已实现(审计记录 `approval.{id,decision}`) |
| Model trace(`model_id` + token 计数) | ✅ 已实现(审计记录 `model.model_id`;无请求/响应体——见下「有意排除」) |
| Tool trace(计数) | ✅ 计数已实现(`tool.count`);工具名谱系 N/A(无 SK/MCP 工具层) |
| 统一审计记录 schema + 回放 | ✅ 已冻结(§5)+ 可回放(`audit.build_audit_trace`/`replay`,`GET …/{id}/audit`) |
| **有意排除(非缺口)** | Prompt/代码/对话**原文**——刻意不留痕(无内容原则);model 请求/响应体——同上;SK/MCP 工具名谱系——无工具层。这些属取舍/后续层,按规则 #9 不伪造、不在本次冻结。 |
| 留存期 / 合规归档策略 | 🟡 由 Audit & Compliance Team 定(留存期、外部归档),非本仓编排器。 |
## 5. 建议审计记录 Schema(草案,待 Audit Team 冻结)
## 5. 审计记录 Schema(FROZEN v1 —— `orchestrator/audit.py` 产出形)
```jsonc
{
"audit_id": "aud_...",
"audit_id": "aud_000001", // 序位确定(非随机),同一 run 可复现
"occurred_at": "2026-06-08T...Z",
"actor": { "user_id": "...", "channel_id": "..." }, // 谁(按 user/channelId,非 tenant)
"subject": { "agent_role": "...", "agent_instance_id": "...", "task_id": "..." }, // 哪个子 Agent / 任务
"action": "task.completed | approval.decided | resource.accessed | ...",
"resource": { "type": "git|database|storage|model", "ref": "non-secret-id", "secret_ref": "azkv://..." },
"approval_id": "approval_... | null",
"action": "task.completed", // = event_type
"actor": { "agent_role": "impl", "agent_instance_id": "agent-7" }, // 谁
"task_id": "swarm-...-t | null",
"model": { "model_id": "gpt-x" } | null, // 何模型(缺则 null,不伪造)
"tool": { "count": 2 } | null, // 何工具(计数;无工具层为 null)
"approval": { "approval_id": "approval_...", "decision": "approved|rejected" } | null, // 何审批(无 secret_ref)
"result": "success | failed | blocked | stopped | info", // 结果
"lineage": { "manager_deployment_id": "...", "deployment_id": "...", "swarm_id": "...", "correlation_id": "..." },
"result": "success | failed | blocked",
"redacted": true
}
```
> 归因主轴为 `user.id`/`channelId`(见 usage-billing),**不引入 tenant**;actor 在审计记录中以 `agent_role`/`agent_instance_id` 表达执行主体,用户主体由 lineage→Manager 侧关联。
## 6. 待对齐对象
Audit & Compliance Team:prompt/model/tool lineage 是否强制留痕及留存期、统一审计查询字段、交付回放快照格式、日志留存与合规要求;与 `telemetry-architecture`(benchmark 目录)数据源对齐。
Audit & Compliance Team:留存期与外部归档策略、合规要求;prompt/model 请求体留痕是否在他处(受隐私/安全约束,本仓默认不留);与 `telemetry-architecture`(benchmark 目录)数据源对齐。