现在,大多数 Coding Agent 都运行在完整的 Linux 虚拟机或容器中,以保障 Agent 安全地读取文件、执行 Bash 命令、安装依赖并运行项目。目前来说,这个方案够用,也符合模型已经形成的工具使用习惯。
一开始,开源 AI 应用构建项目 camelAI采用的也是这条路线:基于 Claude Code Harness 构建 Agent,并为每位用户配置一台带持久磁盘的虚拟机。
但随着用户规模的扩大,这套架构逐渐暴露出成本和扩展问题。常驻虚拟机要持续占用计算资源,用户文件还要保存在高性能磁盘中。每增加一名用户,平台都要增加相应的机器和存储资源。
camelAI 最终选择把 Agent、文件系统和代码执行逐步拆出虚拟机。拆出后,Agent 会运行在 Cloudflare Durable Object 中,项目文件则进入 SQLite 和 R2。像文件处理、部署之类的常见操作会通过 JavaScript 沙箱执行,只有构建和 Notebook 等依赖完整 Linux 环境的任务,才会临时启动容器。
因此,标题说的“Coding Agent 不需要 VM”,重点在于 Agent 不用长期绑定一台完整的虚拟机。Linux 依然保留,只是从默认运行环境转为按需调用的执行资源。

图 1:Coding Agent 运行架构演进
会话与执行环境的耦合
传统 Coding Agent 往往将 Agent Harness、会话状态、项目文件和命令执行集中在同一台虚拟机或容器中。这样一来,一旦运行环境发生故障,任务状态和会话恢复也可能受到影响。
Anthropic 早期的 Managed Agents 架构就遇到过类似问题。当时,Session、Harness 和 Sandbox 被放在同一个容器中,由容器同时承担会话保存、Agent Loop 和代码执行。实际运行中,容器失效连带导致 Session 丢失;进程卡死后,故障难以快速定位,因为问题可能出在 Harness、事件流或底层容器。
因此,架构拆分的第一步是把 Agent Harness 从虚拟机中迁出。
Agent 与执行环境的解耦
Claude Code Harness 与 Linux 环境结合比较深,因此需要重新构建一套 Harness。camelAI 新 Harness 的实现使用 Pi 提供的底层组件,包括 Agent Loop 和状态管理,并将这些组件运行在 Cloudflare Durable Object 中。
Durable Object 是一种有状态计算实例。每个对话线程可以拥有独立的实例和持久状态,需要处理请求时被唤醒,空闲则进入休眠。Agent 因此可以保持稳定身份和会话状态,同时避免长期占用计算资源。
在这个阶段,系统仍然保留虚拟机,但 Agent 已经不再住在里面。只有要执行命令时,Harness 才会远程调用对应的 VM。
整个系统由此被拆成两个部分:
Agent 负责推理、规划、上下文和状态管理,相当于系统的“大脑”;
虚拟机负责执行命令、安装依赖和操作项目,相当于系统的“手”。
完成拆分后,Agent 不用等待 VM 启动就能开始响应;当前任务不涉及命令执行时,虚拟机可以继续休眠;同一个 Agent 也可以同时控制多个执行环境。
不过,这一步主要是为了改善启动速度和请求延迟。只要每位用户仍然对应一台虚拟机,机器和磁盘带来的成本压力就依然存在。
Anthropic Managed Agents 后来也采用了类似思路。早期架构将 Session、Harness 和 Sandbox 放在同一个容器中,后来将三者拆成独立接口:Session 负责保存事件日志,Harness 运行 Agent Loop,Sandbox 则提供代码执行和文件编辑能力。

图 2:Agent 的“脑手分离”。Session 负责保存历史,Harness 负责推理循环,Sandbox、工具和外部系统作为可替换的执行端
经过架构拆分,Harness 不再依赖某个固定容器。执行环境发生故障后可以单独替换,新的 Harness 也能够读取外部保存的历史事件并恢复任务。Sandbox 则被封装为标准化的 execute(name, input) 接口,只有任务确实需要执行代码时才会创建。
这项改造还带来了明显的延迟收益。Anthropic 公布的数据显示,首 Token 延迟的 p50 下降约 60%,p95 下降超过 90%。模型推理无需再等待容器创建、仓库克隆和进程启动,因此可以更早开始响应。
文件系统的数据化
移除常驻 VM 后,下一步是处理项目文件。
在传统架构中,文件系统依附于虚拟机磁盘。机器与磁盘解绑后,Agent 仍然需要一个能够长期保存文件、支持读写编辑,并维持完整项目结构的工作区。
一种实现方式是在 Durable Object 中提供虚拟文件系统:小文件直接写入 SQLite,较大的文件则存入 R2,SQLite 只保留元数据和对象引用。文件超过约 1.5 MB 后会转入对象存储,以避开单行数据的大小限制。对 Agent 来说,这套系统仍然表现为普通文件系统,可以照常读取、写入、编辑、搜索和比较文件,无需关心数据实际存放在 SQLite 还是 R2 中。
但底层的持久化方式已经发生了变化,项目从长期挂载的磁盘迁移为数据库和对象存储中的持久数据。Git 历史则继续交给兼容 Git 的存储服务管理,这样既能保留完整的版本记录,也不用额外运行 Git 服务器。即使虚拟机关闭,项目文件、目录结构和提交历史仍然可以完整保留。
完成这一步后,Agent 状态和文件系统都已离开虚拟机,剩下的主要依赖来自 Bash。
Bash 与受控代码执行
一般来说,Coding Agent 会频繁地调用 Bash,用它去搜索文件、安装依赖、运行测试、执行脚本和调用部署工具。借助 Bash,Agent 可以自由组合系统命令、访问文件和调用外部服务,因此需要运行在完整的 Linux 环境中。
这种开放式执行方式也扩大了权限风险。当 Agent 通过 Bash 调用数据库、代码仓库、云平台等外部服务时,执行环境还得获得相应的访问凭证。凭证进入沙箱后,Agent 生成的代码也可能直接访问这些敏感信息。
一种替代方案是移除通用 Bash,让 Agent 生成 JavaScript 代码,并通过 Code Mode 和 Dynamic Workers 在独立的 V8 Isolate 中执行。虽然每次代码执行都会创建一个新的 Isolate,但其启动通常只需毫秒级时间。执行环境默认无法访问网络和外部资源,平台只向沙箱提供经过明确授权的文件读写、数据连接和项目部署等能力。相关凭证不会进入沙箱,身份验证统一由平台侧完成。
Agent 日常通过 Bash 完成的大部分操作,其实可以被少量显式工具覆盖:
read、write和edit负责文件读写与编辑;grep和glob负责内容搜索与路径匹配;deploy_project负责项目部署;应用构建和 Notebook 运行则分别封装为独立方法。
显式方法还能够提供更准确的执行信息。过去直接运行 wrangler deploy 时,平台只能从代理流量中推测部署行为;封装为 deploy_project 后,系统可以明确记录部署时间、所属项目、执行状态和最终地址,并在部署完成后自动打开在线预览。
Bash 提供的是开放的通用执行环境,显式方法则把常用操作收敛为边界清晰的平台能力。命令执行范围由此更加可控,也更容易加入权限管理、审计记录、超时限制和错误处理。
Linux 能力的按需回归
一些任务仍然离不开完整的 Linux 环境。前端项目构建可能涉及 Vite、Tailwind、React Router,以及通过 bun install 安装依赖;Python Notebook 也可能需要系统包、原生库和更大的运行内存。这类任务很难全部交给资源有限的 Worker 或轻量 JavaScript Isolate。
因此,系统仍然保留短时容器。当 Agent 发起构建任务时,平台会临时启动一个 Linux 容器,将项目文件复制进去,完成依赖安装和项目构建,再把结果同步回来。任务结束后,容器随即关闭。Notebook 运行也采用类似流程。这样既保留了完整 Linux 环境的兼容性,又把容器的使用时间压缩到真正需要它的几秒或几分钟。
Cloudflare 在 Project Think 中进一步将这套思路整理为一条“执行阶梯”:
Workspace:通过 SQLite 和 R2 提供持久文件系统;
Dynamic Worker:运行 Agent 生成的 JavaScript;
npm 运行环境:为代码补充所需依赖;
Browser:处理网页访问与自动化操作;
Sandbox:执行
git clone、npm test、cargo build等需要完整 Linux 环境的任务。
Agent 默认从更轻量的运行环境开始,再根据任务复杂度逐级调用更完整的执行能力。

图 3:Coding Agent 的执行阶梯。文件工具 → JavaScript Isolate → npm / Browser → 短时 Linux Sandbox
显式能力的边界与收益
移除 Bash 有好处,也有坏处。这个坏处就是平台需要提前定义 Agent 可以使用哪些能力。
在开放式 Shell 中,Agent 可以临时查找命令、安装工具,并自由地组合执行步骤。而迁移到显式方法后,一旦缺少某项能力,就要为它补充对应的接口。这种约束会增加前期的工具搭建成本,但也变相地推动高频操作的标准化工作。类似部署、构建、文件搜索和 Notebook 执行这样的任务,可以采用固定的输入输出、权限范围和错误结构,整个执行过程也变得更容易观测、排查和恢复。
工具范围缩小后,模型的选择难度也会下降。开放式 Bash 包含大量命令、参数和组合方式,能力较弱或成本较低的模型容易选错工具、生成无效命令。显式方法让工具数量变少、边界变清晰,小模型也更容易稳定地完成任务。
类似的能力设计也出现在其他 Agent 运行时中。LangChain 的 Code Interpreter 会从一个默认无法访问文件、网络和依赖的小型运行时开始,再由 Harness 明确注入所需能力;完整容器仍然保留,用于处理确实依赖完整计算环境的任务。
由此形成了两种常见模式:Agent 可以运行在 Sandbox 内部,或是把 Sandbox 作为普通工具按需调用,运行在外部。后者将 Agent 状态和凭证保留在执行环境之外,即使沙箱发生故障,也不会直接影响会话状态。
Coding Agent 的分层运行时
经过拆分,一套 Coding Agent 运行时由下面这些相对独立的部分组成:
Harness 负责 Agent Loop 和会话状态管理,运行在 Durable Object 中;
项目文件保存在 SQLite 和 R2;
Git 服务独立保存版本历史;
常见代码通过 JavaScript Isolate 执行;
外部操作通过显式方法和受控凭证完成;
构建、测试和 Notebook 等任务按需启动 Linux 容器。
从用户角度看,Agent 依然可以读取、编辑、搜索、构建和部署项目,但底层已经不再依赖一台长期运行的完整机器。
这也是“Coding Agent 不需要 VM”更值得讨论的地方。Coding Agent 真正需要的是状态、文件、工具和计算能力,而这些能力可以分布在不同的基础设施中。VM 仍然可以承担重型计算和完整 Linux 任务,只是不再承载全部能力。Agent 可以先在成本更低的持久运行时中完成推理和状态管理,再根据任务需要临时调用更重的执行资源。
当推理、存储和执行完成解耦后,Harness、文件系统和沙箱都可以独立替换,不同任务也能选择更合适的计算环境。这样既能减少空闲资源成本,也能让权限边界、故障恢复和工具调用过程更加清晰。