Pi 中的 Codemode:用于编排工具调用的 JavaScript 沙箱

概述 – Codemode 让 LLM 能够在 harness 内部运行简短的 JavaScript 脚本来编排工具调用

Codemode 是一个沙箱化的 JavaScript 环境(基于 WASM 运行时的 QuickJS),它位于 Pi 的 harness(控制端)一侧,而不是运行 bash 工具的执行沙箱中。通过编写微型脚本,LLM 可以组合多个工具调用、存储中间状态并调用 MCP API,而无需将所有中间输出发送到模型的 token 窗口中。这减少了交互轮数,避免了上下文溢出,并使复杂的工作流(例如图像生成、分类、子代理循环)变得可行。


Codemode 的工作原理 – 连接大脑与双手之间的语言桥梁

  • 大脑与双手 – 大脑(受信任的 harness)和 双手(执行 bash 工具的 Unix 沙箱)是具有不同信任级别的隔离文件系统。Codemode 在大脑中运行,为 LLM 提供了一个与 harness 交互的可编程接口。
  • 将工具调用作为 JavaScript 函数 – 在沙箱内,LLM 可以像调用普通异步函数一样调用 tools.bash、models.generateImages、models.classify 等。沙箱返回结构化对象(JSON)而非原始文本,因此模型接收到的是整洁的数据。
  • 状态持久化 – 调用 store(key, value) 会将数据写入会话记录;后续调用可以使用 load(key) 检索它。这使得多步推理无需重新发送大量负载即可实现。
  • 并发性 – 由于环境是 JavaScript,LLM 可以启动 Promise.all 来运行最多四个并发工具执行(Pi 的内置限制),然后处理结果。

优势 – 为什么 Codemode 对 LLM 驱动的代理很重要

  1. 减少上下文占用 – 只有最终的脚本输出会被注入到模型中,而不是完整的中间工具日志。
  2. 可组合的工作流 – LLM 可以编写循环、条件判断和并行调用,将临时的工具调用转化为规范的程序。
  3. 直接访问 MCP – 未作为常规工具公开的 MCP API(例如图像生成、分类器)现在可以从脚本中调用,消除了对定制扩展的需求。
  4. 确定性的数据交换 – 来自 models.* 和 MCP 调用的结构化 JSON 避免了脆弱的文本解析。
  5. 改进的子代理编排 – 脚本可以生成并协调子代理,而无需依赖外部 CLI 包装器。

示例:通过 Codemode 进行图像生成

const [painter] = await models.getAvailableOfType("image");
const result = await models.generateImages(painter, {
  input: [{ type: "text", text: "A cute little puppy sitting on a grassy lawn, soft natural light, photorealistic" }]
});
if (result.stopReason !== "stop") return result.errorMessage;
for (const block of result.output) {
  if (block.type === "image") image(block); // 将图像发送回 LLM
  else text(block.text);
}

该脚本调用内部图像模型,接收包含图像字节的对象,并通过单个 image() 调用将其转发给 LLM。


示例:GitHub Issue 的批量情感分类

const jev = await models.getModelOfType("classifier", "typesafe", "jev-latest");
const r = await tools.bash({
  command: "gh issue list --state open --limit 100 --json number,title,body,comments"
});
const issues = JSON.parse(r.output);
const results = await Promise.all(issues.map(async issue => {
  const res = await models.classify(jev, {
    state: {
      title: issue.title,
      body: (issue.body || "").slice(0,4000),
      comments: issue.comments.slice(-5).map(c => c.body.slice(0,800))
    },
    questions: { /* sentiment, frustration, kind */ }
  });
  return { n: issue.number, title: issue.title, ...res.answers };
}));
store("sentiment_results", results);
return results.filter(r=>!r.error).sort((a,b)=>b.frustration.score-a.frustration.score).slice(0,12)
  .map(r=>`#${r.n} ${r.frustration.score.toFixed(2)} [${r.kind.choice}] ${r.title}`);

该脚本获取 100 个 issue,并行进行分类,存储完整结果,并返回一个简洁的排名。


与 MCP 的交互 – 渐进式发现与局限性

  • 渐进式工具发现 – 在 Codemode 内部,LLM 可以调用 tools.mcp__sentry__find_organizations({}),然后遍历返回的组织以查询项目。harness 提供了一个 structuredContent 字段,使后续调用具有确定性。
  • 当前的痛点 – 许多 MCP 服务器仍然返回自由格式的文本而不是 JSON,这破坏了 Codemode 对结构化数据的预期。不一致的批处理大小和缺乏对大二进制文件的支持也会导致失败。
  • 双重转义问题 – 当 MCP 服务器本身运行 Codemode(例如 Cloudflare 的 MCP)时,LLM 最终会在 JavaScript 中嵌套 JavaScript,导致双重 JSON 转义,增加了调试难度。

社区反馈 – 用户怎么看

"在这篇文章和 Cloudflare 的文章之间,全是文字却没有任何简单的系统级图景。……Code mode 本质上是为 LLM 重新发现了同像性(homoiconicity)和 Actor 语义。" – @ssivark

"如果你像我一样,在阅读完文章和评论后仍然想知道什么是 'codemode':它就是给 harness 提供了一个小的 js 沙箱,用于在将数据读入上下文之前编排工具调用并操作它们返回的数据。" – @ohgodhelpplease

"我不确定为什么几乎所有的 codemode 实现都选择 Javascript。我曾原型设计过一个代理,使用 bash 作为 codemode 的语言,效果同样好,而且不需要教学。" – @ylxdzsw

"Codemode 将交互轮数减少了大约一个数量级。" – @lionkor

"Pi 的初衷是你基本上只需要 bash 而不需要其他工具。最微创的方法本应是将工具调用直接注入为虚拟 bash 命令。根本不需要 code mode。" – @dnlgrmm

这些评论既突显了对减少交互轮数的兴奋,也表达了对语言选择、复杂性以及纯 bash 工作流原始愿景的担忧。


局限性与悬而未决的问题

  • 持久性 – 长时间运行的脚本可能需要检查点;当前的实现缺乏内置的快照功能。
  • 语言选择 – JavaScript 很方便,但不是唯一的选择;一些用户报告使用 Bash 或 Starlark 进行确定性执行也取得了成功。
  • 二进制处理 – 大型图像或文件仍然需要变通方法(预签名 URL),因为 MCP 尚不支持流式传输二进制 blob。
  • MCP 发现的可扩展性 – 目前没有标准化的方法可以在多个 MCP 服务器之间分发工具搜索;涌现行为可能会变得脆弱。

未来方向 – 使 Codemode 成为 LLM 的一等编程层

  • 确定性组合语言 – Starlark 或 Lisp 风格的语言可以取代 JavaScript,以保证引用透明度。
  • 工作流引擎 – 集成持久化工作流引擎(例如 Temporal)可以提供可靠的快照和重试机制。
  • 更好的 MCP 契约 – 强制执行 outputSchema 和一致的 JSON 响应将平滑 Codemode 与外部服务之间的桥梁。
  • 统一的 harness 设计 – 将 LLM 视为语言运行时参与者(而不是 token 生成器)可能会导致一类新的“感知 code-mode”的 harness,类似于同像性操作系统的愿景。

Codemode 并非万能药,但它通过在 harness 内部为模型提供一个轻量级、沙箱化的编程界面,为实现更丰富、更低开销的 LLM 驱动自动化提供了一条务实的路径。

Sources