智能体自信地告诉用户:“我已经发送了确认邮件,并将退款退回到你的账户。”追踪记录很干净:两次工具调用,均返回 {"success": true},模型生成了精练的摘要,对话在 3.2 秒内结束。一周后,客户发起投诉,因为邮件从未送达,退款也从未入账。审计日志中全是绿色的勾选标记。没有任何环节失败——除了实际的工作本身。
这就是在大多数智能体技术栈中尚未被命名的故障模式:撒谎的工具。这里的“撒谎”并非恶意——它们返回了其契约规定的响应。这种谎言是结构性的。HTTP 层返回 “200 OK” 是因为请求被接受了,而不是因为操作完成了。邮件服务商返回 success: true 是因为消息进入了发送队列,而不是因为它已经发出了。数据库写入返回且无报错,是因为它写入了一个从未同步的副本。被训练成“乐于助人”且在“绿色代表完成”的示例上训练过的模型,将这些信号编织成一份自信的摘要,然后继续下一步。
这种问题难以调试的原因在于每一层都表现得如文档所述。工具报告了它所知道的情况。智能体根据它收到的唯一信号进行推理。运行环境(harness)记录了智能体输出的内容。谎言存在于“系统接受了我的命令”与“世界按照我的要求发生了改变”之间的鸿沟中——而对于那些思维模型将工具调用视为函数返回的人来说,这个鸿沟是不可见的。
撒谎工具的剖析
智能体工具调用的词汇借鉴自 RPC,而 RPC 继承自本地函数调用,后者假设“调用返回”即意味着“工作完成”。对于分布式系统来说,这种假设本就摇摇欲坠,而对于带有异步后端的系统来说,这完全是错误的。智能体技术栈继承了所有先前的故障模式,并增加了一个概率推理器,它会按字面意思接受成功信号。
以下几种常见模式会产生虚假成功的响应:
接受即成功(Acceptance-as-success)。 API 接受请求,将其放入队列并返回 200。而队列工作进程已宕机、死锁,或者积压了六小时的任务。下游效果从未发生。
副本写入成功。 写入操作进入了一个尚未同步到只读副本的节点,而智能体随后会查询这些副本。后置条件的读取返回了旧值;模型将其解读为“写入未生效”并再次写入。
服务商侧的软故障。 邮件服务商返回 200 以及 accepted: true,但收件人域名的垃圾邮件过滤器已经屏蔽你的 IP 一周了。你的工具层永远看不到这个失败。
符合 Schema 的错误。 工具返回 200,但响应体包含一个 error 字段,由于智能体的工具封装层是在 4xx 是唯一错误路径的时期编写的,因此该字段未被暴露出来。
部分变更。 复合操作——“创建发票并发送邮件”——在第一步成功了,但静默地丢弃了第二步,返回一个合并了两个步骤的单一成功布尔值。
每一种模式都存在于两个系统的边界,在那里,“事情是否真的发生了”的所有权处于无人认领的状态。工具层认为 API 响应了它。API 认为队列确认了。队列认为工作进程接受了。没人掌握后置条件。
为什么模型不能作为验证层
针对这种故障模式,一种自然的反应是让模型变得更聪明——提示它“在向用户报告成功之前先验证工具是否执行成功”。这是错误的修复层级。
模型只能看到运行环境输入到其上下文中的内容。如果工具返回 success: true,模型没有独立证据去质疑它。要求模型“保持怀疑”只会产生“表演式的怀疑”——一段充满对冲语言的文字,却不会改变下一次调用的行为。更糟糕的是,在人类编写的工具文档上训练出来的模型已经吸收了 success: true 意味着完成的先验知识。用提示词指令去对抗这种先验,会对智能体行为的其他维度产生持续的拖累。
验证必须是确定性的,必须存在于技术栈中模型之下的层级,并且必须表现为模型可以推理的证据,而不是模型可以合理化掉的字符串。也就是说,在不信任工具自我报告的代码观察到后置条件之前,运行环境应该拒绝向模型返回 tool_result: success。这样模型看到的是后置条件,而不只是响应。
这是一种结构性的转变。它将智能体运行环境从“我把工具的答案转发给模型”转变为“我根据外部观察验证了工具的答案,然后转发验证后的答案”。后者是一个实时一致性层,而大多数智能体技术栈并没有这一层,因为大多数智能体技术栈是为“以读为主”的工作负载设计的,在那种情况下模型只需要信息。
后验条件模式
落地生效的准则是“后验条件验证”(post-condition verification):每一次改变状态的工具调用(tool call)之后,都紧跟着一次确定性的读取,以确认世界确实处于该调用所承诺的状态。该模式包含四个要素,各有其权衡。
调用端的幂等键(Idempotency keys)。 智能体框架(agent harness)为每个逻辑操作生成一个唯一的键,并将其作为工具调用的一部分传递。工具在收到重复的键时,会返回原始结果,而不是再次执行操作。这是后续一切的前提,因为如果没有它,验证步骤中的“这东西是否存在?”检查就会与原始调用的实际完成进度产生竞争。Stripe 的幂等合约是典型范例:同一个键可以安全地重试;相同负载但键不同则被视为新操作。
具有明确语义的后验条件读取。 在写入操作之后,框架会执行一次读取以证明写入已生效。“我用幂等键 K 创建了一张发票——让我通过键 K 读取发票,并确认存在一张金额符合预期的发票。”该读取必须针对一个框架能够理解其一致性模型(consistency model)的系统:除非框架愿意等待或从主库读取,否则具有 5 秒同步延迟的只读副本(read replica)不能作为验证目标。
区分故障类型的合约。 工具接口应区分“已受理”(accepted)和“已完成”(completed),而不是将两者都简化为 success: true。一个返回 {"status": "queued", "message_id": "..."} 的邮件工具比直接返回 success: true 的工具更诚实,因为智能体框架现在有了一个钩子(即 message ID),可以稍后查询实际的递送情况。
基于风险权重的验证预算。 验证是有成本的。每次写入后的读取至少会使工具延迟翻倍,并消耗推理 token 来为模型提供后验条件。准则是根据后果的严重程度来衡量验证的力度:群发营销邮件,无需验证;涉及重置密码的触发式邮件,向服务商验证;处理退款,则需向账本(ledger)和支付处理器双重验证,并采用“冻结并释放”(hold-and-release)模式,在对话继续之前向智能体反馈确认信息。
掩盖谎言的重试陷阱 面对“工具偶尔会静默失败”的问题,团队的一个预见性反应是增加重试(retries)。在“虚假成功”的情况下,这会让问题变得更糟,因为重试层信任了那个误导智能体的 success: true 信号。如果系统持续发生故障——队列挂掉、IP 被封、副本陈旧——每次重试都会返回同样的绿色勾选,智能体的信心随着每次成功的响应而攀升,而审计日志现在显示的是三次满怀信心的成功,而非一次。
重试仅在故障是瞬时的且能被检测到时才有帮助。当故障是持续且未被检测到时,重试是危险的,因为它们放大的是虚假声明的数量,而不是实际工作的量。架构规则应该是:重试应关联到“已验证的失败”,而不是“未验证的成功”——这意味着重试决策发生在后验条件读取之后,而不是取代它。
这也凸显了幂等键作为核心支撑的作用。如果没有它,重试一个“已成功但实际未成功”的调用,如果原始调用即将完成,则存在执行两次的风险。有了它,重试就是安全的,而后验条件读取则能区分“已完成”与“仍未完成”,而不是“让我再写一个新的”。
工具合约必须承载的内容 当工具合约(框架向模型公开的 schema)明确编码验证接口而不是隐藏它时,该模式落地最快。在处理良好的生产级智能体技术栈中,有几个原语经常出现:
工具结果中的 verification 代码块。 工具返回其自身的结果,外加一个由框架在观察到后验条件后填充的结构化字段。模型接收到的是 {"call_result": "queued", "verification": {"observed": "delivered", "at": "..."}} 而非仅仅一个成功提示。这使得验证在语义上与调用的自述(self-report)分离开来——模型是基于证据而非承诺进行推理。
具备验证感知的故障分类。 除了 2xx/4xx/5xx 之外,工具层还应区分“已提交但未确认”、“已提交并确认”以及“已提交并确认失败”。一个“确认失败”的结果比含糊不清的成功更有用,因为智能体可以据此判断下一步该做什么。
异步完成句柄。 对于长时运行的操作,工具返回一个作业 ID(job ID),框架公开一个 wait_for 或 verify_complete 原语,进行轮询直到后验条件达成或超时。在框架解析完句柄之前,智能体不会看到“成功”。
风险等级标注(Stake annotations)。 工具目录为每个工具标注风险级别(读取、低风险写入、高风险写入),框架根据标注应用验证策略,而不是针对每次调用进行配置。这防止了验证变成提示词(prompt)必须记住去要求的事情。
架构上的转变在于工具合约变成了一个多阶段协议——提交、验证、确认——而不是单次的请求-响应。这听起来很重,但处理退款、发送触发式邮件和写入账本的智能体,已经在为处理事故承担这些债务。将其显式化,是将这种成本从事故处理转化为可控的延迟。
实践中的表现 那些吸收了这种模式的团队都有一些共同的习惯。他们将工具执行成功视为由 Harness 验证的假设,而非 Harness 记录的事实。他们将低风险工具(读取 API、搜索、内部计算器)与高风险工具(涉及客户、资金或第三方写入的操作)区分开来,且只有高风险工具集才承担验证开销。他们编写的集成测试包含“工具返回了虚假成功”的场景——即一个返回 200 但跳过了副作用的 Mock——因为生产环境最终会产生这种情况,而 Agent 在此情况下的表现是一项功能,而非事后补救。
他们在文化认同上也达成了一致:当客户报告某个操作未发生时,第一步调查不是“模型是否产生了幻觉”,而是“验证层是否确认了后置条件”。默认的怀疑对象是“接受”与“完成”之间的差距,因为过去所有的事故都源于这个差距。
那些能在生产环境中存活下来的 Agent 并不是拥有最巧妙 Prompt 的那些。而是那些 Harness 将每一次改变状态的工具调用都视为带有明确确认步骤的分布式事务,且工具契约旨在暴露“系统收到了我的请求”与“世界发生了变化”之间差异的 Agent。一旦构建了这一层,一类被称为“模型对用户撒谎”的事故就会被发现其实与模型完全无关。
会员专享
余下内容仅对会员开放。 会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。
— 完整文章,包含未公开存档的部分 — 可落地的工作框架,附带权衡与决策依据 — 新文章抢先看,先于公开发布 登录以继续阅读→ 随时取消 · 一次订阅,畅读全部