OOOMETA/OS
← 返回洞察

2026 年 11 月 · 12 分钟阅读

日志能重放,世界不能重放

日志能重放,世界不能重放

关键定义

持久执行(durable execution) 把一次运行中每一步的输入和输出写进事件历史,进程崩了以后从头重放代码、与历史逐条核对,已完成的步直接取回记录值,不再真正执行。代价是控制流必须确定。

日志重放(replay) 恢复时重新跑一遍流程代码,用记录下来的结果替代真实的模型调用和工具调用。它让同一件事不会因为崩溃变成零次或两次,也让当时做的判断原样再出现一次。

步边界(step boundary) 运行里结果会被记录的最小单元。模型调用、工具调用、外部接口、人工审批都落在它上面,它决定了崩溃恢复后哪些东西被当成事实读回。

退款金额超过一千美元,工单就要挂起等主管审批,等的是几小时,也可能是几天。这是 DBOS 那篇持久执行文章里的示例流程。假设这中间服务重启过一次:恢复之后,智能体不会重新问一遍这笔该不该退,它读回日志里审批通过那一步记下的结果,照着执行。

整个过程没有报错,没有重复退款,工单状态也对。这正是持久执行承诺的效果,也是它最容易被误读的地方:日志把两天前的一个决定原样搬到了今天,而这两天里,价格、库存、政策版本、客户账户状态都可能已经变了。

这篇只谈一件事。重放让同一个决定被执行两次,它不保证这个决定在今天仍然成立。两件事长在同一套机制里,前者是设计目标,后者是还没有人补上的那半截。

一、它是怎么做到不重复的

Temporal 那篇讲智能体的文章开头就点出了这套机制的骨架:workflow 必须确定,activity 可以随便不确定。调模型、调工具、发 HTTP 请求都放在 activity 里,因为它们的输出会被记下来,恢复时当作数据读回,而不是重新发生一次。

具体分三步。代码从函数第一行跑到最后一行;每一个真正对外产生作用的地方,把输入和输出写进一份事件历史;进程崩了以后,从第一行再跑一遍,跟历史逐条核对,已经记录过的步直接取回当时的输出,不再真正执行。

这套机制要求控制流本身是确定的。同一份代码、同样的历史,必须走出同样的顺序。于是所有不确定性被赶出控制流,关进那些被记录下来的步里。Temporal 的原话是:workflow 的确定性要求不限制智能体的行为,它反而让智能体在生产里可靠。

他们文档里还有一句更要紧的话:恢复之后,workflow 不会为已经做过的决定重新要一份计划,它把事件历史当成决策记录来读;从恢复点往后,智能体才继续自由规划。这句话把机制说清楚了,也把问题埋下了:被当成决策记录来读的那份历史,记录的是决定,不是决定成立的条件。

反面的场景他们自己也举了。一个度假规划智能体订好了去加拿大 Whistler 的机票,在找酒店这一步崩了。没有日志重跑的话,重启的进程重新规划,这一次它订了去日本的机票,而去 Whistler 的那张还得另外取消。同一件事,两次运行,两个结果,中间没有任何人改过需求。

二、重放出来的是同一个决定,不是同一个事实

Diagrid 在讲自家产品的时候,把这句话写得很克制:「Durable execution doesn’t make any single step smarter or accurate; a replayed step repeats the same decision」。这是这套机制的自述,不是外面的批评。持久执行不提高任何一步的准确性,它让同一步重复时给出同一个结果。

那么恰好一次到底承诺了什么。它承诺以记录时刻的状态为准,那件事发生过一次,不会因为崩溃变成零次或两次。它没有承诺,记录时刻的状态在今天还成立。这两句话听起来只差半句,落到退款、下单、放款、发通知这类动作上,差的是真金白银。

前面那个工单就是例子。主管的同意被记录下来了,重放把它当成事实读回。可日志里只有同意这个结果,没有它当时建立在哪一版政策上、账户余额是多少、那笔订单还在不在待退状态。恢复的时候,运行时能核对的是这一步的记录存在吗,不是这一步的前提还成立吗。

我们此前写过,检查点能恢复流程走到哪,恢复不了已经发出去的那一笔。这篇说的是同一套机制的另一半:外部副作用那一半已经有人管,被重放的那个判断还作不作数,没有人管。

三、设计单元从智能体变成了步

上了日志以后,团队的注意力会自己转移。原来问的是这个智能体有多聪明、提示词写得好不好;现在必须先回答,这一步的输出能不能当事实用。这不是同一个问题的两种说法,是两种不同的活。

每一步要回答三件事:这个结论依赖哪些外部状态;那些状态变了,结论还成立吗;不成立的时候,是重算、重新找人批,还是走补偿。多数团队只答了第一件,而且答在文档里,没有答进日志结构里。

这条为什么重要。重放是运行时自动做的,人不在场。恢复的那一刻,没有人会对两天前的那个同意提出疑问,代码只是照着历史往下走。如果你希望它在恢复时重新校验,就得把这个要求写进记录,运行时才有依据。

四、日志里该有三样东西

一份能当证据用的步记录,至少有三样。输出本身;这条输出成立所依赖的具体前提,比如政策版本号、当时的报价、库存数、授权范围、审批人;有效期,也就是超过多久之后必须重新校验。

三样里缺任何一样,日志就退化成一份存档。它证明当时这么想过,不证明现在还能这么做。区别在于,存档只能事后复盘,凭证可以在恢复时被自动判断。

有个简单判据:把这份日志给一个没参与项目的人看,他能不能判断这个决定今天还该不该执行。不能,说明记录不完整。而这份记录最需要说服的人,恰恰是恢复时那个不在场的审计员。

五、三类步骤,三种处置

不是所有步都一样。粗分三类,处置方式差得很远。

第一类,无条件重放。纯计算、幂等读、已经发生且不可逆的外部事实,比如已经入账的凭证、已经发出的通知。这些记录写的本来就是既成事实,重放是安全的。

第二类,带前提重放。价格、库存、政策、额度、人的审批,都落在这里。重放之前重新比对前提,前提没变就继续,变了就重新算或者重新批。这类步的数量比第一类多得多,也正是现在最容易漏掉的一类。

第三类,不许重放,只许补偿。对外承诺一旦发出,就不能靠再执行一次来修正:合同、报价、退款指令、对外公告。这类步的恢复路径必须是反向动作加一条人工确认,而不是重新跑一遍。

三种处置写在跟着步走的配置里,不写在跟着智能体名字的配置里。换一个实现同样功能的智能体,处置策略不该跟着变。

六、不是每个流程都值得上这一套

持久执行有明确开销:每次运行多一份日志写入,多一层运行时,多一套要跟着版本维护的记录结构。什么时候不值?单步、秒级完成、崩了整条重跑代价本来就低的任务,日志是纯粹的负担。

判据可以写得很具体:找出这条流程里最长一次什么都不做只是在等的时长。等几秒钟的,重放窗口短,状态过期概率低,不值得为它建一套日志。等几小时的,状态必须有人保管。等跨天的,保管的那份状态必须带有效期,否则它保管的是一份过期判断。

还有一类容易忽略:读多写少的分析型任务。每次运行独立,输出不改变外部世界,坏了重跑一次就好。这类任务上持久执行省不了多少钱,反而多出一份需要审计的日志。

七、省钱和省掉的那次重算,是同一个开关

持久执行之所以省,是因为已完成的步不重新执行。不重新执行,就不重新调模型,省下了那几次 token 和时间。问题在于,最可能已经过期的,正是这些没被重算的步。

所以在这条线上,成本和正确性不是两个能分开优化的目标,它们是同一个开关的两端。不重算就省钱,也保住了一个可能已经过期的决定;重算就花钱,也把决定拉回当下。设计要做的是逐步选边,不是给整条流程一个统一答案。

分步选边有个好用的起点:看这一步错了以后,谁来承担。内部报表算错,重跑一遍就好;给客户发了退款,就得有人去解释。前者可以留在省钱那一端,后者必须站在重算那一端。

八、采购和审计该问的四个问题

如果供应商把 durable 当卖点,问题要落到记录结构上,不要停在支不支持崩溃恢复。

一问:日志记哪些字段。是只有输入输出,还是带前提和有效期。这一条决定了恢复时能不能自动判断,还是只能事后靠人回忆。

二问:恢复能不能按步配置。重放、重算、补偿三种策略能不能逐类设置,还是全局只有一个行为。全局一个行为的运行时,会让你在同一个开关上给整条流程做一次妥协。

三问:日志能不能证明没被改过。留存多久,谁能读,权限在哪一层。Diagrid 把每一步可被密码学签名、可追溯来源写进了产品说明,这是厂商声称,方向上指向可验证执行;它是否覆盖到你要审计的每一步,要让供应商拿一条真实的中断运行演示一遍。

四问:只看日志能不能复盘。给一条被中断过的运行,审计员能不能判断每一步当时成立的条件。这一条是验收标准,答得上来的团队,事后审计是查一份文件;答不上来的,审计是重跑一遍流程,再让人凭记忆解释。

顺便把证据的成色分清楚。Temporal 提到 OpenAI 的 Codex 和 Replit 的 Agent 3 跑在它上面,DBOS 的示例流程来自它自己的演示应用,Diagrid 的产品能力来自它的发布说明。三份都是厂商自述,方向上可信,具体覆盖范围要自己核。

九、我们的判断

第一,2026 年下半年这条线换了位置。持久执行原来是工作流引擎给后台任务用的能力,现在被包到了智能体框架底下。Diagrid 用一个包同时接住了 LangGraph、Microsoft Agent Framework、Google ADK、AWS Strands、OpenAI Agents SDK 和 CrewAI;DBOS 把它做成库,和 LangGraph 的检查点并排用;Dapr 那边的持久执行在十月接上了 Goose 和 n8n。底座一变,选型问题就从选哪个框架,变成了这个运行时到底记录什么。

第二,可靠性从智能体身上移到了步边界上。一条能跑三年的智能体流程,靠的不是模型越来越聪明,而是每个步的前提被写清楚、恢复时被重新校验。模型换代改变的是步内部的质量,改不了步与步之间的账。

第三,durable 现在被当成风险控制来卖,但它只兑现了一半。它保证同一件事不重复发生,不保证那个判断今天还成立。把 durable 写进合同条款之前,先要求日志里带前提和有效期,否则你买到的是一种一定会执行过期决定的能力。

十、这个季度能做的三个动作

一,把现有流程的最长等待时长列出来排序。超过一小时的,先确认它的状态现在存在哪里:进程内存、检查点表、还是日志。存在进程内存的,下一次崩溃就是从零开始。

二,挑一条有跨天等待的流程,给每个会被记录的步加两个字段:前提和有效期。这一步不需要换运行时,只改记录结构。改完之后,你至少能在恢复时判断该不该信那份记录。

三,把验收标准加一条:给一条中断过的运行,审计员只看日志能不能判断每一步当时成立的条件。答不上来的,不要把可恢复写进服务条款。

来源

Temporal,Of course you can build dynamic AI agents with Temporal,2025-11-12:temporal.io/blog/of-course-you-can-build-dynamic-ai-agents-with-temporal。workflow 与 activity 的分工、确定性与恢复后不给已做决定重新规划、度假智能体订错机票的例子、Codex 与 Replit 的使用,均出自该文。

Diagrid,Durable Execution, Now Built for Agents,2026-07-27:diagrid.io/blog/what-is-agentic-durable-execution。重放重复同一个决定的表述、四象限模型、密码学签名与可验证执行的产品说明、支持的框架清单,均出自该文。

DBOS,Durable Execution for Building Crashproof AI Agents,2025-02-24:dbos.dev/blog/durable-execution-crashproof-ai-agents。一千美元审批阈值、异步人工审批的退款流程示例,出自该文与配套演示。

Dapr 的持久执行接 Goose 与 n8n 两条发布说明,见 Diagrid 博客 2026-10-28 与 2026-10-21 两篇。文中所有厂商能力均按厂商自述标注,未经独立验证。

常见问题

持久执行保证的到底是什么+

保证同一件事以记录时刻的状态为准发生过一次,不会因为崩溃变成零次或两次。它不保证记录时刻的状态在今天仍然成立,这两件事隔着的正是本文要讲的半截。

重放会不会重新调一次模型+

不会。已完成的步直接取回当时记录的输出,重放不产生新的模型调用。省下这笔钱的同时,也把那个当时做出的判断原样搬到了现在,包括它可能已经不对的部分。

为什么控制流必须确定+

因为恢复的做法是重新跑一遍代码再跟历史对齐。代码如果自己会选不同的分支,对齐就无从做起。所以确定性要求落在控制流上,不落在模型调用上:调模型、调工具都放在被记录的步里,输出当成数据读回。

哪些步可以放心重放+

纯计算、幂等读、已经发生且不可逆的外部事实,比如已经入账的凭证、已经发出的通知。这些记录写的本来就是既成事实,重放不会改变世界。价格、库存、政策、审批这类判断,重放前必须重新比对前提。

等多久的流程才需要这套东西+

看最长一次什么都不做只是在等的时长。等几秒的,重放窗口短,过期概率低,不值得为它建日志。等几小时的,状态得有人保管。等跨天的,保管的那份状态必须带有效期。

日志里应该多记什么字段+

输出本身、输出成立所依赖的具体前提(政策版本号、当时报价、库存数、授权范围、审批人)、以及有效期。缺任何一样,日志就只是存档,只能事后复盘,不能在恢复时自动判断。

这个决定过期了怎么办+

分三种处置写进步配置:前提没变就继续重放;前提变了就重新算或者重新批;对外承诺类的动作不许重放,恢复路径只能是反向动作加人工确认。这套策略跟着步走,不跟着智能体的名字走。

拿你自己的执行日志跑一遍

交一份脱敏的近 30 天 agent 执行日志,48 小时内拿到八行表:八项技术项每项一行,每格带分子、分母和判据,缺口按是否要改 schema 排序。前三单 ¥8,000。

看八项字段检查 →