# 不保存提示词，如何审核 AI 智能体操作

自主智能体的审核记录，应当能够解释一次外部操作，同时不能变成一份同样缺乏保护、却保存了智能体全部工作记忆的副本。这意味着要记录谁运行了智能体、哪项权限批准了这次运行、操作跨越了什么边界，以及最后发生了什么。它不意味着要永久保存每条提示词、工具消息、草稿区内容和 API 负载。

团队通常会先从对话记录开始，因为它们容易捕获，也让人觉得完整。但一旦发生事故，记录中可能包含客户数据、源代码、误粘贴的令牌、推测性的指令，以及与待审核操作毫无关系的大量内容。与此同时，审核人员仍然无法回答最基本的问题：哪个可执行程序在谁的授权下，使用什么凭据范围，向哪个服务发送了什么请求，结果又是什么？

审核记录应当围绕操作建立，而不是围绕对话建立。这样既能减少暴露面，也能生成操作人员真正用得上的证据。

## 操作记录回答的问题不同于对话记录

操作记录回答的是：某个特定进程是否跨越了一个边界，以及外部系统如何响应。对话记录回答的是：哪些文本经过了智能体上下文。这是两种不同的产物，它们需要不同的访问权限、保留期限，也有不同的故障模式。

假设一个智能体阅读了一大段问题讨论，检查了代码仓库，起草了发布说明，然后调用 API 创建部署。对话导出可能包含数千行内容。而有意义的审核记录可以小得多：

- 进程身份和会话标识符。
- 覆盖该操作的授权决定。
- 目标、方法、资源类别和凭据引用。
- 结果，包括状态和安全的结果摘要。
- 时间戳和完整性数据。

这份记录足以让工程师还原运营事件：一个经过签名的进程在某个时间启动，获得了某个会话的批准，向指定的 API 主机和部署端点发送了 `POST` 请求，使用部署凭据，并收到成功状态和部署标识符。如果事件值得进一步调查，审核人员可以向目标服务的负责人请求更深入的证据。

对话记录或许能解释智能体为什么认为应该部署，但很少能证明它确实完成了部署。智能体上下文中的文字可能只是假设、已经过时、由模型编造，或者从未真正执行。外部操作拥有更狭窄、也更容易站得住脚的证据链。

只有在团队有明确保留理由时，例如质量评估或具体事故调查，才应将对话材料留在开发环境中。不要以问责为名，把这些材料偷偷放进审计系统。

## 完整提示词会制造一个没人计划保护的数据仓库

完整捕获提示词，会把操作日志变成高风险内容档案。这种风险并非理论上的，也不只涉及显而易见的机密。

提示词中经常包含源代码片段、客户工单、数据库输出、内部 URL、设计决策、Shell 历史记录和复制的错误文本。模型可能会在工具参数或错误解释中重复之前的上下文。API 也可能在错误响应中回显提交的标头或请求正文。如果日志记录器把所有字符串都当作无害诊断信息，最终一定会保留团队从未打算收集的内容。

常见的辩解是：“我们会把机密脱敏。”但这只有在日志路径能在存储前识别每一种机密格式，并理解每一种协议时才有效。它可能漏掉短时有效的签名 URL、会话 Cookie、专有令牌格式、嵌入 JSON 字符串中的机密，以及服务在错误中返回的凭据。广泛的访问者已经读过原始记录后，脱敏也无法恢复隐私。

OWASP 的 Logging Cheat Sheet 提出了一个有用的区分：日志应支持监控和调查，但系统不应记录访问令牌、密码、连接字符串、加密密钥，或那些收集后会造成不必要隐私暴露的数据。将这条建议应用到智能体审核时，更应严格，而不是放松。智能体上下文通常非常宽泛，因此比传统请求日志携带更多意外材料。

应当在捕获数据的地方进行最小化。保存请求指纹或经过批准的元数据，而不是保存生成请求的原始提示词。如果调查之后需要上下文，应按照来源系统适用的控制措施，从来源系统中获取。不要让审计日志成为浏览所有敏感对话最方便的地方。

有一个狭窄的例外：团队在诊断损坏的集成时，可能需要临时、严格受限地捕获内容。应将其设计为明确的诊断模式，并规定负责人、到期时间、访问限制和删除日期。如果诊断捕获因为没人关闭而变成永久捕获，那它从一开始就不是诊断捕获。

## 进程身份不能只停留在一个友好的进程名称上

进程名称并不能识别智能体。`agent`、`node`、`python` 和 `shell` 对审核人员几乎没有帮助，恶意程序或粗心的程序也可以使用这些名称中的任何一个。

应记录足够的身份数据，以区分发起会话的程序：

- 可执行程序路径和稳定的代码身份，例如操作系统支持时的签名机构。
- 进程 ID、父进程 ID、启动时间和会话标识符。
- 用户账户和本地主机标识符。
- 如果智能体客户端提供，记录客户端版本或构建标识符。
- 到达操作网关的传输方式，例如本地 stdio MCP 连接。

父进程很重要。由经过签名的编辑器启动的获批编程智能体，与一个未知 Shell 进程启动的复制二进制文件，即使两者使用相同的命令名称，也代表完全不同的审核情况。父进程身份不能证明意图良好，但它能为调查人员提供起点，也更容易发现简单的冒充行为。

不要把进程身份和人的身份混为一谈。开发者可能启动了智能体，但操作记录应分别说明两个事实：哪个本地账户启动了进程，以及哪个可执行程序持有会话。共享工作站、远程 Shell、服务账户和工具之间的交接，都让这种区分变得必要。

如果可用，代码签名机构应得到特别重视，因为操作系统可以在启动时验证它。但这仍然不是对发布者的道德判断。受信任的签名机构可能发布有缺陷的更新，而未签名的内部工具也可能是合法的。记录需要保留这一事实，以便审核人员将它与授权决定和预期部署路径进行比较。

## 授权需要范围、时间和批准人

没有范围的批准只是模糊的记忆，不是审核证据。记录必须说明批准了什么、批准从何时开始，以及何时不再适用。

对于开发者主导的智能体运行，会话授权通常是合适的默认选择。人员批准一个已知的智能体进程一次，进程就可以持续执行操作，直到退出。审核日志应将这一决定绑定到进程会话，而不是假装之后的每次调用都单独获得了人工判断。

对于风险更高的凭据，应记录单独的逐调用决定。该决定的记录需要包含批准人身份、时间、凭据引用、操作类别、目标，以及它所放行的确切调用标识符。像“用户已批准”这样的通用备注，在糟糕的操作发生后会留下太多争议空间。

一个可行的授权对象如下：

```json
{
  "authorization_id": "auth_7f3c",
  "kind": "session",
  "decision": "approved",
  "approved_at": "2025-03-08T14:22:31Z",
  "approver": "local-account:maya",
  "process_session": "sess_31a9",
  "process_identity": {
    "executable": "/Applications/Agent.app/Contents/agent",
    "signing_authority": "Example Development Team"
  },
  "scope": {
    "credential_refs": ["deploy-production"],
    "expires_when": "process exits"
  }
}
```

上面的名称和标识符只是示例，但结构很重要。批准人是一个本地账户身份，并不意味着某个指定人员逐行查看了所有输出。凭据引用是标签或内部 ID，绝不能是凭据本身。范围说明了这个决定覆盖的是整个会话还是单次调用。

除非运行环境确实需要，否则不要用庞大的策略语言解决这个问题。团队经常制定一些事故发生时没人读得懂的规则，然后把规则存在本身称为控制措施。少量清晰可见的授权选项，可能更容易审核，也更难配置错误。

## 请求元数据应描述跨越的边界

审核人员需要足够的请求元数据来了解操作范围，但不应收到操作的原始副本。对于 HTTP，应捕获服务主机、适用时的端口、方法、规范化路由模板、凭据引用、请求大小、关联 ID，以及规范化安全表示的摘要。

规范化路由模板意味着记录 `/v1/projects/{project_id}/deployments`，而不是记录包含客户标识符或类似不透明机密值的实际 URL。原始路由可以保留在目标服务中，因为目标服务已经拥有这些数据，也有自己的访问控制。

对于 SSH，相应的记录包括目标主机、经过验证的主机身份、远程账户、请求的命令或命令分类、凭据引用和退出结果。避免记录不受限制的命令输出。命令可能打印环境变量、私有仓库内容，或配置错误的脚本中的凭据。

HTTP Semantics，即 RFC 9110，区分了请求方法的语义，例如安全、幂等和不安全方法。将这种区分作为审核信号，而不是授权捷径。`GET` 可能泄露敏感数据。`PUT` 可以是幂等的，但仍可能替换生产配置。`POST` 可能造成无法撤销的外部影响。方法有助于审核人员理解操作，但真正的风险取决于目标和路由。

使用允许列表定义的元数据模式。不要先记录完整请求对象，再在之后删除字段。更安全的做法是先定义操作记录可以包含哪些字段，然后拒绝或转换其他所有内容。

```json
{
  "call_id": "call_c24e",
  "session_id": "sess_31a9",
  "channel": "https",
  "destination": "api.example.internal",
  "method": "POST",
  "route_template": "/v1/projects/{project_id}/deployments",
  "credential_ref": "deploy-production",
  "request_bytes": 842,
  "request_digest": "sha256:6d1d...",
  "started_at": "2025-03-08T14:24:09Z"
}
```

当审核人员拥有原始的获批表示时，摘要可以检测规范化记录是否发生变化。但它并不会让原始内容适合公开。低熵机密的哈希值也要谨慎处理，因为攻击者可以猜测并进行比对。不要给短令牌做哈希后就称其为脱敏。

## 结果需要运营证据，而不是响应转储

结果记录应说明目标报告了什么，以及网关是否完成了请求的操作。它不应默认保存完整响应正文。

对于 HTTP 操作，应记录传输是否完成、HTTP 状态、响应大小、耗时、在安全情况下记录服务请求 ID，以及经过明确选择的结果字段。例如，部署 API 可能返回一个适合保留的部署 ID，但完整 JSON 响应中可能包含环境变量和来自私有仓库的提交消息。

对于 SSH 操作，应保留退出代码、耗时、主机身份验证结果，以及由命令适配器选择的摘要。如果命令需要证明工作已成功完成，应让它输出受限的机器可读结果，例如 `{\"release\":\"r42\",\"status\":\"published\"}`。不要把任意终端记录当作审计结果。

这个区分在失败期间尤其重要。假设部署调用返回 HTTP 403，并在诊断对象中回显调用方的授权标头。智能体看到错误后重试两次，每次尝试都产生一条不同的日志记录。粗心的审核系统现在包含三份暴露的凭据副本，而且都被归入一个会有更多人打开的事故记录。

应先设计错误路径，再设计成功路径。操作网关应分类错误、移除不安全字段，并记录有界摘要。实用的类别包括网络失败、授权被拒、目标拒绝请求、目标超时和本地执行失败。类别应与状态码或退出代码等安全事实结合，而不是记录远程服务返回的自由文本块。

重试应有自己的字段。记录 `attempt`、`max_attempts`，以及指向原始调用的因果关联。否则，审核人员会看到三个破坏性请求，却无法判断是智能体有意重复，还是传输重试造成了重复。对于不安全操作，重试可能需要重新授权，或需要目标服务提供幂等机制。日志无法修复目标服务已经执行两次的操作。

## 审核模式应让禁止字段一目了然

在生产记录不断累积之前，模式审核可以发现日志记录错误。应把模式当作安全边界，明确允许哪些字段，也明确拒绝自由格式的提示词和负载字段。

下面的示例将身份、授权、操作元数据、结果和完整性信息组合起来。它有意不包含 `prompt`、`messages`、`headers`、`request_body`、`response_body` 和 `stderr` 字段。

```json
{
  "event_type": "external_action",
  "event_id": "evt_91bd",
  "occurred_at": "2025-03-08T14:24:10Z",
  "actor": {
    "local_account": "maya",
    "process_session": "sess_31a9",
    "pid": 4812,
    "parent_pid": 4601,
    "executable_digest": "sha256:2a84...",
    "signing_authority": "Example Development Team"
  },
  "authorization": {
    "authorization_id": "auth_7f3c",
    "mode": "session",
    "decision": "approved"
  },
  "action": {
    "channel": "https",
    "destination": "api.example.internal",
    "operation": "POST /v1/projects/{project_id}/deployments",
    "credential_ref": "deploy-production",
    "request_digest": "sha256:6d1d..."
  },
  "result": {
    "category": "completed",
    "status_code": 201,
    "destination_request_id": "req_18c7",
    "duration_ms": 614
  },
  "integrity": {
    "previous_event_digest": "sha256:8f50...",
    "event_digest": "sha256:bd7e..."
  }
}
```

不要在可能曾经存放机密内容的字段旁边放置“已脱敏”之类的注释。直接省略字段。一个存在但为空的 `request_body`，会诱使后来的开发者在调试时填入内容。模式验证应拒绝未知的顶层字段，也应拒绝嵌套数据块，除非该数据块的格式由经过审核的适配器负责。

审核人员还需要事件的可读视图。应从规范记录生成这个视图，而不是维护另一份手写说明。面向人的条目可以写成：“获批的进程会话使用 deploy-production，在 api.example.internal 创建了部署。服务在 614 毫秒内返回了 201。”日志保留检查事件所需的标识符，但默认视图不暴露多余材料。

## 完整性可以证明篡改，但不能证明完整

只有在团队理解其局限时，防篡改记录才有用。假设审核人员保留了验证所需的链材料，哈希链可以揭示某人在事件进入链后修改、删除或重新排序了事件。但它无法证明一个已经被入侵的日志记录器一开始就记录了某次操作。

NIST SP 800-92，即 Guide to Computer Security Log Management，建议组织保护日志完整性，定义哪些事件值得记录，并由明确的运营负责人审核日志。真正有用的是这几项的结合。没有明确的事件边界，完整性只能给你一份可信但不完整的故事。没有完整性，一长串事件列表只是一个别人可以悄悄修改的故事。

应将操作网关作为观察点，因为它能看到凭据使用和外部调用。如果智能体可以绕过网关，使用复制的凭据直接发起调用，那么审计轨迹只覆盖合作路径。应解决凭据分发问题，而不是声称日志可以看到一切。

验证应独立于日常日志阅读。验证命令应能针对存储的加密记录运行，并报告链是否完整。Sallyport 通过 `sp audit verify` 提供这项检查。它可以验证加密且采用哈希链的审计日志，无需保险库密钥。这个设计很重要，因为调查人员应能检查记录连续性，而不必获得凭据访问权限。

验证结果需要明确的运营处理方式。如果链检查失败，应保留受影响的存储，不再把该日志视为完整证据，找出第一个断裂的序列点，并比较这段时间内目标服务一侧的记录。不要简单地重新生成一条干净的链然后继续。那会把可检测的完整性故障变成无法回答的空白。

## 访问权限和保留期限决定日志会不会变成下一次泄露

即使记录很精简，如果太多人可以永远搜索它，仍然可能造成伤害。授权历史可能暴露员工活动。目标名称可能泄露基础设施信息。项目标识符可能暴露商业工作。访问权限应根据调查角色限制，而不是根据普遍好奇心开放。

将运营视图与取证视图分开。大多数工程师只需要近期操作列表、状态、目标和会话身份，就能诊断一次失败的运行。更小范围的人员在事故期间可能需要事件摘要、签名信息、批准详情以及原始加密记录访问权限。操作智能体的人员，并不会因此自动获得永久查看其他开发者历史记录的权限。

应通过回答两个问题来设置保留期限：团队实际需要多长时间调查一项有争议的操作，以及目标服务保留自己的权威记录多长时间？如果目标服务只在短期内保留部署历史，就应保留足够长时间的操作元数据，以便与之关联。如果法律或合同要求改变了期限，应记录这项要求及其负责人。“全部保留”通常意味着根本没有做出决定。

删除同样需要证据。记录保留策略版本，以及已执行计划删除或聚合这一事实。不要为了证明删除而继续保留已删除的内容。进行长期趋势分析时，可在详细记录过期后，按操作类别和结果聚合计数。

## 在网关构建记录，然后测试错误路径

最安全的实现方式，是在持有凭据并执行操作的地方捕获审核数据。智能体应通过受约束的接口请求操作，网关负责验证本地进程并执行授权，然后由网关执行 HTTP 或 SSH 操作。智能体收到结果，审核记录则捕获操作事实。

Sallyport 为支持 MCP 的智能体采用了这种结构：本地 `sp mcp` shim 将 HTTP 和 SSH 操作通过应用转发，应用的加密保险库将 API 和 SSH 凭据隔离在智能体上下文之外。它的会话日志和活动日志都从同一个不可写入的加密审计日志中生成，因此会话授权和单个调用能够保持关联，同时无需让智能体持有机密。

应使用普通成功路径演示通常会避开的故障来测试设计：

1. 发送一个请求，让目标服务在失败后回显一个伪造的授权标头。确认记录保存的是错误类别和状态，而不是标头或响应正文。
2. 启动两个显示名称相同、可执行程序身份不同的进程。确认审核视图能区分它们的会话。
3. 批准一个会话，终止它，然后启动新的进程。确认旧批准不会应用到新的运行过程。
4. 强制触发超时并重试。确认记录将各次尝试关联到同一个调用，并保留结果不确定这一事实。
5. 修改一条存储的测试事件并运行完整性验证。确认验证器报告失败，并且团队拥有书面响应流程。

在添加仪表板、摘要或模型生成的解释之前，先完成这些测试。再精美的活动信息流，也无法弥补会泄露令牌，或无法区分不同可执行程序的事件记录。

当审核记录足够精简、便于保护，足够具体、便于调查，并且锚定在外部操作实际发生的位置时，人们才会信任它。如果一条记录无法告诉你谁执行了操作、什么权限覆盖了它、它跨越了什么边界，以及返回了什么结果，就需要补充字段。如果它包含完整对话，那它包含得太多了。
