Agent说完成了,数据库却不同意
一个客服 Agent 查了订单、看了物流、读了退款政策、创建了工单,还给用户回了一句礼貌的已处理。
九次工具调用,轨迹干净得像一次标准演示。
可数据库里,那个本该等待物流异常解除的工单,被它关成了已解决。用户真正的问题也没有被回答。
这不是我编的反面案例。Microsoft 和 Hugging Face 刚发布的 ThinkingBox-Bench 里,就有这样一道零售客服任务。它戳中的地方很刺眼。我们这半年做 Agent,太容易把「模型把活干完了」等同于「系统完成了正确的状态变化」。这两件事,差得远。
我今天想聊的不是又一个榜单,而是这套评测背后那个应该被工程团队拿走的判断。Agent 的最终回复只是自述,系统留下的状态才是证据。
一条看着顺的轨迹,为什么还是错了
做过 Java 或 Spring 服务的人,对这类错误其实不陌生。
一个支付接口返回 200,不代表账已经记对。一个事务没有抛异常,不代表库存、优惠券、订单状态都满足业务约束。你会查数据库,会看事件表,会核对幂等记录。没有人会因为日志里打印了「扣款成功」就直接给财务结账。
可轮到 Agent,很多团队突然变得很宽容。
我们看它有没有调用工具。看最后一句是否自然。再高级一点,看 trace 是否完整。评测过了,就准备接进工单、CRM、审批流。
坦率地讲,这套验收方式有点像只看 Controller 返回值,不看事务最终提交了什么。Demo 时很好看,上线后会很别扭。
ThinkingBox 的做法并不神秘。每一道任务都有初始后端状态、用户目标、可调用的 MCP 工具、领域规则和对终态的可执行断言。Agent 在隔离环境里工作,评测器不要求它走某一条固定路径,只问一件事,结束时留下的记录和副作用是不是对的。
比如该把工单改成 on hold,不能改成 solved。该创建退款记录时必须创建。不能多给一笔补偿。用户必须被告知某条政策不保证什么时,回复里也得有。
这套思路很像数据库测试里的 post-condition。过程可以不同,终态必须满足不变量。
这句话听上去朴素,落到 Agent 上却很容易被忽略。因为语言模型特别擅长把过程讲得像已经完成了。
507 个工作流,把「像完成」和「真完成」拆开了
ThinkingBox-Bench 一共放了 507 个有状态业务工作流,覆盖零售与电商、旅行与酒店、车险、数字银行支持、咨询公司的 IT 与 HR 支持。每次运行都从相同的干净后端状态开始,任务通过 MCP 工具与环境交互,最终由确定性的检查器核验数据库终态和副作用。
这里有两个设计我很喜欢。
第一个是隔离。两次尝试不共享数据库行,也不共享工具缓存。你今天偶然把状态弄对,不能给下一次留后门。
第二个是重复。每个任务不是跑一遍,而是独立跑 20 次。
这一刀很关键。
行业里常用的 pass@k 很适合回答「它能不能至少解出一次」。可如果你把 Agent 放在一个会写真实数据的流程里,用户更关心的是「它会不会这次正确、下次又变卦」。前者是能力上限,后者才接近可靠性。
论文给了两个很有杀伤力的数字。Claude Opus 5 的 pass@1 是 66.50%,20 次都通过的任务占比是 47.53%。Kimi-K3 的 pass@1 是 57.37%,对应的 20 次一致通过率只有 17.60%。
不是模型完全不会做。恰恰相反,它们经常能做对一次。
问题在于,偶尔找到正确路径和稳定地走完正确路径,中间隔着一整套工程问题。
Hugging Face 发布页把这个差异拆得更细。Kimi-K3 至少成功过一次的任务有 476 个,占 507 个任务的 93.89%。听起来很能打。但它 20 次全过的只有 68 个任务。Claude Opus 5 至少成功一次的覆盖面更低,却有 241 个任务连续 20 次通过。
你如果只看一眼排行榜,可能会选前者。你如果要让它修改客户账户、发起赔付、创建员工权限,答案未必一样。
真正麻烦的失败,不会报错
我原先也以为,Agent 的大头失败应该是推理走偏,或者工具调用格式错了。ThinkingBox 给出的证据没这么简单。
在一组包含 121,680 次有效尝试、覆盖 12 个模型的分析里,有 79,853 次没有通过可执行检查。更让人头大的是,其中 67.24% 的失败尝试是正常结束的,调用过会改状态的工具,最后也没有工具报错。
它们看起来都像成功。
但终态检查发现,77.61% 存在错误字段值,43.30% 有不该发生的额外副作用,25.36% 漏掉了必须发生的副作用。这几个比例会重叠,它们不是三类互斥错误。
这就解释了很多团队遇到的怪事。测试同学看 trace 会说「它明明查到了」。研发看日志会说「接口全通」。业务同学最后打开后台,却发现单子被关了、金额不对、权限多开了一层。
模型不是没努力,它只是没有把工作闭合到业务终态。
这话听着有点刺耳,但 Agent 的问题并不总在「聪不聪明」。更多时候,它卡在工具失败后的恢复、前置条件没有满足、查询为空时该不该继续、多个写操作该按什么顺序提交。
论文作者按可观察的轨迹给失败归因,工具使用问题占约 79.9%,错误状态更新占 10.3%,没有完整解决用户诉求占 7.0%,完全没有状态变更只占 2.9%。这不是严格的因果解释,却给了工程上很实在的优先级。
别急着换更大模型。先把工具协议、错误语义、重试边界和终态校验做好。
把 Agent 当成一个不稳定的分布式参与者
我觉得这里可以借一点分布式系统的视角。
传统服务调用里,我们不会相信一次 RPC 返回 success 就代表业务完成。网络可能超时,消费者可能重复,数据库可能局部提交,异步消息可能还在路上。所以才会有 outbox、幂等键、补偿事务、状态机和对账任务。
Agent 接入工具以后,也进入了同一个世界,只是失败形态更丰富。
它会读取到过期信息。会把自然语言里的约束漏掉一半。会在工具返回空结果时脑补原因。会在第七步忘记第三步拿到的用户偏好。甚至工具都调用成功了,参数组合也可能违反业务规则。
所以别把 Agent 编排理解成给模型接一串 function calling。它更像把一个不完全可靠、会解释自己行为的外部参与者,放进了你的业务状态机。
这个参与者有两个特点。
它不保证确定性。
它会写一段非常有说服力的执行总结。
后者尤其危险。人类对一段顺畅的解释有天然信任,日志和自然语言又常常互相印证,大家就不再追问那个最笨的问题,最终那一行记录到底是什么。
ThinkingBox 的价值,就在于把这个问题变成机器可判定的断言。
给 LangGraph 或 Spring 工作流补一条验收链
这篇论文不是让每个团队都去搬 507 道题回来跑满 20 次。它真正值得抄的,是评测边界。
如果你正在用 LangGraph 编排 Agent,我建议从三个很小的改动开始。
把最终状态写进图,而不是藏在回答里
不要只让最后一个节点输出自然语言答案。给每一个能改变外部系统的任务定义一个结构化 outcome,例如 ticket_status、refund_id、approval_state、side_effects。最终节点先读回目标系统,再生成给用户的文字。
顺序别反。
用户看到的句子应该是状态校验后的解释,不应该是状态校验的替代品。
在 LangGraph 里,这通常可以是一个专门的 verify 节点。它拿到预期状态、工具调用返回和数据库读模型。如果状态不一致,图回到 recover 或 human_review,而不是直接 END。
1 | plan -> execute_tools -> read_back_state -> verify |
这类图看起来多了一步,却会把最贵的事故挡在真正写入用户数据之后。
为每一个写操作定义不变量
别只写「创建工单成功」这种技术性断言。把业务的不变量写出来。
例如物流异常未关闭的订单,工单状态必须是 on_hold。补偿只能在客户分层和延误天数同时满足时创建。撤销权限后,权限表和审计表必须都出现对应记录。退款成功后,订单、支付、库存三处状态必须可对账。
Spring 的团队可以把它做成 service 层的后置校验,或在 Saga 的每个状态迁移后发出校验事件。Agent 只是调用者,不应该成为规则的唯一解释器。
我自己的感受是,能被代码表达的业务规则,就不要只留在 Prompt 里。Prompt 是沟通协议,不是数据库约束。
区分可重试、需补偿和必须停下的错误
很多 Agent 的重试策略只有一种,失败就再试一次。这个事儿我也踩过坑。网络超时可以重试,工具返回空列表也许应该追问用户,已经写入一半的数据则可能需要补偿。把三类情况混成统一 retry,往往会制造重复扣款、重复建单或更难查的脏数据。
一个实用的分类可以是这样。
| 情况 | Agent 可以做什么 | 系统要留什么 |
|---|---|---|
| 连接超时且没有写入证据 | 有上限地重试 | 请求幂等键和尝试次数 |
| 工具返回业务前置条件不满足 | 停止写入并说明原因 | 失败原因和待补信息 |
| 已发生部分副作用 | 进入补偿或人工审核 | 状态快照与补偿任务 |
| 终态和目标不一致 | 禁止对用户宣称完成 | 校验失败证据与 trace |
你想想看,这张表其实不是 AI 专属。它是成熟业务系统早就有的故障模型。Agent 的出现,只是逼着我们别再把它们藏在人工操作和经验里。
20 次重复,不该变成一项昂贵的仪式
看到 20 次,有人可能会觉得成本要爆炸。这个担心完全合理。
论文自己也没有把 20 当成神奇数字。它报告的是观测到的 20 次全过,不是统计学承诺。生产里该跑几次,取决于失败代价和任务频率。
给内部知识库整理标签,跑三次或五次就够发现大多数波动。给客户退款、改合同权限、触发生产部署,一次错误的代价高得多,20 次甚至只是最低限度的预演。还有一些不可逆操作,正确答案不是把评测次数加到 100,而是保持人审。
Hugging Face 的成本分析也给了一个现实提醒。单次成功最便宜的模型,未必是得到一个连续 20 次都可靠任务的最便宜选择。把成本分母从「成功一次」换成「稳定成功」,模型排序会变化。
这也是为什么我不太喜欢只讨论每百万 token 单价。对 Agent 来说,真正该算的是每个可验收业务结果的成本,再加上失败之后的补偿、排障和信誉成本。
一次看似便宜的错误,常常是最贵的路径。
ThinkingBox 也不是生产系统的免责卡
这套基准有边界,得说清楚。
它的任务是对真实企业模式的合成重建,客户和业务数据不是真实生产数据。覆盖了五类业务领域,不代表能代替你的支付链路、风控规则、权限模型。基准中的可执行断言写得再好,也不会自动发现你没写进断言的新风险。
而且状态正确不等于所有事情都正确。隐私泄露、越权读取、糟糕的用户沟通、延迟和成本,都可能在终态正确时依然造成伤害。论文也为其中 30 个任务额外加了回复语义检查,剩余 477 个主要检查状态。这是一种有取舍的设计,不是一把万能尺。
不是说状态评测不够,而是它终于把最容易被漂亮回答掩盖的那一层补上了。
真正的生产验收,应该至少有四层。权限边界、过程审计、终态断言、用户可理解的结果说明。ThinkingBox 把第三层做得很扎实,正好适合成为我们现有监控和 eval 的中间骨架。
不要再问 Agent 做没做,要问系统留下了什么
回到那张被错误关掉的客服工单。
它之所以让人不舒服,不是因为 Agent 犯了一个低级错误,而是因为每一个表面信号都在替它作证。工具调通了,日志正常,回复也像样。只有数据库里的那个字段没有配合演戏。
这很像老派软件工程里最朴素的经验。不要迷信过程承诺,要验证可观察结果。
Agent 让这句话重新变得昂贵,也重新变得重要。
下次你验收一个能写真实数据的 Agent,可以先别问它「你完成了吗」。给它一个更难逃的问题。
最终状态是什么,证据在哪里。
参考资料
- ThinkingBox 论文,2026 年 10 月 1 日更新版本
- Hugging Face 与 Microsoft 发布说明,含 507 个任务的重复评测、成本和失败类型数据
- ThinkingBox-Bench 可执行数据集,MCP 工具服务、场景和测试用例




