Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

工具收据

Tool receipts 是加密证明,用于表明一个成功的工具结果来自运行时。启用 receipts 时,成功的工具执行会收到一个针对调用及其结果的 HMAC-SHA256 摘要。该摘要会追加到工具结果文本中,并作为对话的一部分传回给模型。

实际结果是:模型无法令人信服地声称自己运行了一个它并未运行的工具,也无法伪造工具结果。这些声明会产生缺失或无效的收据。

威胁模型

LLM 是一个字符串生成器。默认情况下,没有任何机制阻止它叙述一个从未发起的工具调用(“我运行了 git log,最新的提交是……”),或为某个工具调用编造结果(“天气 API 显示 72°F”,而实际上该调用已超时)。对于具有自主性的智能体而言,这不仅仅是一个正确性问题,更是一个可推诿性问题。

工具收据通过最便宜的可能构造弥补了这一差距:使用带有临时内存密钥的对称 MAC。

这里的“ephemeral”是什么意思。 Channel-server 路径会在 channel 运行时上下文的整个生命周期内在内存中保留一个 HMAC 密钥。直接 agent turn 路径会为该轮创建一个新的 receipt 作用域。在这两种情况下,密钥都保留在内存中,从不发送给模型,也不是持久的跨会话验证器。

基于:Basu, A. (2026). “工具收据,而非零知识证明:AI 代理幻觉检测的实用方法。” arXiv:2603.10060

工作原理

  1. 当启用收据时,运行时会为活动收据作用域创建一个 256 位的内存密钥。Channel-server 路径将该密钥保存在 ChannelRuntimeContext 中;direct turn 路径则为该轮创建一个新的作用域。该密钥绝不会写入磁盘,绝不会发送给模型,也绝不会记录到日志中。

  2. 每次成功的工具执行之后,运行时会计算:

    receipt = HMAC-SHA256(key, tool_name || args || result || timestamp)
    
  3. 收据以如下方式附加到工具结果文本中:

    [receipt: zc-receipt-<timestamp>-<base64url-digest>]
    
  4. 工具结果(包含收据)被反馈给模型。

模型可以看到其对话历史中的每一个回执。它可以在生成给用户的文本中复述这些回执,但它无法生成一个_新的_有效回执:HMAC 需要会话密钥,而模型并不持有该密钥。

收据形状

zc-receipt-1774608496-gzpEBuUIRYX1vd4fQl4oYkqhq4-GnoJDStmlYzvQiWA
          ^ epoch seconds     ^ base64url(HMAC-SHA256 digest)

zc-receipt- 前缀的存在是为了防止泄漏检测器将其删除(收据可以安全地显示,因为它们不包含任何敏感信息)。

收据检测

场景没有收据带有收据
模型声称它运行了一个工具,但实际上没有。无法检测无收据:可见伪造痕迹
模型为真实调用伪造了结果无法检测验证时 HMAC 不匹配
模型拒绝它所进行的一次成功调用无法验证对话中的收据证明已发出签名结果
模型伪造了一个看似合理的收据字符串合理的HMAC 验证失败

收据无法做到的事情

  • 不要限制文本输出。 模型仍然可以输出与任何工具调用无关的内容。
  • 不要强制使用工具。 收据仅在调用工具时生成;它们无法帮助解决“模型在应该查找某些内容时却依靠已有知识回答”的问题。
  • 不要涵盖被阻止或失败的调用。 审批拒绝、超时、被阻止的调用以及失败的工具返回属于可观测性或审计事件,而不是带有收据的工具结果。
  • 不要跨越 receipt 作用域进行传递。 receipt key 是短暂的,因此在某个作用域下生成的 receipt 在该作用域消失后无法再验证。
  • 不要在一个通道运行时上下文中将通道或对话彼此隔离。 该上下文中的通道-服务器对话共享密钥。威胁模型针对的是运行时内的 LLM 伪造,而不是跨通道伪造。
  • 请勿扩展到后台或分离的委托派生任务。 从用户回合中分离出来的后台和并行委托派生任务(background: true)不会在用户可见区块中显示回执,因为按回合收集器是在这些派生任务完成之前渲染的。同步委托子代理内部的回执会被捕获。

查看收据

在调试日志中

sh

RUST_LOG=zeroclaw_runtime::agent=debug zeroclaw daemon

产生:

DEBUG Tool receipt generated tool=shell receipt=zc-receipt-1774604899-fVRG...

在用户可见的回复中

如果 [agent.tool_receipts] show_in_response = true,回复中将包含一个尾随块:

Here's the weather in Istanbul: 16°C, sunny.

---
Tool receipts:
  weather: zc-receipt-1774608496-gzpEBuUIRYX1vd4fQl4oYkqhq4-GnoJDStmlYzvQiWA

在 LLM 自己的输出中

由于模型在其上下文中能看到回执,它在描述工具结果时可能会原样输出这些回执。泄漏检测器被配置为让 zc-receipt-* 令牌不经修改地通过,以便这种原样输出能够正常工作。如果运行时和模型都包含了回执块,用户就会看到两个:可通过特定通道的格式化规则去除其中一个。

配置

安全属性

  • 临时内存密钥。 仅保留在活动收据作用域中:channel-server 路径使用 channel 运行时上下文,direct turn 路径使用每轮作用域。绝不持久化,绝不记录,绝不进入模型上下文。破坏长期存储毫无意义。
  • 标准 MAC 原语。 来自 Rust 生态系统的 hmac + sha2
  • 开销可忽略不计。 每次工具调用耗时 <1 毫秒。
  • 无需新的外部依赖。

哪些收据

  • 不是零知识证明。运行时可以验证收据,因为它持有密钥。第三方则无法做到。
  • 未与对话哈希交叉签名。篡改之前的对话不会使后续收据失效(收据仅涵盖其计算所针对的调用)。
  • 不是审批流程的替代品。收据仅证明某次调用已发生,并不能决定该调用是否应该执行。

当前状态

功能状态
成功工具结果的 HMAC 生成已发货
成功的工具结果已附加收据已发货
收据的调试日志已发货
show_in_response已发货
系统提示指令以回显收据已发货
收据的持久化审计数据库计划
跨作用域收据验证未计划(参见临时密钥设计)

另见