原文:Project Think: building the next generation of AI agents on Cloudflare
一个改变了认知的观察
Cloudflare 团队每天在用 Claude Code、Codex 这类编码 Agent。他们发现了一件事:人们用这些工具做的,远不只是写代码。
管理日历、分析数据集、谈判采购、填税表、自动化整条业务流程------这些用法背后的模式是一致的:Agent 读取上下文,推理,写代码来采取行动,观察结果,然后迭代。代码成了行动的通用媒介。
这个洞察很有力量,但当前的 Agent 碰到了三堵结构性的墙:
- 只跑在本地或高价 VPS 上:无法共享、无法协作、无法在设备之间交接
- 闲置也在烧钱:无论 Agent 在不在工作,每月的固定成本都在跑。拓展到一个团队、一家公司,成本会快速叠加
- 需要手动管理和配置:安装依赖、管理更新、配置身份和密钥
这三个问题加在一起,意味着"每个用户配一个 Agent"在现有的基础设施范式下,根本跑不通。
规模化的根本矛盾:Agent 天然是一对一的
传统应用的扩容逻辑是:一个实例服务多个用户,用户越多,单个用户的基础设施成本越低。
Agent 不是这样的。每个 Agent 是一个唯一的实例,服务一个用户,运行一项任务。餐厅有一张菜单、一个厨房,可以批量出餐;Agent 更像私人厨师,每次都是不同的食材、不同的技法、不同的工具。
这从根本上改变了扩容的数学。如果 1 亿知识工作者各自拥有一个 Agent,哪怕并发使用率不高,你也需要同时支撑数千万个活跃会话。按照当前的容器成本,这是不可持续的。
这就是 Project Think 要解决的问题:Agent 需要一个不同的计算基础。
基础:为什么 Durable Objects 改变了经济账
Project Think 建立在 Cloudflare 的 Durable Objects(DO)之上。理解 DO 的特性,是理解后续所有设计决策的前提。
DO 实现的是 Actor 模型 :每个 Agent 是一个可寻址的实体,有自己独立的 SQLite 数据库。关键在于它的休眠机制------Agent 不工作时,消耗的计算资源是零。当某件事发生(一条 HTTP 请求、一条 WebSocket 消息、一封入站邮件、一个定时闹钟),平台才唤醒 Agent,加载它的状态,把事件交给它处理。处理完,Agent 重新进入休眠。
这和容器的差异,用一张表来对比会更直观:
| 容器 / VM | Durable Objects | |
|---|---|---|
| 闲置成本 | 全额计算费用,始终在跑 | 零(休眠态) |
| 扩容方式 | 需要预置和管理容量 | 自动,按 Agent 粒度 |
| 状态存储 | 需要外部数据库 | 内置 SQLite |
| 崩溃恢复 | 自己实现(进程管理、健康检查) | 平台重启,状态保留 |
| 路由寻址 | 自己实现(负载均衡、会话粘性) | 内置(名称直接路由到 Agent) |
| 1 万个 Agent,各自 1% 时间活跃 | 1 万个常驻实例 | 约 100 个活跃实例 |
这张表的最后一行说明了问题。DO 把"同时跑 1 万个 Agent"的成本,实际上压缩成了"同时跑约 100 个"的成本。
这改变了产品设计的可能性边界。"每个客户一个 Agent"、"每个任务一个 Agent"、"每条邮件线程一个 Agent",这些原来成本不可接受的设计,在 DO 上都变得可行。创建一个新 Agent 实例的边际成本,几乎是零。
Project Think 的六项新原语
一、持久执行:Agent 崩溃了也能续跑
一次 LLM 调用可以持续 30 秒,一个多轮 Agent 循环可以跑更长时间。在这个过程中,执行环境随时可能消失:一次部署、一次平台重启、撞到资源上限。连接断开,内存状态丢失,客户端看到的是流突然停了,没有任何说明。
runFiber() 解决这个问题。Fiber 是一种持久化的函数调用:在执行开始前就注册到 SQLite,可以通过 stash() 在任意节点设置检查点,在 Agent 重启后通过 onFiberRecovered 从最近的检查点恢复:
typescript
export class ResearchAgent extends Agent {
async startResearch(topic: string) {
void this.runFiber("research", async (ctx) => {
const findings = [];
for (let i = 0; i < 10; i++) {
const result = await this.callLLM(`Research step ${i}: ${topic}`);
findings.push(result);
// 检查点:如果 Agent 被驱逐,从这里恢复
ctx.stash({ findings, step: i, topic });
this.broadcast({ type: "progress", step: i });
}
return { findings };
});
}
async onFiberRecovered(ctx) {
if (ctx.name === "research" && ctx.snapshot) {
const { topic } = ctx.snapshot;
await this.startResearch(topic);
}
}
}
SDK 会在 Fiber 执行期间自动维持 Agent 存活,不需要额外配置。对于以分钟计的任务,keepAlive() 防止在活跃工作期间被驱逐;对于更长的操作(CI 流水线、视频生成),Agent 启动任务、持久化任务 ID、进入休眠,等待回调唤醒。
二、子 Agent:把工作分发出去
一个 Agent 不应该什么都自己做。子 Agent 是与父 Agent 并置的子 Durable Objects,各有独立的 SQLite 和执行上下文:
typescript
export class Orchestrator extends Agent {
async handleTask(task: string) {
const researcher = await this.subAgent(ResearchAgent, "research");
const reviewer = await this.subAgent(ReviewAgent, "review");
const [research, review] = await Promise.all([
researcher.search(task),
reviewer.analyze(task)
]);
return this.synthesize(research, review);
}
}
子 Agent 之间的数据完全隔离------没有隐式的共享状态,各自的 SQLite 互不可见。Sub-agent 之间的 RPC 调用延迟等同于一次函数调用,而 TypeScript 在编译阶段就能捕获接口误用。
三、持久会话:支撑长达数周的对话
跑几天甚至几周的 Agent,光靠一个扁平的消息列表是不够的。Session API 把对话建模为树形结构 ,每条消息都有 parent_id。
这带来了几个实际能力:分叉 (在不丢失原始路径的情况下探索另一个方向)、非破坏性压缩 (总结老消息而不是删除它们,完整历史仍保存在 SQLite 里)、以及通过 FTS5 全文索引在对话历史里搜索。
四、从工具调用到代码执行
传统的工具调用有一个固有的尴尬:模型调用一个工具,结果拉回上下文窗口,再调用下一个,再拉回,如此循环。100 个文件意味着 100 次往返。工具数量越多,这个过程越笨重也越昂贵。
@cloudflare/codemode 背后的洞察是:让模型写一个程序来处理整个任务,比让它逐个玩工具调用要好得多。
javascript
// LLM 写出这段代码,在沙箱 Dynamic Worker 里运行
const files = await tools.find({ pattern: "**/*.ts" });
const results = [];
for (const file of files) {
const content = await tools.read({ path: file });
if (content.includes("TODO")) {
results.push({ file, todos: content.match(/\/\/ TODO:.*/g) });
}
}
return results;
100 次模型往返变成 1 次。Cloudflare 在自己的 API MCP Server 上验证了这个效果:只暴露 search() 和 execute() 两个工具(约 1,000 个 Token),对比为每个 API 端点各暴露一个工具的等效方案(约 117 万个 Token),Token 消耗减少了 99.9%。
五、执行阶梯:五个层级,按需升级
一旦接受"让模型写代码来行动"这个前提,下一个问题就是:这些代码在哪里安全地跑?
Project Think 定义了一个执行阶梯,Agent 根据任务需要逐级提升执行环境的能力:
Tier 0 --- 工作空间 :基于 SQLite 和 R2 的持久化虚拟文件系统。读、写、编辑、搜索、grep、diff,由 @cloudflare/shell 提供支撑。
Tier 1 --- Dynamic Worker(沙箱隔离) :LLM 生成的 JavaScript 跑在一个独立的 V8 隔离环境里,默认无网络访问 (globalOutbound: null)。这和"先给一台通用机器再想办法限制它"的思路相反------Dynamic Worker 从零权限开始,开发者通过 bindings 显式授予每一项能力。
在规模上:一个新 Dynamic Worker 的启动时间是毫秒级,内存占用在几 MB 量级,约是容器的 100 倍速度、100 倍内存效率。
Tier 2 --- npm :@cloudflare/worker-bundler 从 npm 注册表拉取包,用 esbuild 打包,加载进 Dynamic Worker。Agent 写 import { z } from "zod",直接可用,不需要预装任何东西。
Tier 3 --- 无头浏览器:通过 Browser Run 实现网页导航、点击、内容提取、截图。在目标服务还没有支持 MCP 或 API 的时候,这是让 Agent 仍然能行动的方案。
Tier 4 --- 完整沙箱 :配置好工具链、代码仓库和依赖的 Cloudflare Sandbox,支持 git clone、npm test、cargo build,和工作空间双向同步。
设计原则:Agent 在 Tier 0 单独就能工作,每一层都是增量能力的叠加,而不是必须整体引入。
六、自编写扩展:Agent 为自己写工具
Think 把代码执行推进了一步。Agent 可以在运行时为自己编写新的扩展------TypeScript 程序,运行在 Dynamic Worker 里,声明对网络和工作空间的权限:
json
{
"name": "github",
"description": "GitHub integration: PRs, issues, repos",
"tools": ["create_pr", "list_issues", "review_pr"],
"permissions": {
"network": ["api.github.com"],
"workspace": "read-write"
}
}
ExtensionManager 打包扩展(可以带 npm 依赖),加载进 Dynamic Worker,注册新工具。扩展持久化在 DO 存储里,休眠后仍然存在。
30 秒前还没有 github_create_pr 工具,现在有了。这不是通过微调或 RLHF 实现的自我改进,而是通过代码------沙箱隔离、可审计、可撤销的 TypeScript。
Think 基类:10 行代码得到一个完整 Agent
Project Think 同时提供了一个叫做 Think 的基类,把上述所有原语接在一起,处理完整的 chat 生命周期:会话循环、消息持久化、流式输出、工具执行、流恢复。
最简单的用法只有 10 行:
typescript
import { Think } from "@cloudflare/think";
import { createWorkersAI } from "workers-ai-provider";
export class MyAgent extends Think<Env> {
getModel() {
return createWorkersAI({ binding: this.env.AI })(
"@cf/moonshotai/kimi-k2.5"
);
}
}
这已经是一个拥有流式输出、消息持久化、中止/取消、错误处理、流恢复、以及内置工作空间文件系统的完整 Agent。npx wrangler deploy 即可部署。
需要更多控制时的覆盖接口
Think 做了很多默认决策,当你需要干预时,覆盖对应方法即可:
| 方法 | 用途 |
|---|---|
getModel() |
返回使用的语言模型 |
getSystemPrompt() |
系统提示 |
getTools() |
工具集(兼容 AI SDK 格式) |
maxSteps |
每轮最大工具调用轮数 |
configureSession() |
上下文块、压缩策略、搜索、技能文件 |
生命周期钩子覆盖了每个阶段,不需要接管整条流水线:
beforeTurn()
→ streamText()
→ beforeToolCall()
→ afterToolCall()
→ onStepFinish()
→ onChatResponse()
在后续轮次切换更便宜的模型、限制工具集、注入客户端上下文、记录所有工具调用到分析系统------这些都可以通过钩子完成,而不是替换整个 onChatMessage。
跨休眠的持久记忆
Think 在 Session API 的树形消息存储之上,增加了上下文块(Context Blocks):结构化的系统提示片段,模型可以读取并主动更新,跨休眠持久化。
typescript
configureSession(session: Session) {
return session
.withContext("memory", {
description: "Important facts learned during conversation.",
maxTokens: 2000
})
.withCachedPrompt();
}
模型在上下文里看到 "MEMORY [42%, 462/1100 tokens]" 这样的信息,可以用 set_context 工具主动记下重要事实。这些记忆和 Agent 一起持久化,不依赖任何外部存储。
会话长度增长时,Think 用非破坏性压缩处理上下文限制:老消息被总结而不是删除,完整历史依然存在于 SQLite,随时可以全文检索。
三次浪潮
Cloudflare 用"三次浪潮"描述 AI Agent 的演进路径,这个框架本身值得单独看一下。
第一波:聊天机器人。无状态、被动、脆弱。每次对话从零开始,没有记忆,没有工具,无法主动行动。适合回答问题,但被限制在只能回答问题。
第二波:编码 Agent。Claude Code、Codex 这类工具证明了有状态、有工具的 Agent 是怎样的。LLM 加上读文件、写代码、执行代码的能力,就成了一台通用机器。但它跑在你的笔记本上,服务一个用户,没有持久化保证,关机就断。
第三波:Agent 作为基础设施。持久、分布式、架构上安全、Serverless。这是跑在互联网上的 Agent,能从失败中恢复,闲置时成本为零,安全性通过架构来保证而不是通过行为约束。任何开发者都可以为任意数量的用户部署和运行。
这是一个选择问题,也是一个赌注。Cloudflare 明确说这是他们押注的方向。
小结
Project Think 的核心贡献是给出了一个完整的垂直切片:从计算经济学(Durable Objects 把闲置成本降到零),到执行安全(Dynamic Worker 从零权限起步),到长期记忆(树形会话 + 上下文块),到自我扩展能力(运行时写工具)。
这些原语单独拿出来,都在解决具体的工程问题。但把它们接在一起,形状就变了:一个 Agent 不再是一个运行在本地、崩溃就没了的进程,而是一个可以持续数周、跨越网络中断、在无人值守时自主行动的实体。
对于正在构建 Agent 应用的团队来说,有几个具体判断值得关注:codemode 的 99.9% Token 减少 不是小优化,它改变了工具集设计的方式;执行阶梯的分层设计 让你不需要一开始就接入完整沙箱,按需升级;自编写扩展意味着 Agent 的能力边界不是在部署时就固定的。
目前 Project Think 处于预览阶段,API 仍在演进,但核心的使用方式已经稳定。
参考链接
- 原文:https://blog.cloudflare.com/project-think/
- Agents SDK 文档:https://developers.cloudflare.com/agents/
- Think 文档(GitHub):https://github.com/cloudflare/agents/blob/main/docs/think/index.md
- 完整示例:https://github.com/cloudflare/agents/tree/main/examples/assistant
- 快速安装:
npm install @cloudflare/think agents ai @cloudflare/shell zod workers-ai-provider