Codemode
Beta
Codemode 让 LLM 编写并执行编排你的工具的代码,而不是一次调用一个。它受 CodeAct ↗ 启发,之所以可行,是因为 LLM 在编写代码方面比进行单个工具调用更擅长 — 它们见过数百万行真实世界的代码,但只见过人为构造的工具调用示例。
@cloudflare/codemode 包从你的工具中生成 TypeScript 类型定义,为 LLM 提供单个 “write code” 工具,并在安全的、隔离的 Worker 沙箱中执行生成的 JavaScript。
警告
Codemode 是实验性的,在未来版本中可能有破坏性更改。在生产环境中谨慎使用。
何时使用 Codemode
Codemode 在 LLM 需要执行以下操作时最有用:
- 链接多个工具调用,在它们之间使用逻辑(条件、循环、错误处理)
- 组合不同工具的结果,在返回之前
- 使用暴露许多细粒度操作的 MCP 服务器
- 执行多步工作流,这些工作流使用标准工具调用会需要多次往返
对于简单的单工具调用,标准 AI SDK 工具调用更简单且足够。
安装
终端窗口
npm install @cloudflare/codemode
如果你使用 @cloudflare/codemode/ai,还需安装 ai 和 zod 对等依赖:
终端窗口
npm install ai zod
快速开始
1. 定义你的工具
使用标准 AI SDK 的 tool() 函数:
JavaScript
import { tool } from "ai";
import { z } from "zod";
const tools = {
getWeather: tool({
description: "Get weather for a location",
inputSchema: z.object({ location: z.string() }),
execute: async ({ location }) => `Weather in ${location}: 72°F, sunny`,
}),
sendEmail: tool({
description: "Send an email",
inputSchema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
execute: async ({ to, subject, body }) => `Email sent to ${to}`,
}),
};
Explain Code
TypeScript
import { tool } from "ai";
import { z } from "zod";
const tools = {
getWeather: tool({
description: "Get weather for a location",
inputSchema: z.object({ location: z.string() }),
execute: async ({ location }) => `Weather in ${location}: 72°F, sunny`,
}),
sendEmail: tool({
description: "Send an email",
inputSchema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
execute: async ({ to, subject, body }) => `Email sent to ${to}`,
}),
};
Explain Code
2. 创建 codemode 工具
createCodeTool 接收你的工具和一个执行器,返回一个 AI SDK 工具:
JavaScript
import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
const executor = new DynamicWorkerExecutor({
loader: env.LOADER,
});
const codemode = createCodeTool({ tools, executor });
TypeScript
import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
const executor = new DynamicWorkerExecutor({
loader: env.LOADER,
});
const codemode = createCodeTool({ tools, executor });
3. 与 streamText 一起使用
像使用其他工具一样,将 codemode 工具传递给 streamText 或 generateText。你可以选择模型:
JavaScript
import { streamText } from "ai";
const result = streamText({
model,
system: "You are a helpful assistant.",
messages,
tools: { codemode },
});
TypeScript
import { streamText } from "ai";
const result = streamText({
model,
system: "You are a helpful assistant.",
messages,
tools: { codemode },
});
当 LLM 决定使用 codemode 时,它会编写一个像这样的异步箭头函数:
JavaScript
async () => {
const weather = await codemode.getWeather({ location: "London" });
if (weather.includes("sunny")) {
await codemode.sendEmail({
to: "[email protected]",
subject: "Nice day!",
body: `It's ${weather}`,
});
}
return { weather, notified: true };
};
Explain Code
代码在隔离的 Worker 沙箱中运行,工具调用通过 Workers RPC 派回主机,结果返回给 LLM。
配置
Wrangler 绑定
向你的 wrangler.jsonc 添加一个 worker_loaders 绑定。这是唯一必需的绑定:
JSONC
{
"$schema": "./node_modules/wrangler/config-schema.json",
"worker_loaders": [
{
"binding": "LOADER"
}
],
"compatibility_flags": [
"nodejs_compat"
]
}
Explain Code
TOML
worker_loaders = [{ binding = "LOADER" }]
compatibility_flags = ["nodejs_compat"]
工作原理
createCodeTool从你的工具中生成 TypeScript 类型定义,并构建一个 LLM 可以读取的描述。- LLM 编写一个异步箭头函数,调用
codemode.toolName(args)。 - 代码通过 AST 解析(acorn)进行规范化,然后发送到执行器。
DynamicWorkerExecutor通过WorkerLoader启动一个隔离的 Worker。- 在沙箱内,一个
Proxy拦截codemode.*调用,并通过 Workers RPC 将其路由回主机(ToolDispatcher extends RpcTarget)。 - 控制台输出(
console.log、console.warn、console.error)被捕获并在结果中返回。
网络隔离
外部 fetch() 和 connect() 默认被阻止 — 通过 globalOutbound: null 在 Workers 运行时级别强制执行。沙箱代码只能通过 codemode.* 工具调用与主机交互。
要允许受控的出站访问,传递一个 Fetcher:
JavaScript
const executor = new DynamicWorkerExecutor({
loader: env.LOADER,
globalOutbound: null, // default — fully isolated
// globalOutbound: env.MY_OUTBOUND_SERVICE // route through a Fetcher
});
TypeScript
const executor = new DynamicWorkerExecutor({
loader: env.LOADER,
globalOutbound: null, // default — fully isolated
// globalOutbound: env.MY_OUTBOUND_SERVICE // route through a Fetcher
});
与 Agent 一起使用
典型的模式是在 Agent 的消息处理器内创建执行器和 codemode 工具:
JavaScript
import { Agent } from "agents";
import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { streamText, convertToModelMessages, stepCountIs } from "ai";
export class MyAgent extends Agent {
async onChatMessage() {
const executor = new DynamicWorkerExecutor({
loader: this.env.LOADER,
});
const codemode = createCodeTool({
tools: myTools,
executor,
});
const result = streamText({
model,
system: "You are a helpful assistant.",
messages: await convertToModelMessages(this.state.messages),
tools: { codemode },
stopWhen: stepCountIs(10),
});
// Stream response back to client...
}
}
Explain Code
TypeScript
import { Agent } from "agents";
import { createCodeTool } from "@cloudflare/codemode/ai";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
import { streamText, convertToModelMessages, stepCountIs } from "ai";
export class MyAgent extends Agent<Env, State> {
async onChatMessage() {
const executor = new DynamicWorkerExecutor({
loader: this.env.LOADER,
});
const codemode = createCodeTool({
tools: myTools,
executor,
});
const result = streamText({
model,
system: "You are a helpful assistant.",
messages: await convertToModelMessages(this.state.messages),
tools: { codemode },
stopWhen: stepCountIs(10),
});
// Stream response back to client...
}
}
Explain Code
与 MCP 工具一起使用
MCP 工具的工作方式相同 — 将它们合并到工具集中:
JavaScript
const codemode = createCodeTool({
tools: {
...myTools,
...this.mcp.getAITools(),
},
executor,
});
TypeScript
const codemode = createCodeTool({
tools: {
...myTools,
...this.mcp.getAITools(),
},
executor,
});
带有连字符或点的工具名(在 MCP 中常见)会自动清理为有效的 JavaScript 标识符(例如,my-server.list-items 变为 my_server_list_items)。
MCP 服务器包装器
@cloudflare/codemode/mcp 导出提供两个用 Code Mode 包装 MCP 服务器的函数。
codeMcpServer
使用单个 code 工具包装现有的 MCP 服务器。每个上游工具在沙箱内成为一个类型化的 codemode.* 方法:
JavaScript
import { codeMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = await codeMcpServer({ server: upstreamMcp, executor });
TypeScript
import { codeMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = await codeMcpServer({ server: upstreamMcp, executor });
openApiMcpServer
从 OpenAPI 规范创建一个带有 search 和 execute 工具的 MCP 服务器。所有 $ref 指针在传递到沙箱之前被解析,主机端的 request 处理器将认证保持在沙箱之外:
JavaScript
import { openApiMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = openApiMcpServer({
spec: openApiSpec,
executor,
request: async ({ method, path, query, body }) => {
// Runs on the host — add auth headers here
const res = await fetch(`https://api.example.com${path}`, {
method,
headers: { Authorization: `Bearer ${token}` },
body: body ? JSON.stringify(body) : undefined,
});
return res.json();
},
});
Explain Code
TypeScript
import { openApiMcpServer } from "@cloudflare/codemode/mcp";
import { DynamicWorkerExecutor } from "@cloudflare/codemode";
const executor = new DynamicWorkerExecutor({ loader: env.LOADER });
const server = openApiMcpServer({
spec: openApiSpec,
executor,
request: async ({ method, path, query, body }) => {
// Runs on the host — add auth headers here
const res = await fetch(`https://api.example.com${path}`, {
method,
headers: { Authorization: `Bearer ${token}` },
body: body ? JSON.stringify(body) : undefined,
});
return res.json();
},
});
Explain Code
Executor 接口
Executor 接口刻意精简 — 实现它即可在任何沙箱中运行代码:
TypeScript
interface Executor {
execute(
code: string,
fns: Record<string, (...args: unknown[]) => Promise<unknown>>,
): Promise<ExecuteResult>;
}
interface ExecuteResult {
result: unknown;
error?: string;
logs?: string[];
}
Explain Code
DynamicWorkerExecutor 是内置的 Cloudflare Workers 实现。你可以为 Node VM、QuickJS、容器或任何其他沙箱构建自己的实现。
API 参考
createCodeTool(options)
返回一个 AI SDK 兼容的 Tool。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| tools | ToolSet | ToolDescriptors | 必需 | 你的工具(AI SDK tool() 或原始 descriptor) |
| executor | Executor | 必需 | 在何处运行生成的代码 |
| description | string | 自动生成 | 自定义工具描述。使用 \{\{types\}\} 表示类型定义 |
DynamicWorkerExecutor
通过 WorkerLoader 在隔离的 Cloudflare Worker 中执行代码。
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| loader | WorkerLoader | 必需 | 来自 env.LOADER 的 Worker Loader 绑定 |
| timeout | number | 30000 | 执行超时(毫秒) |
| globalOutbound | Fetcher | null | null | 网络访问控制。null = 阻止,Fetcher = 路由 |
| modules | Record<string, string> | — | 沙箱中可用的自定义 ES 模块。键是 specifier,值是源代码。 |
代码和工具名在内部被规范化和清理 — 你不需要在传递给 execute() 之前调用 normalizeCode() 或 sanitizeToolName()。
generateTypes(tools)
从你的工具中生成 TypeScript 类型定义。在内部由 createCodeTool 使用,但导出供自定义使用(例如,在前端显示类型)。
JavaScript
import { generateTypes } from "@cloudflare/codemode/ai";
const types = generateTypes(myTools);
// Returns:
// type CreateProjectInput = { name: string; description?: string }
// declare const codemode: {
// createProject: (input: CreateProjectInput) => Promise<unknown>;
// }
TypeScript
import { generateTypes } from "@cloudflare/codemode/ai";
const types = generateTypes(myTools);
// Returns:
// type CreateProjectInput = { name: string; description?: string }
// declare const codemode: {
// createProject: (input: CreateProjectInput) => Promise<unknown>;
// }
对于不依赖 AI SDK 的 JSON Schema 输入,使用主入口点:
JavaScript
import { generateTypesFromJsonSchema } from "@cloudflare/codemode";
const types = generateTypesFromJsonSchema(jsonSchemaToolDescriptors);
TypeScript
import { generateTypesFromJsonSchema } from "@cloudflare/codemode";
const types = generateTypesFromJsonSchema(jsonSchemaToolDescriptors);
sanitizeToolName(name)
将工具名转换为有效的 JavaScript 标识符。
JavaScript
import { sanitizeToolName } from "@cloudflare/codemode";
sanitizeToolName("get-weather"); // "get_weather"
sanitizeToolName("3d-render"); // "_3d_render"
sanitizeToolName("delete"); // "delete_"
TypeScript
import { sanitizeToolName } from "@cloudflare/codemode";
sanitizeToolName("get-weather"); // "get_weather"
sanitizeToolName("3d-render"); // "_3d_render"
sanitizeToolName("delete"); // "delete_"
安全考虑
- 代码在隔离的 Worker 沙箱中运行 — 每次执行获得自己的 Worker 实例。
- 外部网络访问(
fetch、connect)在运行时级别默认被阻止。 - 工具调用通过 Workers RPC 派发,而不是网络请求。
- 执行有可配置的超时(默认 30 秒)。
- 控制台输出被单独捕获,不会泄漏到主机。
当前限制
- 尚不支持工具批准(
needsApproval)。 带有needsApproval: true的工具在沙箱内立即执行,不会暂停以等待批准。计划在 codemode 中支持批准流程。目前,不要将需要批准的工具传递给createCodeTool— 而是通过标准 AI SDK 工具调用使用它们。 DynamicWorkerExecutor需要 Cloudflare Workers 环境。- 仅限 JavaScript 执行。
- LLM 代码质量取决于提示工程和模型能力。
相关资源
Codemode example 完整可用的示例 — 一个使用 codemode 和 SQLite 的项目管理助手。
Using AI Models 在你的 Agent 中使用 AI 模型。
MCP Client 连接到 MCP 服务器并通过 codemode 使用其工具。