让每个 harness 都具备持久性(7 分钟阅读)

TLDR AI 工具

摘要

Vercel 发布开源工具 durable-harness,让 Pi、OpenCode、Codex、Claude Code 等编码代理在云端具备持久性,可在崩溃或函数超时后从日志恢复会话与工作。

Vercel 的 durable-harness 让 agent harness 可以开箱即用地在云端持久运行。它支持用户在 Vercel 上或在自己的 Postgres 实例上自托管,从而让 Pi、OpenCode、Codex 和 Claude Code 持久运行。durable-harness 确保会话能够在中途崩溃后存活,即使浏览器断开连接也能继续运行。在执行过程中丢失函数的回合,会在新的函数上从日志继续执行,无需手动重试,也不会重复已完成的工作。
查看原文
查看缓存全文

缓存时间: 2026/10/21 18:20

# 让每一个 Harness 都具备持久性 Source: https://vercel.com/blog/making-every-harness-durable 开发者们已经习惯于通过 Pi、Claude Code 等 harness 在本地运行编码 agent。它们是日常生成代码的可靠工具,但要让 agent 全天候运行,就需要你的电脑一直开着、不休眠——于是就有了那些"合上笔记本盖子"的表情包,以及 Mac mini 需求量的暴涨。 Cormac (https://x.com/cormachayden_/status/2050773588521992642)https://x.com/cormachayden_/status/2050773588521992642 软件工程师使用 agent 前 vs 使用 agent 后 Harness 现在可以在云端运行,所以你可以放心合上盖子了。 #### 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#running-harnesses-in-the-cloud)在云端运行 Harness Harness 的工作负载正在向云端迁移。独立开发者正在部署始终在线的编码 agent 工作流,像 OpenWork 和 HanaAgent 这样的项目将开源 harness 用作其应用的引擎,工程师们也在打造自己的 harness 产品。 这些 harness 是为长驻进程设计的,会将会话保存在内存中,agent 任务通常运行几分钟到几小时。在本地,harness 可以直接、持续地访问同一磁盘上的工作区和会话历史,因此恢复被中断的工作只需一个简单的提示词。 在云端,你可以用一台 VM 和持久化存储来复制这种配置,但 VM 不会自动记录哪些模型调用和工具调用已经完成,也不会在崩溃后恢复被中断的轮次。一个失败的轮次或网络错误就意味着工作丢失。 另外,并非通过 harness 执行的每个 agent 任务都需要沙箱。数据库查询和 API 调用可以在函数中安全运行,这也意味着所需的凭据永远不必进入沙箱。 ### 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#making-every-harness-durable)让每一个 Harness 都具备持久性 我们构建了 eve (https://eve.dev/) 和 fx (https://fx.sh/),它们开箱即用地在云端持久运行。今天,我们推出 `durable-harness`,它将这种持久性扩展到任何 harness,包括 Pi (https://github.com/earendil-works/pi)、OpenCode (https://opencode.ai/)、Codex (https://github.com/openai/codex) 和 Claude Code (https://docs.claude.com/en/docs/claude-code/overview)。它使用与 Workflow SDK (https://workflow-sdk.dev/) 相同的机制,能够在崩溃和函数超时后存活下来。 有了 `durable-harness`,你可以: - 在 Vercel 上持久运行任何这些 harness - 用自己的 Postgres 实例自托管 harness - 让你本地的 harness 工作流具备持久性 - 构建属于你自己的持久 harness ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#sessions-that-survive-a-crash-mid-turn)能在轮次中途崩溃中幸存的会话 以一个编码 agent 为现有应用添加定价页面为例。agent 读取现有页面,编写新页面,发起一个 pull request,然后等待 CI 完成后再报告结果。 如果它的函数在 CI 还在运行时超时了,会发生什么? 在下面的演示中,终止函数或断开连接,比较两个 agent 如何恢复以及哪些工作被重复执行。 没有持久性时,agent 会停止工作,直到手动重启,已完成的工作也会被不必要地重复。 有了 `durable-harness`,一个新的函数会从日志中读取已完成的结果并恢复该轮次。如果浏览器断开连接,agent 会继续运行,重新连接时会从日志中追赶进度。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#logs-and-queues)日志与队列 `durable-harness` 是我们开源产品组合中最新的包。它是一组适配器和工具,让你已经在使用的 harness 将会话保存在 World(https://workflow-sdk.dev/worlds)中——Workflow SDK 的持久化后端。 它将每一轮的每个步骤写入持久日志。在模型调用或工具调用运行之前,它记录一个意图,当调用完成时,它记录结果。该轮次本身由一条队列消息兜底,因此如果它的函数挂掉,队列会将该轮次交给一个新的函数,该函数读取日志并从中断处继续。 崩溃之后,日志会告诉你什么完成了、什么在运行中、什么从未开始: - 已完成的调用不会再次运行,因为它们的结果是从日志中读取的,所以你永远不会为已完成的模型调用的 token 重复付费。 - 模型会记住之前的调用,而不是重复执行它们。 - 被标记为 `replay: 'safe'` 的中断工具会重新运行。任何其他工具会以"可能已执行"的状态返回给模型,由模型决定是否进行检查。 那些保存自己的会话文件的 harness,比如 Claude Code 的 transcript、Codex 的 rollout 和 OpenCode 的 event store,它们的每条记录都会被镜像到日志中,这样一个新的函数或 Sandbox 就能从中重建这些文件。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#pick-your-harness)选择你的 Harness fx (https://fx.sh/) 和 eve (https://eve.dev/) 本身就具备持久性。对于其他 harness,`durable-harness` 从外部为其添加持久性,无需修改 harness 本身。 每个 harness 的完整设置指南,请查看 文档(https://vercel.com/docs/agent-harnesses/harnesses)。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#runs-everywhere)随处运行 持久化 agent 需要每个步骤的日志、在进程死亡时恢复轮次的方法,以及一个供观察者使用的流。 Workflow SDK 将这三者封装在一个接口背后:World(https://workflow-sdk.dev/worlds)。 ``` import type { Queue, Storage, Streamer } from '@workflow/world' interface World extends Storage, Queue, Streamer {} ``` 一个 World 就是同一个接口背后的日志、兜底和流。 `durable-harness` 写入这个接口,所以你可以将同一个 agent 从一个 World 迁移到另一个 World,而无需修改其代码: - 在 Vercel 上,它使用 Vercel World(https://workflow-sdk.dev/worlds/vercel)。 - 在开发环境中,它使用你机器上的文件(https://workflow-sdk.dev/worlds/local)。 - 在你自己的服务器上,它使用 Postgres(https://workflow-sdk.dev/worlds/postgres)、MongoDB、Redis 或 Turso。 两个变量即可让任何使用 `durable-harness` 的 agent 迁移到 Postgres。 ``` WORKFLOW_TARGET_WORLD="@workflow/world-postgres" WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database" ``` 将 durable-harness 重定向到使用 Postgres World 以获取持久性。 如果需要更多控制,比如设置命名空间或在服务器启动时恢复会话,可以在代码中创建 World 并将其传递给你的 agent: 更多详情请查看 自托管文档(https://vercel.com/docs/agent-harnesses/self-hosting)。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#see-every-session-in-agent-runs)在 Agent Runs 中查看每个会话 Vercel 上的 Agent Runs(https://vercel.com/docs/agent-harnesses/observability)会显示每个会话的追踪信息,包括每次模型调用和工具调用。崩溃后恢复的轮次会出现在同一个追踪中,因此你可以准确看到轮次在哪里停止以及在哪里恢复。 一个 Pi 会话在 Agent Runs 标签页中,其中有一次模型调用被中断并重试。 Agent 只拥有你赋予它的工具,它们运行在你的函数中。每个工具都会说明如果崩溃中断了它应该发生什么。标记为 `'safe'` 的工具(比如一次查询)可以再次运行。标记为 `'never'` 的工具(比如一次退款)永远不会运行两次:模型会被告知该调用可能已经发生,因此它可以在再次尝试之前先进行检查。 下面是上面演示中的编码 agent 可能如何定义其工具的示例: - 读取文件或检查 CI 状态的工具被标记为 `'safe'`,因此可以自动重新运行。 - 发布 PR 评论的工具被标记为 `'never'`,以避免评论重复发布。 以下示例以一个处理退款的 agent 为例,为每种策略定义了一个工具: ``` import { pi } from 'durable-harness/pi'; import type { HostTool } from 'durable-harness'; import { getOrder, refundOrder } from './orders'; // 你自己的代码 const tools: HostTool[] = [ { name: 'get_order', description: 'Looks up an order', replay: 'safe', // 崩溃后可以安全地重新运行 inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] }, execute: async ({ id }) => JSON.stringify(await getOrder(id)), }, { name: 'refund_order', description: 'Refunds an order', replay: 'never', // 永远不会运行两次 inputSchema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] }, execute: async ({ id }) => JSON.stringify(await refundOrder(id)), }, ]; export const agent = pi({ model: 'anthropic/claude-sonnet-5.5', tools }); ``` 一次查询可以在崩溃后重新运行,而一次退款永远不会运行两次。 关于每个 harness 的内置工具,比如它的 shell 和文件编辑,请继续往下读。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#files)文件 当 harness 需要 shell 和文件时,直接在共享的无服务器函数主机上运行并不安全。多个会话可能共享同一个函数,而它的文件系统在调用之间不保证持久。 为了解决这个问题,`durable-harness` 提供了几个选项: - `virtual()` 将文件保存在会话的日志中,配备一个与 bash 兼容的 shell,它无法访问你函数的文件或网络。它适合短任务和小型工作区,无需启动整个沙箱。 - `virtual({ durable: false })` 将文件保存在内存中用于临时工作,并在每个轮次结束时清除它们。 - `sandbox()` 为会话提供其专属的 Vercel Sandbox(https://vercel.com/docs/sandbox),`/workspace` 位于一个 Drive(https://vercel.com/kb/guide/vercel-drives)上,该 Drive 比任何沙箱和任何函数都活得更久。它适合真实项目和长时间运行的工作。 在文档(https://vercel.com/docs/agent-harnesses/tools#files)中可以并排比较这三种方式。 每个 harness 的内置工具都运行在你选择的工作区上。对于 Pi,`CodingTools` 会安装 Pi 自己的 `read`、`write`、`edit` 和 `bash`,而 `virtualEnv()` 或 `sandboxEnv()` 为它们提供一个工作区: ``` import { BACKGROUND_CONTEXT as ctx } from '@earendil-works/chord/context'; import { createRegistry, Harness, type Storage } from '@earendil-works/pi-durable'; import { CodingTools, gatewayModels, resumeWith, virtualEnv } from 'durable-harness/pi-durable'; const models = gatewayModels(); const registry = createRegistry(); registry.install(CodingTools); // Pi 的 read、write、edit 和 bash // 文件保存在会话的日志中,或使用 sandboxEnv() 获取会话专属的 Vercel Sandbox const env = virtualEnv(); export const open = resumeWith((storage: Storage) => Harness.open(storage, { models, registry, env }, ctx)); ``` 在虚拟工作区或 Vercel Sandbox 中为 Pi 提供其编码工具。 对于 OpenCode,将工作区作为 `workspace` 传入;对于 fx,将其传给 `fxTools()`。Claude Code 和 Codex 始终在 Vercel Sandbox 中运行,因为它们需要真实的 shell 和文件系统,不适合多租户使用。一旦你通过 `builtins: true` 启用它们,它们的内置工具就可以在会话的 Drive 上工作。每个 harness 指南(https://vercel.com/docs/agent-harnesses/harnesses)都列出了它的内置工具。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#documents)文档 有些状态属于会话但不属于对话本身,比如待办清单、计划或 agent 填写的表单。文档是带类型的 JSON,存在于会话的日志中,因此它能在崩溃后幸存并随会话一起迁移。 工具可以通过 `io.docs` 编辑文档,每次编辑都会与工具的结果一起提交到日志中,因此文档的持久状态与工具调用本身保持一致: ``` import { defineDoc, type HostTool } from 'durable-harness'; export const Todos = defineDoc<{ items: string[] }>({ kind: 'app.todos', version: 1, initial: () => ({ items: [] }), }); export const addTodo: HostTool<{ item: string }> = { name: 'add_todo', description: 'Adds a todo', replay: 'safe', inputSchema: { type: 'object', properties: { item: { type: 'string' } }, required: ['item'] }, async execute({ item }, io) { const todos = io.docs!.update(Todos, (draft) => { draft.items.push(item); }); return `${todos.items.length} todos`; }, }; ``` 定义了一个待办清单文档和一个向其中添加条目的工具。 你的应用可以读取和编辑同一个文档,而无需自建数据库,聊天路由甚至可以将每次变更流式传输到你的 UI: ``` import { agent } from '@/lib/agent'; import { Todos } from '@/lib/todos'; // 在路由或 webhook 中:无需自建数据库即可读取列表 const { value } = await agent.sessions.doc(sessionId, Todos); // 在聊天路由中:将每次变更流式传输到浏览器 export const { GET, POST } = agent.chatRoute({ authorize, docs: [Todos] }); // 你的鉴权检查 ``` 从你的应用中读取待办清单并将其变更流式传输到你的 UI。 实际上,前面提到的 `virtual()` 工作区就是建立在文档之上的:它的文件树是会话日志中的一个文档,每次工具调用的变更都与结果一起提交。 了解更多关于 工具、文件和文档(https://vercel.com/docs/agent-harnesses/tools)以及 如何保护你的 agent 安全(https://vercel.com/docs/agent-harnesses/security)的信息。 ## 复制标题链接 (https://vercel.com/blog/making-every-harness-durable#wrapping-up)总结 开发者选择一个 harness,看中的是它对提示词、工具和上下文的处理方式。它如何在崩溃中存活,不应是需要操心的事情。有了 `durable-harness`,一个在中途失去函数的轮次会在新的函数上从日志继续,无需手动重试,也不会重复已完成的工作。自托管和可移植性开箱即用,因为它们构建在 Workflow SDK 的 World 接口之上。 `durable-harness` 已在 GitHub 上 开源(https://github.com/vercel-labs/durable-harness)。 **让你的 Harness 具备持久性** 使 Pi、OpenCode、Claude Code 和 Codex 在 Vercel、Postgres 或你的笔记本电脑上都具备持久性的适配器。阅读文档(https://vercel.com/docs/agent-harnesses)

相似文章

@ludamn513: https://x.com/ludamn513/status/2108809228978880988

X AI KOLs Timeline

文章以 Pi Durable 为切入点,拆解 Agent Harness 架构的演进:将持久化系统、agent loop 与执行环境解耦,实现无损状态恢复、codemode 工具调用以及 server/client 分离,并提出一套可 scale up 的 long-time Harness 架构设计,目标是将硬件资源消耗降低一个数量级。

@JoshARosen: https://x.com/JoshARosen/status/2107840212915077350

X AI KOLs Following

一篇架构对比文章,分析 Pi Durable 与 OpenCode 的设计差异:Pi Durable 将模型生成、工具调用等序列化为可调度的持久任务,使工作不再绑定于单个进程,而传统会话式 harness(如 OpenCode、Claude Code、Codex)仍依赖进程内的活动 agent 循环。

Harness by Autonomous

Product Hunt

Harness by Autonomous 推出了一款开源的编码代理指挥中心,可在 Mac 和 Linux 上统一管理 Claude Code、Codex、Hermes 等各类编码代理,并支持终端、桌面应用以及 iOS/Android 语音应答,还扩展到 CAD、电路、机器人等领域。