我们重写了我们的智能体,使其完全在Durable Object中运行,使用Pi、Agents SDK和Code模式(10分钟阅读)
摘要
camelAI重写了他们的编码智能体,使其完全在Cloudflare Durable Object中运行,使用SQLite和R2作为文件系统,并用JavaScript替代了bash。从虚拟机迁移降低了成本和延迟,代码库现已开源。
查看缓存全文
缓存时间: 2026/07/29 18:24
camelAI 最近将其智能体从虚拟机迁移到了 Cloudflare Durable Object 上。它的文件系统存储在 SQLite 和 R2 中,使用 JavaScript 代替 bash 进行脚本编写。团队之所以放弃虚拟机,是因为为每个用户提供一台始终在线且附带磁盘的机器,成本过高,难以扩展。这篇文章解释了团队如何完成此次迁移。camelAI 的代码库最近已开源。
我们重写了智能体,使其完全在 Durable Object 中运行,结合 Pi、Agents SDK 和 Code Mode
我们最近完成了将 camelAI 智能体从虚拟机中迁移出来的工作。该智能体现在运行在 Cloudflare Durable Object 内部,其文件系统驻留在 SQLite 和 R2 中,并使用 JavaScript 代替 bash。大多数团队在完整的 Linux 虚拟机或容器沙箱中运行编码智能体,我们过去也是如此。
我们之所以要摆脱虚拟机,是因为为每个用户提供一台始终在线且附带磁盘的机器,成本过高,难以扩展。难点在于编码智能体默认依赖 Linux 环境。它们被训练为使用 bash,而我们最初发布的框架需要完整的虚拟机,因此我们经历了三次重新设计才达到现在的状态。这样做的代价是,智能体现在只能执行我们为其构建了显式方法的任务,这听起来有限制性,但对产品本身是有益的。
我是 Miguel,camelAI 的 CTO。我们的代码库最近已开源,因此这篇文章中提到的所有代码都可以在 github.com/qaml-ai/camelAI 上找到。我会在相关位置附上链接。以下是我们的演进过程。
第零步:虚拟机时代
我们在 Claude Code 框架上发布了产品,该框架需要完整的虚拟机才能运行。我们尝试了几家虚拟机提供商,但没有一家能满足我们对持久性和性能的要求,最终我们构建了自己的容器服务。那篇文章仍然在线,但我们已不再运行任何相关基础设施。
容器服务虽然有效,但过于沉重。为每个用户提供一台始终在线的虚拟机成本高昂,同时在快速附加磁盘上保存每个用户的文件也同样昂贵。扩展意味着需要扩展真正的机器和真正的磁盘,这对于我们目标用户数量来说将是极其昂贵的。因此,我们没有在虚拟机编排上耍小聪明,而是开始围绕“根本不需要虚拟机”这一目标进行设计。
第一步:将智能体从虚拟机中移出
Claude Code 框架与其虚拟机密不可分,因此第一步是构建我们自己的框架。我们基于 Mario Zechner 的开源编码智能体 pi 进行构建。pi 是一个库栈。它的最高层假设有正常的操作系统,但底层提供了智能体原语,例如智能体循环和状态管理,而不关心它们在何处运行。我们没有修改任何 pi 代码。我们导入了这些底层库,并在其之上构建了自己的框架,使其运行在 Cloudflare Durable Object 内部,而非 Linux 环境中。
Durable Object 是一个小型有状态计算实例,它在 Cloudflare 的边缘节点上按需启动,靠近创建它的用户。每个聊天线程拥有自己的 Durable Object,这本身就比将所有流量路由到集中的虚拟机主机降低了延迟。
在这个阶段,我们仍然保留了虚拟机,但智能体不再运行在虚拟机内部。当需要运行命令时,它会远程调用虚拟机。Anthropic 对其托管智能体也采用了相同的拆分方式——大脑与双手分离。这给我们带来了一些好处:
- 智能体在虚拟机启动之前就开始响应,因为它不需要等待机器启动。
- 虚拟机可以在智能体继续工作时进入休眠状态,或者如果当前轮次不需要执行任何命令,则根本不需要启动。
- 一个大脑可以控制多双手。单个智能体可以同时操作多个虚拟机。
我们将这些“双手”称为项目。每个项目都附带一个用于执行命令的虚拟机,以及一个通过 Cloudflare Artifacts 以编程方式创建的 git 仓库,Artifacts 是一种兼容 git 的存储,可以从 Worker 动态预配置。智能体实际上并不知道它在虚拟机外部运行。它仍然拥有 bash,并且像其他任何编码智能体一样工作。
问题是这只解决了延迟问题,其他问题依然存在。我们仍然为每个用户配备了一台虚拟机,因此原始设计中的成本和扩展问题依旧。
第二步:移除虚拟机
下一个版本保留了相同的项目结构,但移除了其后端的虚拟机。现在,每个项目都由一个位于 Durable Object 内部的文件系统支持,对于较大的文件则使用 R2。
这不是我们发明的。Cloudflare 的智能体团队构建了 Shell,一个实验性的 Worker 文件系统和执行运行时,我们大量重用了他们的代码。其机制很简单。Durable Object 的存储是一个具有 10 GB 上限的 SQLite 数据库,每个行都有最大大小限制。小文件直接存储在 SQLite 行中。超过约 1.5 MB 的文件会被写入 R2,SQLite 行中只保留一个指针。对于智能体来说,它看起来就像一个普通的文件系统,但底层是数据库和对象存储,因此持久化是存储数据,而不是我们必须保持运行的基础设施。
版本历史仍然通过 Artifacts 管理,因此每个项目都保留着 git 历史,而我们无需托管 git 服务器。
第三步:移除 bash
移除 bash 感觉像是一次大刀阔斧的改革。编码智能体被训练为使用 bash,而正是 bash 导致每个人最初都将其运行在虚拟机中。这不仅是成本问题。一个拥有 bash 和网络访问权限的智能体需要凭据才能执行任何有用的操作,而我们尝试的认证代理 URL 方法变得越来越繁琐且难以强制实施。
因此,我们移除了它。智能体现在使用 JavaScript 编写脚本,通过 Code Mode 和 Cloudflare 的动态 Worker 加载器执行。每次执行都在一个全新的 V8 隔离环境中运行,启动时间仅为毫秒级,内存消耗只有几兆字节。沙箱预先加载了用户的数据连接以及平台能提供的所有方法。凭据永远不会进入沙箱。智能体调用连接的方法,认证在服务端完成。
当你审视智能体实际使用 bash 的场景时,会发现失去它带来的影响比预期的要小。大多数操作是文件操作,智能体为此拥有原生工具。我们提供了读取、写入和编辑功能,以及我们自己实现的 grep 和 glob 功能。这覆盖了 80% 的常见需求。其余的是针对特定任务的特定命令,而它们变成了显式方法:
- 通过代理进行的
wrangler deploy变成了我们完全控制的deploy_project方法。由于我们确切知道部署何时发生,我们可以拦截它并自动打开实时预览。之前,我们必须嗅探代理的 wrangler 流量来猜测哪个线程部署了某些内容。 - 构建用户应用和运行 Python 笔记本变成了各自独立的方法,两者都由短生命周期的容器支持。
我们为这两项任务保留了容器,因为它们确实需要 Linux。用户应用使用 Vite、Tailwind 和 React Router 构建,添加依赖意味着运行 bun install。我们考虑过在 Worker 内部运行构建,因为正在构建的本身就是一个 Worker,但这条路径的支持并不完善,而且 Worker 有 128 MB 的内存限制和一小部分 CPU。构建过程会很慢,并且许多项目会超出内存限制。因此,构建会通过 Cloudflare Sandbox SDK 启动一个容器,将项目复制进去,运行任务,返回结果,然后关闭容器。笔记本运行也是同样的方式。我们仍然使用完整的 Linux,但仅限于实际需要的几秒钟工作。
诚实的缺陷是我们必须预见智能体的需求。使用 bash 时,它能够自己解决问题。现在,如果缺少某项功能,我们必须添加它。在实践中,这种压力对产品是有益的,因为它迫使我们思考用户正在做什么,并为此构建一流的路径,而不是让智能体临场发挥。
还有一个意想不到的好处。Bash 是开放式的,而能力较弱的模型在开放式环境中表现挣扎。使用一个更小的显式方法集合,它们的表现显著提升,这很重要,因为保持 camelAI 运行成本低廉正是这种架构的初衷。
我们现在所处的阶段
目前的架构是:智能体及其文件系统使用 Durable Objects,大文件使用 R2,git 历史使用 Artifacts,框架使用 pi,执行环境使用 Code Mode 和动态 Workers。它像其他任何 Cloudflare 应用一样部署,无需管理外部容器服务。
动态 Workers 按执行次数计费,而非按运行时长计费。数千次执行的成本大约相当于我们之前评估的服务上使用容器几分钟的成本。延迟很低,因为所有内容都在靠近用户的边缘节点上运行,扩展问题由 Cloudflare 负责,而非我们操心。
用户仍然可以构建和部署全栈应用至实时 URL,智能体仍然可以读取、写入、搜索和部署。从用户的角度来看,一切都没有改变。
TL;DR
我们从在自建虚拟机服务上运行 Claude Code 框架开始,这种方式昂贵且难以扩展。首先,我们将智能体本身移入 Cloudflare Durable Object,并让它远程控制虚拟机,这解决了延迟问题,但成本问题依旧。然后,我们基于 Cloudflare 的 Shell 项目,用存储在 Durable Object SQLite 和 R2 中的文件系统完全替代了虚拟机,并通过 Cloudflare Artifacts 管理 git 历史。最后,我们移除了 bash,通过 Code Mode 和动态 Workers 为智能体提供了一个 JavaScript 沙箱,并为其提供了用于部署、构建和笔记本的显式方法。结果是成本降低了好几个数量级,延迟更低,运维更简单,并且对较小的模型也更友好。所有代码已在 github.com/qaml-ai/camelAI 开源。
相似文章
@akshay_pachaar: https://x.com/akshay_pachaar/status/2053166970166772052
The article discusses a shift in AI agent tool usage from the 'MCP vs CLI' debate to 'Code Mode,' where agents write code to dynamically import tools, significantly reducing context window usage. It highlights Anthropic's approach and Cloudflare's implementation, demonstrating a 98.7% reduction in token consumption for specific tasks.
我刚刚为了可靠性重写了整个代理基础设施,有人也这样做吗?
作者描述了在遭遇级联故障后,使用DBOS持久化执行重写其AI代理基础设施以提高可靠性的经历,并向社区询问类似的经历、工具选择以及自建与购买决策。
为何我们放弃Cloudflare Durable Objects
Wire是一个面向AI代理上下文容器的平台,因结构限制——向量索引分离、计算与数据共置、部署灵活性及缺乏自托管支持——正从Cloudflare Durable Objects迁移至Fly Machines上的自定义运行时。新架构降低了延迟并实现了专用容量。
在遇到 LangGraph 天花板后,我构建了自己的智能体运行时——将 UI 作为图节点,Postgres 持久化,零编排成本
作者介绍了 cascaide,这是一个全栈智能体运行时和 AI 编排框架,使用 TypeScript 编写,可在任何支持 JS/TS 的环境运行。它提供 UI 作为图节点、持久化 Postgres 检查点、零编排成本,并且设计为可自托管,无供应商锁定。
AI 代理依然拉胯,于是我自己造了一个
作者构建了一款自定义 AI 代理应用,封装了 Claude Code 并即将支持 Codex,侧重于可组合的工作流,并期待社区反馈。