先摆一个假想场景。
你让 AI 助手替一场活动采购物料。订单提交,商家收款,助手所在的进程恰好在“保存成功结果”之前崩了。
重启以后,聊天记录还在,计划还在,助手很有干劲:“刚才中断了,我继续。”
你盯着那句“继续”,突然希望它别那么积极。采购清单只要一份,账单可不想要双份。
这不是某个产品的真实事故,而是一个恢复过程里的故障窗口:外面的事情已经发生,里面的记录还没来得及写。聊天界面看起来只是少了一句回复,后面的系统却要判断一件很要命的事:刚才到底做没做成?
10 月 1 日,Earendil 发布 Pi 1.0,同时推出仍处于实验阶段的 Pi Durable。后者面向长时间运行的 Agent 应用。官方介绍把保存进度、进程崩溃后恢复,以及提交请求去重放在了显眼位置。
这些能力当然值得做。一个查资料查到半夜、容器重启就把工作丢干净的助手,很难让人放心托付长任务。但读完实现,我更在意它在哪些地方没有急着继续。
Pi 的工具执行源码在执行前保存参数和重放策略。没有声明时,默认按 unsafe 处理。恢复时,只有当时保存的策略和现在的工具声明都为 safe,才重新执行;否则返回中断结果,提醒模型这次调用可能已经部分运行。
这句“可能已经”,比一个漂亮的恢复动画更值钱。
它没有把“本地没看到成功”擅自翻译成“外面肯定没做”。查一遍资料通常可以再查,已经提交的订单就不能只凭聊天记录决定再下一遍。即使都是一个工具调用,重复的后果也完全不同。
项目的恢复测试专门覆盖了这条分界:保存和当前策略都允许才重跑;原先不允许,后来改成允许,也不能替那次已经发生的调用补上可安全重放的依据。这里说的是对源码和测试用例的核对,本文没有运行 Pi 的故障注入实验。
不过,停住一次旧调用,还没有替整个应用解决重复执行。中断信息返回模型后,模型可能提出一次新的调用。假如应用把所有新调用照单全收,同一件采购仍然可能再做一遍。这是从调用边界推出来的产品风险,不是本文发现了 Pi 的重复扣款事故。
收银台的小票没打印出来,不能让收银员换个窗口就当一笔新生意。
最容易让人误会的词,是 exactly-once。
Pi 文档里这项承诺有明确对象:同一会话内,带同一个 requestId 的提交会去重。它解决的是客户端没收到回应后重发,别把原来的任务又登记一遍。对应测试也检查了会话边界;另一个会话使用相同 ID,并不会被当成原来的提交。
就像前台认得同一张采购委托单,客人再递一次,不必再登记一次任务。这很有用,却不能据此推断后台每一个动作都只会发生一次。任务登记处和商家订单系统,毕竟没有因为一句“持久化”就共用一本账。
也不能反过来宣布,外部动作永远做不到明确的去重保证。支付等系统已经有可用的办法。关键是保证落在哪一层、满足什么条件,而不是给整个 Agent 贴一个听起来很踏实的词。
以 Stripe 的幂等请求文档为例,客户端为同一次操作带上幂等键,在端点开始执行后,服务端保存首次请求的状态码和响应体,后续同键请求返回原结果;参数验证失败或与正在执行的请求冲突时,没有开始执行,就不会缓存该结果。文档也列出了边界:参数需匹配,键可在至少 24 小时后清理,清理后再用旧键可能形成新请求。这个 24 小时来自 Stripe 的规则,不能移植成所有服务的通用保证。
这时真正有价值的,是重启前后仍能认出“同一笔生意”。如果助手每次恢复都生成一个新键,或者换了一次对话就把业务身份丢了,接了幂等接口也白搭。编号换了,柜台当然可能按一笔新订单办理。反过来,把两笔真正不同的订单错误地塞进同一个键,也会出问题。
所以我对长任务 Agent 的判断是:一个醒来后肯先核对订单、再决定做什么的产品,比一个永远能流畅接话的产品更值得信任。流畅感可以展示,未知状态必须处理。
这里的难处,不全在模型会不会推理。应用得保住业务编号,拿得到外部回执,知道何时还能安全重试;没有查清的,要把“结果待确认”作为真实状态留给人看。让模型再读一遍聊天记录,不能补出一张根本没被保存、也没向商家查询的小票。
还有一种“恢复”同样容易过度承诺:取消。
取消一个任务,可以意味着不再继续;对已经发生的效果,有时还需要退款、撤销或补偿。Pi 的任务设计说明明确把外部动作夹在“保存意图”和“保存结果”之间,并要求恢复逻辑选择安全重试、查询外部句柄或记录中断。这个分层提醒我们:本地状态能恢复到哪一步,要和外部世界已经走到哪一步分开看。
即使退款成功,也是一笔后来发生的新动作。货已经发出,收件人已经读到邮件,生产环境已经对用户提供过一次错误响应,都不能靠把对话退回上一条消息当作没发生。程序很擅长回到旧状态,现实通常不提供那么整齐的撤销按钮。
这不意味着所有 Agent 都要先造一套复杂交易系统。只读检索、草稿生成,或者可以丢弃重建的临时文件,往往可以采用更轻的恢复办法。重新生成一份摘要,与重新付一笔钱,没必要背同样重的包袱。具体外部服务如果已经提供稳定的业务去重和查询能力,也应该用好,不必重复造轮子。
值得警惕的是,产品把能力从“帮你想”扩展到“替你办”,交付标准却仍停留在“崩了以后聊天还能接上”。事情越做越长,碰到的外部系统越多,那些过去藏在演示之外的半张小票,就越可能成为主要工作。
如果让我挑一个演示环节,我会把断点设在最不讨巧的位置:外部已经受理,本地还没记下成功。然后看产品怎样找回这笔操作,怎样显示不确定,怎样避免把同一件事换个调用编号再办一次。这是本文建议的验收场景,不是声称 Pi 已经替每个接入服务完成了验证。
一个系统愿意在这里承认“还没查清”,未必显得聪明。它只是在替用户认真保管那张账单。
AI 可以说“我接着干”。在动手以前,先把上一张小票找回来。
资料核对:2026 年 10 月 4 日。发布事件为 10 月 1 日;Pi Durable 当时标注为 experimental。源码与测试引用固定于 200387122ca450d6387f033949423114a270b96c,不代表独立运行验证,也不承诺后续版本行为不变。采购场景与配图为假想示意;产品判断和验收建议为本文分析,未声称发生真实重复扣款事故。所述 exactly-once 边界不否认下游幂等接口或事务设计在明确条件下能提供的保证。
想继续拆解 Agent 的工程细节,可以看 AI 实践路线;公众号入口在关于页。
本文文字与图解由 AI 辅助制作,事件、源码和文档来源已在正文列明。

