Vercel 在 10 月 21 日开源了 durable-harness,把 Pi、OpenCode、fx、Claude Code、Codex 这 5 个主流编码智能体框架接到同一套持久化执行机制上。一句话结论:它用一条可恢复的会话日志加上队列承接,让智能体在函数崩溃、实例丢失或客户端断线之后,从中断的那一步继续跑,免去从头再来一遍。
这件事的分量在于它把编码智能体从本地长驻进程搬到云端时最常见的那类故障,用工程手段固定成了默认行为。编码智能体搬上云之后,卡在断电丢活
编码智能体上云,最先崩掉的是会话状态
绝大多数编码智能体框架在本地运行时,会话状态一直放在进程内存里。模型选出一个动作,执行完,回一句,接着问下一个模型。这套流程在单机上很顺,因为进程活着,状态就活着。
搬到云端之后,这个前提就断了。机器会被换掉,磁盘是挂载盘,函数有最大执行时长,队列会重新投递消息,而框架本身对哪些调用已经真正完成一无所知。Vercel 的文档给出的对照实验很直白:同一轮对话、同样超、同样掐断连接,在没有持久化的情况下这轮工作直接作废,步骤会重跑,模型调用次数被重复计费。挂上 durable-harness 之后,会话状态写在持久化日志里,每一轮背后压着一条 Vercel Queues 消息,函数死了就由新函数读日志接着往下走。
这里的关键差别在于已经写进日志的调用会被直接读回,不会重新执行。已经跑完的模型 token 不会被二次计费,这件事直接决定了一份云端账单是否可控。类似机制在推理侧的价值可以对照 llm-d 1.0 的分布式推理改造 来看,那边解决的是吞吐与首 token 延迟,这边解决的是状态丢失。
durable-harness 是什么:一次会话就是一条可恢复日志
durable-harness 是 Vercel Labs 放在 GitHub 上的 TypeScript 仓库,采用 Apache-2.0 协议,仓库创建于 10 月 16 日,到 10 月 22 日累计 178 个 star、9 个 fork、8 个 open issue。npm 上包名 durable-harness 最新版本为 0.0.1-beta.6,总共发布了 8 个版本,创建时间是 10 月 15 日。
它的定位是适配器层。底下那个被它依赖的地基叫 Workflow SDK,同样开源,提供一个叫 World 的抽象,World 同时承担存储与队列两个职责。放在 Vercel 上运行时,World 是 Vercel 自己的实现;本地开发时,World 落到本机文件;自托管时,可以换成 Postgres World,甚至换成任何别的 World 实现。
对上层的编码框架来说,变化被压缩到很小。每个适配器导出一个包,开发者照文档装依赖、注册工具、挂一个 chat 路由就能跑起来。仓库里的快速开始示例用的是 Pi,配 @earendil-works/pi-durable、@earendil-works/chord、@earendil-works/pi-ai 和 just-bash 四个包,模型侧走 Vercel AI Gateway,在 Vercel 部署时用部署自身的 OIDC token 认证。
| 适配包 | 对应框架 | 运行位置 | 成熟度 |
|---|---|---|---|
| durable-harness/pi-durable | Pi | 开发者自己的函数 | 推荐变体 |
| durable-harness/pi | Pi | 开发者自己的函数 | 常规变体 |
| durable-harness/opencode | OpenCode | 开发者自己的函数 | 稳定 |
| durable-harness/fx | libfx 的 fx | 开发者自己的函数 | 稳定 |
| durable-harness/claude-code | Claude Code | 仅 Vercel Sandbox | 实验性 |
| durable-harness/codex | Codex | 仅 Vercel Sandbox | 实验性 |
表里最需要留意的是最后两行。Claude Code 和 Codex 目前标记为实验性,且被限制在会话专属的 Vercel Sandbox 里运行,而 Pi、fx、OpenCode 这三个库形态的框架可以留在开发者自己的函数里。
重放规则 safe 与 never:崩溃后工具到底跑不跑
这是整套机制里最见功力的一个设计。工具调用被中途打断后到底该不该重跑,没有统一答案,因为查询和退款的风险等级完全不同。durable-harness 用 replay 规则把这件事交给开发者逐个声明,文档里给出三个取值。
| replay 取值 | 语义 | 各框架的处理方式 |
|---|---|---|
| safe | 中断后重跑是安全的 | Pi 直接重跑;Claude Code、Codex、OpenCode 告诉模型这次调用可以重复 |
| never | 绝不执行第二次 | 调用前先等日志落盘,模型侧被告知这次可能已经发生,需要先查再动 |
| 其余情况 | 视为可能已执行 | 结果回传给模型,由模型自行判断 |
开发者自己的工具通过一个 replay 字段声明行为。文档给的示例里,get_order 标成 safe,refund_order 标成 never。前者是查询,重复执行没有副作用;后者是退款,重复执行就是真金白银的损失。这两条规则让同一套日志机制能同时承载这两种语义,而不靠模型自觉。
在调用顺序上,durable-harness 还区分了 durability 的 on 与 strict 两种等待策略。durable-harness/pi 默认是 on,重放安全的调用可以不等日志落盘就开跑;Claude Code、Codex、OpenCode 默认是 strict,每次调用前都等日志写完,想让它们也跑在前面的话得显式传 durability: on。对于标记为 never 的调用,所有框架都会等到日志记录完成才执行,自身层面保证不会重复。
工具本身的可用性也在这套框架下被重新定义。文档里的说法是智能体只能拿到你给它的工具,每个 harness 启动时工具集为空。这种设计取向与 OpenCode Console 密钥泄露事件的复盘 形成对照,那次事故里真正的教训同样落在权限边界上,模型能力反而排在其次。
三种文件工作区:文件到底存在哪里
模型选出来的命令和文件从来不在函数自己的磁盘上跑,选项一共三种。差异体现在崩溃之后的残留、成本,以及命令能触达的范围。
| 方案 | 命令执行环境 | 崩溃后文件 | 成本构成 | 适合场景 |
|---|---|---|---|---|
| virtual() | 函数内的 just-bash,虚拟文件系统 | 保存在会话日志中,中断不留痕迹 | 函数 CPU 时间加日志写入 | 短任务与小工作区 |
| virtual({ durable: false }) | 同上的虚拟文件系统 | 每轮结束即清空,模型会收到告知 | 函数 CPU 时间 | 轮内临时工作 |
| sandbox() | 会话专属 Vercel Sandbox 虚拟机 | 保留在 Drive 上,中断的调用可能已部分执行 | Sandbox 与 Drive 用量,首次使用含启动开销 | 真实项目与长任务 |
virtual() 提供的 just-bash 是一个兼容 bash 的 shell,自带 coreutils、grep、sed、awk、jq,没有网络,也没有 node、python 这类真实二进制。它的命令碰不到函数的文件、进程与环境变量。每个改动文件的调用都会把变更连同自己的结果一起提交,因此调用中途崩溃不会留下半截状态,只会整体重跑。文档给的工作区容量是大约 500 个文件,小文件存进日志,大文件以带哈希校验的 blob 形式放在日志旁边。
sandbox() 则是另一套代价结构。harness 仍然跑在函数里,Sandbox 在第一次工具调用时才启动,文件落在会话专属的 Drive 上。Drive 的寿命比 Sandbox 和函数都长,命令写下的内容在崩溃后依然保留,因此一个被中断的改文件调用不会重跑,模型会收到它可能已部分执行的提示。对长工程任务来说,这是把状态保在盘上的必需选项,相关的本地算力路线可以看 VAST 的 EdenLive 单卡实时世界模型。
自托管路径:两个环境变量接上 Postgres
不想把会话存在 Vercel 上,durable-harness 也留了出口。仓库 README 明确写了自托管走 Postgres World,要求 Node.js 22 及以上、@workflow/world-postgres 5.0.4 及以上。建表用一条 bootstrap 命令,重复执行是安全的,升级包之后再跑一次同样可以。
应用侧需要设置两个变量:WORKFLOW_TARGET_WORLD 指向 @workflow/world-postgres,WORKFLOW_POSTGRES_URL 给出数据库连接串,若该变量缺失则回落到读取 DATABASE_URL。另外还需要一个 AI Gateway 的 API key,用于在 Vercel 之外发起模型调用。这个 key 不承担 Sandbox 请求的认证,Sandbox 用的是另一套独立凭据。
Postgres World 的好处是可以部署多台服务器。local World 把会话存在单机文件里,适合开发以及磁盘持久的单节点服务,一旦要扩到多台,就得换 Postgres World、Redis、MongoDB 或 Turso 这些别的 World 实现。fx 这个框架的 World 选择逻辑与其它框架不同,由它自己决定。
安全边界:为什么 Claude Code 与 Codex 只能待在 Sandbox
durable-harness 的安全规则只有一条:函数里只运行开发者自己写的代码,模型只决定参数,不决定在函数里执行的代码。模型选中的命令和文件一律落在虚拟工作区或 Vercel Sandbox,绝不进入函数磁盘、函数进程,也不接触函数的环境变量。
原因是 Vercel Fluid 计算下一个实例会同时服务多个请求,包括其它会话和其它用户的会话。一个在真实磁盘上跑 shell 的工具,能让某个会话的模型读到另一个会话的文件,也能读到函数的环境变量,其中就包括 AI_GATEWAY_API_KEY 和部署自身的 OIDC token。网页、文件、消息里的一处提示注入,就可能把模型推向这条路。
Claude Code 和 Codex 被限定在 Sandbox 的理由写得很直白。这两个是面向单用户的命令行工具,状态存在磁盘上,当前版本能把它们隔离开,但它们发布频繁,没有承诺下一个版本不会留下跨会话的状态。而 OpenCode 内置了服务端和一套替换进程派生器与宿主文件访问的 profile,durable-harness 用同样方式替换掉了它。Pi、fx、OpenCode 是为多会话设计的库形态框架,跑在共享进程里的风险因此更低。
文档也诚实列出了这些控制覆盖不到的地方,例如 chat 路由本身需要额外校验会话归属,Sandbox 启动时的 OIDC token 归属,以及模型自行决定参数时越权调用工具的可能。这些是使用者需要自己补上的部分。
几个必须提前知道的现实约束
durable-harness 当前处于 0.0.x 实验阶段,API 可能在版本之间变动,官方文档在自托管页面明确写了这些集成都是实验性的。仓库里没有 release 记录,版本号停在 0.0.1-beta.6,官方也没有公布延迟或吞吐方面的基准数据。恢复效果目前全部依据 Vercel 自己的声明。
还有一个具体的坑。用 durable-harness/pi 和 OpenCode 时,一轮对话先由 chat 路由自己的请求驱动,最多 60 秒之后交给队列路由接管。交接点上仍在运行的命令会被停掉,不会重跑,因此一轮早期执行的耗时命令,例如安装依赖或跑构建,有可能被切断。文档给出的绕过办法是给 agent.chatRoute() 传 drive: ‘queue’,让队列路由驱动整轮对话,按套餐的最大时长执行。这个限制被标注为 Vercel Queues 当前的短板,官方表示修复在路上。同一份文档还提到 Sandbox 首次启动偏慢时,也可能撞上同样的交接。
这件事对编码智能体意味着什么
durable-harness 本身代码量不大,价值主要在于它把几个此前靠各家自行处理的工程问题固化成了公共约定:会话日志怎么写、工具调用重不重跑、文件放哪里、模型碰不到什么。
对想把编码智能体搬上云的团队,这套工具给了一条现成路径,同时也给了一份清晰的安全边界说明。对正在自研编码智能体产品的团队,replay 规则与 never 语义这套设计值得直接抄走,它的通用性远高于 Vercel 这一家的实现。谷歌在 Harness 自我修补框架 里解决的是模型自身的改进闭环,而这里解决的是执行环境的可靠性,两条线最终要在同一个智能体里合流。
更值得留意的是模型成本侧的连带影响。当每个会话都有可审计的调用记录,重复计费与静默重跑这两类浪费都变得可见,而这恰好是编码智能体从玩具走向企业采购时最先被追问的账目问题。相关讨论在 Replit 从 Anthropic 转向 OpenAI 的成本逻辑 里有更完整的背景。
常见问题
durable-harness 支持哪些编码智能体框架
官方提供 5 个适配包,覆盖 Pi、OpenCode、fx、Claude Code 与 Codex。其中 Pi 分为 durable-harness/pi-durable 与 durable-harness/pi 两个变体,前者推荐。Claude Code 与 Codex 目前标记为实验性,且只能运行在会话专属的 Vercel Sandbox 内。
智能体崩溃后工具调用会被重复执行吗
取决于工具的 replay 声明。标记为 safe 的调用在崩溃后允许重跑,标记为 never 的调用在执行前会等待日志落盘,自身保证不会执行第二次。其余被中断的调用会被回传给模型,由模型判断是否已经发生。日志中已有结果的调用一律直接读回,不会重跑,也不会二次计费。
不想用 Vercel 可以自己部署吗
可以。使用 Postgres World 即可,需要 Node.js 22 及以上与 @workflow/world-postgres 5.0.4 及以上,应用侧设置 WORKFLOW_TARGET_WORLD 与 WORKFLOW_POSTGRES_URL 两个环境变量。本地开发则使用 local World,无需任何配置,会话存在 WORKFLOW_LOCAL_DATA_DIR 指向的目录中。
延伸阅读
- cua 升级 computer-use 2.0,跨系统机群训练评测1 月前
- Ling-3.0-flash-VL 开源:124B 参数仅激活 5.5B,256K 上下文原生多模态1 月前
- LingBot-World 2.0 开源三款模型:1.3B Small 可在消费级单卡实时生成世界1 月前
- Fireworks 发布 FireRouter,编码成本降 57%3 周前
继续读
同一批更新里这几篇值得一看:
想快速查定义?对应的词条在这里:
- Claude词条 · AI 自学笔记
