这四者都能让崩掉的运行从最后一个已完成步骤继续,也就是说,人人拿来挑选的那个功能,恰恰什么都决定不了。对智能体而言,真正把它们区分开的是:智能体的持久化记录不是几个小小的步骤结果——它是一份每一轮都在长大的对话记录,而每个引擎对「谁来吸收这份增长」给出的答案都不一样。Temporal 会在第 51,201 个事件或 50 MB 历史处终止工作流。Inngest 把单步输出限制在 4 MB、单次运行状态限制在 32 MB。Restate 与 DBOS 则把这份增长推给你自己运维的存储。先按这一条来选;反正那些功能对照表彼此之间也没什么分歧。
一览
四个项目,四种真正不同的形状——而你买的是形状,不是功能清单。
| 项目 | 形状 | 持久化记录存放于 | 许可 |
|---|---|---|---|
| Temporal | 一个服务端集群,加上你自己跑的 worker 进程 | Temporal 服务中的事件历史 | MIT;托管选项为 Temporal Cloud |
| Restate | 一个带分布式日志的 Rust 单体二进制 | journal 加一个内嵌的键值存储 | SDK 为 MIT,运行时为 BSL |
| Inngest | 一个通过 HTTP 回调你函数的托管平台 | 由平台持有的步骤状态 | 商业平台,免费额度慷慨 |
| DBOS | 一个跑在你自己进程内的库 | 你现有 Postgres 里的检查点行 | 开源库(DBOS Transact) |
Restate 用 Rust 写成、以单个二进制交付,事件记录在一个基于 Flexible Paxos 演化出的虚拟共识设计所构建的分布式日志里,并提供 TypeScript、Python、Java/Kotlin、Go 与 Rust 的 SDK。Temporal 出自当年在 Uber 打造 Cadence 的团队,是四者中资历最老、运维上最经得起考验的。Inngest 是唯一一个「我们要部署什么」的正确答案是「什么都不用」的;它也是唯一自带第一方智能体框架的——AgentKit:把多个智能体编成网络、由一个路由器决定下一个谁跑、共享网络状态,并把 MCP server 当作工具。DBOS 则是唯一完全不新增基础设施的:给函数加个装饰器,检查点就落在你早已运维的 Postgres 里。
没人会去预留的那个上限
在一条常规工作流里——扣一笔卡、发一封邮件、更新一行记录——持久化状态只有几 KB,之后没人会再想起它。智能体把这件事翻了过来。每一轮都会追加一次模型回复,每一次工具调用都会追加一份结果,而那些结果才是胖的:一整页抓来的 HTML、一次返回四百行的查询、一个智能体读过的文件。如果你把它们记进 journal,持久化记录大致随对话记录一起增长;而把「每一轮重发」算进去之后,对话记录大致随步数的平方增长。
这些公开数字值得背下来,因为它们是以生产事故、而不是以警告的形式登场的:
- Temporal。单个载荷在 256 KB 时告警、在 2 MB 时报错。事件历史在 10,240 个事件或 10 MB 时告警,而服务会在第 51,201 个事件或 50 MB 处以一个不可重试的错误终止该工作流。另有一条单个 Workflow Task 事务 4 MB 的上限——即便没有哪个单独载荷很大,一个工作流一次性调度许多中等大小的 activity 也可能撞上它。
- Inngest。单步返回的数据上限为 4 MB,而整个函数运行状态不得超过 32 MB。
- Restate。没有以同样方式公布固定的单次运行上限;1.5 版新增了在载荷接近大小上限时自动压缩,这在跑 AWS Lambda 时尤其有用。
- DBOS。没有引擎侧上限,因为检查点就是你数据库里的行。你接手的是另一套东西:表增长、索引膨胀,以及一份 vacuum 的功课。
由此有两个结论,而第二个才是有用的那个。其一,引擎强制的上限不是缺陷——它是一个强制函数,逼你早早把对话记录外置,而这本来就是你该做的。其二,也正是决定架构的那一条:无论你选哪个引擎,从第一个提交起就按引用来存对话记录。把工具结果与消息写进对象存储或一张表,journal 里只记键和一小段摘要,那个上限就永远不会变成一次事件。按值把载荷记进 journal 的团队,会在第六个月撞墙;而那次迁移不是改个配置——它是把代码库里每一个步骤签名重写一遍,还得在「运行已经开始失败」的压力下进行。
模型调用允许待在哪里
恢复机制的差别,是以「对你源码的约束」而不是以「对照表里的一行」显现出来的。Temporal 的恢复方式是拿已记录的历史去重放工作流代码,这正是工作流函数必须是确定性的原因:不能读挂钟、不能有无保护的随机、不能有 I/O。这本身不算负担——它是一套被理解得很透、背后有十年工具积累的纪律——但对智能体而言,它有一个具体后果。模型调用是 I/O,所以它住在 activity 里;而你想拿 token 流做的任何事情,都不能从工作流作用域发起。从一个 Temporal 智能体向用户流式输出,是你要自己搭的另一条通道,不是一个你 await 得到的返回值。
Restate 与 Inngest 走的是记 journal,而不是在同样意义上重放你的控制流:你写普通函数并标出持久步骤,恢复的运行会从 journal 里读回已完成的步骤。剩下的那份纪律更隐蔽,却照样绊人——一个步骤的结果必须能序列化、且必须保持稳定;所以一个返回活跃客户端对象、返回生成器、或返回内嵌了当前时间的值的步骤,会在重试路径上而不是在正常路径上出岔子。
DBOS 是四者中侵入性最小的:给普通函数加装饰器、把检查点写进 Postgres,请求路径上没有额外服务。代价是持久性从此与你的应用共享一个失效域,并与你的主库共享一份容量预算。对一支已经在跑 Postgres 的 Python 或 TypeScript 团队来说,这是一笔极好的交易;而对一份「持久化状态会让本已吃紧的数据库体量翻倍」的负载来说,这是一笔糟糕的交易。
这四者都在被接进智能体框架,而不是与之竞争:DBOS 与 Restate 都有 Pydantic AI 集成的文档,Inngest 直接自带 AgentKit,而 Temporal 与图式编排器的配对则有它自己的一篇深入解析。持久化层不是你的智能体框架;它是在框架底下活下来的那个东西。
计费形状,对上一个不停造步骤的循环
定价问题不是「每月多少钱」,而是「我这个程序的形状会对计数器做什么」。而智能体循环特别擅长制造那个被计费的单位。
Inngest 按 execution 计费,而一次 execution 是一次函数运行加上其中的每一个步骤——一个含五次 step.run() 调用的函数消耗六次。对一条形状固定的事件驱动工作流来说,这完全合理。而对一个「跑到它自己决定停」的智能体来说,步数是一个由模型控制的变量,于是成本变成任务难度的函数——难以预测,也很容易碰上糟糕的一周。免费额度是每月 50,000 次步骤运行,付费计划从约每月 20 美元起,所以这笔账事先就算得出来——用你的 p99 步数去算,别用中位数。
Temporal 自建版是 MIT 许可、免费的,意思是只有集群本身要花钱;对不少团队来说这是诚实意义上最便宜的选项,而对另一些团队来说,它是一层没人想接手的 Cassandra 或 SQL 持久化。Temporal Cloud 则把它换成一张托管账单。DBOS 是异类:持久化执行层是一个库,所以持久性带来的边际基础设施成本,就是你的检查点给一个本来就在付钱的数据库额外添的存储与负载。Restate 介于两者之间——跑一个二进制,或者用托管服务。
无论你选哪个,真正重要的成本模型是智能体成本控制里那一个:模型 token 占大头,持久化层的账单不过是舍入误差——除非它的计费单位恰好就是你的循环会无节制生产的那个东西。在 Inngest 上,恰好就是。
什么时候选哪个
| 情形 | 选 | 理由 |
|---|---|---|
| 运行持续数天或数周、多语言团队、已有平台组 | Temporal | 运维上最经得起考验、对重试与超时的控制最显式、有十年的工具积累——代价是一个集群与那份确定性纪律 |
| 已经在跑 Postgres 的 Python 或 TypeScript 团队,想要持久性但不想新增基础设施 | DBOS | 进程内的一个库;没有集群、没有额外一跳,检查点落在你早已运维并备份的数据库里 |
| TypeScript 优先、serverless,想要开箱即有的智能体框架 | Inngest | 什么都不用部署,且自带 AgentKit——先把步数建模清楚,因为那就是计费单位 |
| 想要一个轻量的自建运行时、多语言、低延迟 | Restate | 一个 Rust 二进制、journal 加按对象的状态,以及一套异常干净的编程模型——四者中生态最年轻的一个 |
| 对话记录很大,而且会一直很大 | 都行,但要按引用存对话记录 | 那个上限只对「按值把载荷记进 journal」的团队构成问题 |
不该用来做决定的是:谁的重试示意图更好看,以及你那个框架的教程恰好用了哪一个。四者都会重试、都能恢复、都有定时器与信号。能在生产环境里活下来的差别,就是上面那三条——增长归谁、恢复模型对你的代码提出什么要求,以及计数器在数什么。
常见问题
智能体到底需不需要持久化执行?
一个在单次 HTTP 请求内跑完、失败了从头重试即可的请求级智能体,不需要。你需要它的那一刻,是一次运行的寿命超过了一个进程——长任务、中途要人审批、定时与事件触发的工作——因为到那时崩溃就是数据丢失,而重试就是重复的副作用。决定要不要上的是这个门槛,不是框架。
持久化执行会让我的智能体变确定性吗?
不会,而指望它会,是这里最常见的误解。这些引擎让恢复变得确定:已经完成的步骤是从记录里回放,而不是重跑一遍。步骤内部的模型调用仍然是从一个分布里采样,所以一次恢复后的运行,其可复现程度与原来那次一模一样——也就是说,不怎么样。
Temporal 那个 50 MB 历史上限真的会咬到我吗?
只有当你按值把载荷记进 journal 时才会。一个把工具结果存进对象存储、只在 journal 里记键与摘要的运行,在任何现实的智能体负载下都不会逼近大小或事件上限。而一个把抓来的页面和查询结果直接塞进 activity 返回值的运行会撞上,而且那次终止是不可重试的。
我能从一个持久化工作流里向用户流式输出 token 吗?
能,但不能作为那个持久步骤的返回值。在 Temporal 那样的重放模型下,模型调用是一个 activity,而流式输出不能从工作流作用域发起,所以通常的设计是从 activity 里走一条旁路通道往外流——队列、websocket 或 pub/sub 主题——同时让持久化记录捕获最终结果。请提前规划这条通道,别到后期才发现它。
Restate 运行时的 BSL 许可是个问题吗?
SDK 是 MIT,运行时是 BSL;这是如今很常见的一种形态,意图是阻止超大云厂商把这项服务转售出去,而不是限制普通的自建部署。它过不过得了你们的合规政策是个法律问题,而在多数公司答案很明确;Temporal 那个 MIT 许可的服务端,则是完全免掉这场对话的选项。
延伸阅读
本站内容:
- 持久化执行:LangGraph 加 Temporal——这套配对模式的详解。
- 持久状态与可恢复性——哪些东西必须做检查点,以及为什么。
- 幂等与重试——这个问题里引擎不替你解决的那一半。
- 定时与事件触发的智能体——逼你做这个决定的那类负载。
- 智能体成本控制——为什么步数才是该预测的那个数字。