持久化智能体:为什么异步队列无法胜任长运行 AI 工作流
一个每步成功率为 95% 的智能体并不是一个 95% 可靠的智能体。将 20 个步骤串联起来,端到端的完成率就会下降到 36%。这是大多数团队在智能体上线生产环境后才发现的算数逻辑,也是为什么这么多“运行良好”的原型在真实流量涌入的瞬间就会陷入停滞。解决方法不是更好的提示词或更大的模型,而是一个乏味的分布式系统基础设施,大多数 AI 团队在第三次宕机被迫应对之前都会试图避开它。
这种基础设施就是“持久化执行”(durable execution)——这是一种让多步骤工作流在崩溃、重启和局部故障中幸存且不丢失进度的准则。这并不是什么新鲜主意。Temporal、Restate、DBOS、Inngest 和 Azure Durable Task 已经为此推销多年。2026 年的新变化是,每个严肃的智能体框架都已悄然承认持久化执行是入场券:LangGraph 现在内置了 PostgresSaver 检查点,OpenAI Agents SDK 暴露了 resume(恢复)原语,Anthropic 的 Managed Agents 运行在内部的持久化基座上。如果你的智能体架构仍然依赖 Celery 队列和乐观主义,那么你是在 2026 年解决一个整个 行业在 2024 年就不再假装视而不见的问题。
本文探讨的是无状态 LLM 与必须包装它的有状态工作流引擎之间的架构接缝。接缝之处正是可靠性所在,也是大多数团队目前编写 Bug 的地方。
为什么异步队列模式会失效
当智能体调用时间超过请求-响应周期所能承受的范围时,默认反应是将其推入队列。Redis、SQS、RabbitMQ、Celery —— 具体选择几乎无关紧要。你将任务入队,一个 worker 领取任务,调用 LLM,调用一些工具,将结果写入某处,并确认消息。对于单次推理,这运行良好。但对于多步智能体来说,这是一个等待触发的陷阱。
第一个失败场景是 worker 在执行过程中宕机。一个花费 18 分钟搜集资料并综合研究结果的研究型智能体,在 Kubernetes 自动伸缩器决定重新调度其 Pod 时,已经烧掉了数十美元的 LLM 费用。当 worker 宕机时,队列会重新投递消息 —— 智能体将从第一步重新开始,再次消耗同样的费用。你可以尝试用内存状态来缓解这种情况,但 worker 丢失的正是不受持久化保护的内存。你可以尝试用可见性超时(visibility timeout)来缓解,但你实际上并不知道智能体需要多长时间,因为 LLM 是非确定性的,而且工具可能会陷入循环。
第二个失败场景是不可安全重试的重试。如果你的智能体在第七步执行了退款、发送了 Slack 消息或创建了 Stripe 扣费,重新投递任务会导致智能体再次规划到第七步并重复执行。队列为你提供了“至少一次交付”(at-least-once delivery),这是队列的特 性。使工作具备幂等性是你需要解决的问题,而且在有 LLM 参与的情况下,“幂等”并不意味着“计算两次相同的值” —— LLM 这次的规划可能会有所不同。非确定性工作流的重试不是重试,而是一次全新的运行。
第三个失败场景是当你试图修补前两个问题时累积的状态蔓延。团队开始在每一步后向 Postgres 写入状态标志,在重试时检查它们,在每个智能体中构建临时恢复逻辑。看起来只是“增加了几个列”,最终却演变成了一个自研的工作流引擎,其语义甚至比你直接采用的现成引擎还要差。每当有人增加一个新步骤,维护成本就会成倍增加。
接缝:无状态规划器,有状态基座
清晰的思想模型是将 LLM 视为返回下一个预期动作的无状态先知(oracle),并将工作流引擎视为实际执行这些动作、持久化结果并向 LLM 提供一致历史视图的持久化基座。智能体循环变为:询问 LLM 该做什么,记录决策,持久地执行动作,记录结果,将所有内容反馈给 LLM,如此循环。
这种反转至关重要,因为它把非确定性组件放在了它该在的地方 —— 作为一个从观测状态到建议动作的纯函数 —— 并在其周围包裹了确定性机制。工作流引擎处理确定性机制擅长的事情:持久化、重试、超时、并发、补偿。LLM 处理它擅长的事情:选择下一步行动。它们之间的接缝是一个微小的 API,而不是蔓延的临时状态列。
目前主要有两种实现风格。基于日志的回放(Journal-based replay),由 Temporal 和 Restate 使用,它将每个完成的步骤记录到日志中,并在恢复时回放日志,通过返回缓存的结果来跳过已完成的工作。数据库检查点(Database checkpointing),由 LangGraph 和 DBOS 使用,它在每个节点之后持久化完整的状态对象。两者都能达到同样的目的 —— 让工作流从中断处精确恢复 —— 但在存储、回放成本以及框架内部与数据库内部状态占比方面有不同的权衡。
真正重要的区别在于框架是否将“副作用”视为一等公民。Diagrid 对纯检查点系统的批评非常尖锐:仅靠检查点并不知道第三步调用了支付 API,因此它无法保证回放时不会再次调用该 API。持久化执行要求每一次外部交互 —— 每一次 LLM 调用、每一次工具调用、每一次数据库写入 —— 都必须包裹在一个引擎知道如何在回放时进行去重的原语中。没有这一点,你得到的只是“恢复的假象”,而不是真正的恢复。
当回滚不可行时的 Saga 模式与补偿机制
一旦你接受了 Agent 工作流可能跨越数分钟到数天,并涉及外部系统,你就无法再将数据库事务作为一致性模型。你不能在可能耗时两分钟的 LLM 调用期间保持 Postgres 事务开启,更不能在调用第三方 API 的工具调用期间保持事务。分布式系统社区在 20 年前达成的共识是 Saga 模式,它几乎可以原封不动地迁移到 Agent 领域。
Saga 是一系列本地事务的序列,每个事务都对应一个在逻辑上撤销它的补偿动作(Compensating Action)。如果第五步失败了, 引擎会以相反的顺序运行第一步到第四步的补偿操作。这些补偿并不是 ACID 意义上的回滚——它们是语义上的撤销。信用卡扣款的补偿是退款,而不是删除那一行记录;发送邮件的补偿是发一封道歉邮件,而不是撤回发送。补偿是应用层面的决策,引擎无法替你完成。
Saga 在 Agent 工作流中变得更加有趣,因为 LLM 本身就是决策图的一部分。最近一篇关于 SagaLLM 的论文指出,多 Agent LLM 规划器需要事务保障,否则它们的计划会悄无声息地与它们所操作的现实世界脱节。实际上,这意味着 Agent 采取的每一个外部可见的操作——写入 CRM、调用 Webhook、创建日历事件——都应该被编码为一个带有明确补偿定义的步骤,而不是一个裸露的工具调用。目前大多数 Agent 框架并未强制要求这一点,这也是为什么许多在演示中看起来很棒的 Agent,在流程中途失败被迫恢复时,表现得惨不忍睹的原因之一。
Agent 的 Saga 设计中最难的部分是决定什么是可补偿的,什么不是。给客户发信息是不可撤销的;给予折扣是部分可撤销的;创建一个 GitHub Issue 则很容易撤销。Saga 的设计压力迫使工程师预先思考操作的可逆性,这种强制约束往往能产生更合理的 Agent 设计,即便最初的动力只是为了实现持久化执行(Durable Execution)。
真正适用于 LLM 的幂等键
幂等性(Idempotency)是让重试变得安全的基础原语,而 Agent 工作流对它的压力测试超出了大多数库的设计范畴。标准的 HTTP 幂等键模式——客户端生成 UUID,服务器缓存响应 24 小时——在客户端是确定性的时候运作良好。但当客户端是 LLM 时,会出现两个问题。
首先,LLM 在重试时可能会生成不同的请求。如果模型在重试时决定调用 send_email(to=customer, subject="welcome"),而上次尝试是 send_email(to=customer, subject="Welcome"),基于内容哈希的幂等键会将它们视为不同的请求并发送两封邮件。解决方法是从工作流的确定性上下文(工作流 ID 加上步骤编号)中派生幂等键,而不是根据参数。这样,无论 LLM 在重试时决定发送什么,引擎都会将其识别为同一个逻辑步骤的重放,并返回缓存的结果。
其次,LLM 调用本身也需要幂等性,否则每次重放都会消耗 Token。基于日志(Journal-based)的引擎通过在日志中缓存 LLM 响应并在重放时返回来处理这个问题,这虽然正确,但存在一个微妙的失效模式:如果你的提示词模板在原始运行和重放之间发生了变化,缓存的响应可能不再与新提示词保持逻辑一致。显式对工作流进行版本管理的框架(如 Temporal 的工作流版本控制、LangGraph 的节点标识)避开了这个问题;而不具备此功能的框架最终会让你踩坑。
更深层次的教训是,Agent 工作流中的幂等性是工作流引擎的属性,而不是单个操作的属性。你不能逐个操作地去硬套幂等,因为操作是由一个可能会改变主意的模型动态生成的。引擎必须拥有每个步骤的身份标识。
何时采用以及采用什么
在第一天就为玩具级 Agent 引入 Temporal 或 Restate 实在是杀鸡用牛刀,会拖慢你 的速度。通常的路径是在经历三次生产事故后才开始考虑它们,而到那时,你已经积累了大量的临时状态,导致迁移过程异常痛苦。在实践中行得通的折中方案是:选择一个与你团队技能匹配的最轻量级的持久化底层,并在 Agent 逻辑超过一轮对话时立即采用它。
对于已经在使用 Python 和 LangGraph 的团队,带有 PostgresSaver 的内置检查点(Checkpointer)是门槛最低的起点。它让你无需引入新基础设施就能获得恢复运行和人工介入(Human-in-the-loop)的停顿功能。当你达到它的极限——跨服务事务、多区域、运行时间极长的工作流——时的升级路径是转向 Temporal 或 Restate。不要试图将 LangGraph 的检查点扩展成通用的工作流引擎;它本身并不是为此设计的。
对于习惯于显式“工作流即代码”(Workflow-as-code)的团队,Temporal 是成熟的选择,并已接近成为大规模运行的 AI 原生公司的默认标准。Restate 占用空间更小,如果你的部署模型偏向 Serverless 或边缘计算,它值得评估。DBOS 是一个较新的竞争者,它将工作流引擎折叠进你的 Postgres 数据库中,对于不想运维另一套系统的初创团队很有吸引力。Inngest 属于队列原生(Queue-native)阵营,比 Temporal 更容易上手,但在重放语义上做了一些权衡。
无论你选择哪一种,你所做的承诺都是架构层面的,而不仅仅是运维层面的:每一个外部可见的操作都被包裹在一个引擎原语中,每一步都有一个补偿动作或一个明确的“不可逆,请勿重试”标签,而 LLM 不再是那个知道工作流执行到哪一步的角色。LLM 负责挑选下一步;引擎负责记住该步骤已经发生。混淆这两项职责是大多数生产环境 Agent 故障的“原罪”。
