AI 博客

Temporal、Restate、Inngest 与 DBOS:智能体的对话记录住在哪里

四者都能让崩掉的运行从最后一个已完成步骤继续,所以那不是决策点。智能体的持久化记录是一份每一轮都在长大的对话记录,而不是几个小小的步骤结果——而这几个引擎的分歧在于:这份增长存在哪里、会撞上什么上限,以及你的模型调用允不允许待在会被重放的代码里。Temporal 会在第 51,201 个事件或 50 MB 历史处终止工作流;Inngest 把单步输出限制在 4 MB、单次运行状态限制在 32 MB;Restate 与 DBOS 则把这份增长推给你自己运维的存储。先按这个来定,再看计费形状,功能对照表就不重要了。

作者 智能体 AI 维基 26 分钟读完

这四者都能让崩掉的运行从最后一个已完成步骤继续,也就是说,人人拿来挑选的那个功能,恰恰什么都决定不了。对智能体而言,真正把它们区分开的是:智能体的持久化记录不是几个小小的步骤结果——它是一份每一轮都在长大的对话记录,而每个引擎对「谁来吸收这份增长」给出的答案都不一样。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)
Where each engine keeps the durable record of a run Four columns. Temporal keeps event history in a server cluster backed by Cassandra or a SQL store, operated by you or by Temporal Cloud. Restate keeps a journal and embedded key-value state in a single Rust binary using a distributed log. Inngest keeps step state in its managed platform and calls your functions back over HTTP. DBOS keeps checkpoints in the Postgres database your application already uses, as a library running inside your own process. Where the durable record of a run is kept Temporal Restate Inngest DBOS THE RECORD Event history Every command and result, append-only Journal + KV state Durable log, plus state on virtual objects Step state Each step’s output, held by the platform Postgres rows Checkpoints in the DB your app already has WHAT YOU RUN Server cluster Self-hosted (MIT) or Temporal Cloud One Rust binary Distributed log; MIT SDKs, BSL runtime Nothing Managed platform calls your functions over HTTP Nothing new A library inside your own process WHO ABSORBS THE GROWTH The engine Hard ceiling; the run is terminated Your storage Payloads compressed near the size limit The platform Per-step and per-run size limits apply Your Postgres Your table growth, your vacuum A long agent run grows the record on every turn; the column you pick decides who notices first.
一次长时间的智能体运行,每一轮都会把记录撑大;你选哪一栏,决定了谁会先察觉。

Restate 用 Rust 写成、以单个二进制交付,事件记录在一个基于 Flexible Paxos 演化出的虚拟共识设计所构建的分布式日志里,并提供 TypeScript、Python、Java/Kotlin、Go 与 Rust 的 SDK。Temporal 出自当年在 Uber 打造 Cadence 的团队,是四者中资历最老、运维上最经得起考验的。Inngest 是唯一一个「我们要部署什么」的正确答案是「什么都不用」的;它也是唯一自带第一方智能体框架的——AgentKit:把多个智能体编成网络、由一个路由器决定下一个谁跑、共享网络状态,并把 MCP server 当作工具。DBOS 则是唯一完全不新增基础设施的:给函数加个装饰器,检查点就落在你早已运维的 Postgres 里。

没人会去预留的那个上限

Published per-run ceilings on durable state A horizontal bar chart of the published ceiling on durable state per run. Temporal terminates a workflow whose event history passes 50 megabytes or 51,201 events. Inngest caps function run state at 32 megabytes. Restate publishes no fixed per-run ceiling and compresses payloads as they approach the limit. DBOS publishes no ceiling because checkpoints are rows in the Postgres database you operate. The two open-ended bars are drawn with a dashed edge to show they are bounded by storage rather than by the engine. Published ceiling on durable state, per run 0 16 MB 32 MB 48 MB 64 MB Temporal 50 MB history, or 51,201 events — run terminated Inngest 32 MB function run state; 4 MB per step output Restate no fixed published ceiling; payloads compressed near the limit DBOS no ceiling; checkpoints are rows in your own Postgres Solid bars are enforced by the engine. Dashed bars run to the edge of your storage instead.
实心条是引擎强制的。虚线条则一直延伸到你存储的边界。

在一条常规工作流里——扣一笔卡、发一封邮件、更新一行记录——持久化状态只有几 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 的团队,会在第六个月撞墙;而那次迁移不是改个配置——它是把代码库里每一个步骤签名重写一遍,还得在「运行已经开始失败」的压力下进行。

模型调用允许待在哪里

Three execution models and where the LLM call is allowed to sit Three columns. Replayed deterministic code, used by Temporal, re-executes the workflow function on recovery, so the model call must live in an activity and streaming cannot originate in workflow scope. Journaled ordinary code, used by Restate and Inngest, records each durable step's result and replays it, so the constraint is that step results must be serialisable and stable. Decorated in-process functions, used by DBOS, checkpoint to your own Postgres from inside your application, so there is no separate worker fleet and the failure domain is your app. What the recovery mechanism asks of your code Replayed deterministic code Temporal The workflow function is re-executed on recovery. The model call must be an activity; no clocks, no random, no streaming in workflow scope. Journaled ordinary code Restate · Inngest Each durable step’s result is recorded and replayed. Normal functions; the rule is that step results serialise and stay stable across a retry. Decorated in-process DBOS Decorators checkpoint to the Postgres you already run. No worker fleet, no extra hop — and the failure domain is your own application process. Only the first column changes how you have to write the agent loop.
只有第一栏会改变你写智能体循环的方式。

恢复机制的差别,是以「对你源码的约束」而不是以「对照表里的一行」显现出来的。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 许可的服务端,则是完全免掉这场对话的选项。

延伸阅读

本站内容:

项目来源: