Cloudflare Project Think 超详细教程,附源码
上周三凌晨刷到 Cloudflare 的博客推送,差点从椅子上蹦起来——他们搞了个叫 Project Think 的东西,直接把 AI Agent 开发卷到了新高度。
我花了一个周末把文档啃完,踩了七八个坑,终于跑通了一个完整的多步骤研究 Agent。今天就把我折腾的过程完整记录下来,帮你省掉那些我踩过的坑。
为什么需要 Project Think?
先说个扎心的事实:现在主流的编码 Agent(Claude Code、Codex、Cursor Agent)都有个硬伤——它们只能在本地跑,或者你得花大价钱租 VPS。一个 Agent 闲着不动,每月也得烧你好几十刀。
更要命的是扩展性。假如你有 100 个用户同时用 Agent,你就得开 100 个容器。我之前试过用 Docker 跑 10 个 Agent 实例,那台 32G 内存的机器差点被我搞崩。
Cloudflare 给了个思路:用 Durable Objects。每个 Agent 是一个独立的 actor,自带 SQLite 数据库,空闲时零计算成本,收到消息自动唤醒。10 万个 Agent 同时在线,实际活跃的可能就几百个——只为你真正在跑的那些买单。
说白了,就是把"一个容器跑一个 Agent"变成了"一个轻量级隔离环境跑一个 Agent",成本砍到原来的 1% 甚至更少。
环境准备
我试下来的环境配置,亲测可用:
# Node.js 版本要求 >= 18.17,我用的是 20.x
node -v
# v20.11.0
# 安装 wrangler CLI(Cloudflare 的命令行工具)
npm install -g wrangler
# 登录 Cloudflare 账号
wrangler login
# 验证登录状态
wrangler whoami
对,你需要一个 Cloudflare 账号。好消息是 Workers 的免费额度完全够你玩——每天 10 万次请求,Durable Objects 现在也有免费额度了。
# 创建项目
mkdir my-think-agent && cd my-think-agent
npm init -y
npm install agents @cloudflare/workers-types
项目结构长这样:
my-think-agent/
├── src/
│ └── agent.ts # Agent 主逻辑
├── wrangler.toml # Cloudflare 配置
├── package.json
└── tsconfig.json
wrangler.toml 配置(这个文件很关键,我第一次就因为少写了 [durable_objects] 整个跑不起来):
name = "my-think-agent"
main = "src/agent.ts"
compatibility_date = "2026-04-15"
[durable_objects]
bindings = [
{ name = "MY_AGENT", class_name = "MyAgent" }
]
[[migrations]]
tag = "v1"
new_classes = ["MyAgent"]
tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"types": ["@cloudflare/workers-types"],
"strict": true,
"outDir": "dist"
},
"include": ["src/**/*"]
}
开始写代码
第一步:最小可用的 Agent
先把最基础的跑起来,能接收消息、能回复:
// src/agent.ts
import { Agent } from "agents";
interface Env {
MY_AGENT: DurableObjectNamespace;
AI: any; // Workers AI binding
}
export class MyAgent extends Agent<Env> {
// Agent 启动时调用
async onRequest(request: Request): Promise<Response> {
const { message } = await request.json() as { message: string };
// 调用 Workers AI(Cloudflare 自带的模型)
const result = await this.env.AI.run(
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
{ messages: [{ role: "user", content: message }] }
);
return Response.json({
reply: result.response,
agentId: this.id.toString()
});
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// 从 URL 获取 agent ID,没有就生成一个
const url = new URL(request.url);
const agentId = url.searchParams.get("id") || crypto.randomUUID();
// 获取或创建 Durable Object 实例
const id = env.MY_AGENT.idFromName(agentId);
const stub = env.MY_AGENT.get(id);
return stub.fetch(request);
}
};
部署试一下:
npx wrangler deploy
第一次部署会问你确认一些东西,一路回车就行。部署完会给你一个 URL,类似 https://my-think-agent.<你的子域名>.workers.dev。
测试一下:
curl -X POST "https://my-think-agent.xxx.workers.dev" \
-H "Content-Type: application/json" \
-d '{"message": "你好,介绍一下你自己"}'
能看到回复就说明基础环境 OK 了。
第二步:加上持久化对话(Session API)
上面那个 Agent 每次都是全新的对话,没记忆。Project Think 的 Session API 就是为了解决这个问题:
import { Agent, Session } from "agents";
interface Env {
MY_AGENT: DurableObjectNamespace;
AI: any;
}
export class ResearchAgent extends Agent<Env> {
async onStart() {
// Agent 初始化时创建一个会话
if (!this.sessions.list().length) {
this.sessions.create("main");
}
}
async onRequest(request: Request): Promise<Response> {
const { message } = await request.json() as { message: string };
const session = this.sessions.get("main");
// 把用户消息存入会话
session?.push({ role: "user", content: message });
// 拼装完整的对话历史发给模型
const history = session?.messages || [];
const result = await this.env.AI.run(
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
{ messages: history }
);
// 把模型回复也存进去
session?.push({ role: "assistant", content: result.response });
return Response.json({
reply: result.response,
historyLength: history.length + 2 // 加上这次一问一答
});
}
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
const agentId = url.searchParams.get("id") || "default";
const id = env.MY_AGENT.idFromName(agentId);
const stub = env.MY_AGENT.get(id);
return stub.fetch(request);
}
};
有意思的是,Session API 支持树状结构的消息——每个消息都有 parent_id,你可以从任意一条消息分叉出新的对话分支,不会破坏原来的上下文。这比简单的线性对话记录灵活太多了。
第三步:Durable Execution(这个是真香功能)
runFiber() 是 Project Think 的核心杀器。它让你的 Agent 能执行长时间任务,中途被中断了也不怕——自动从上次 checkpoint 恢复:
export class ResearchAgent extends Agent<Env> {
// ... 前面的代码 ...
async startResearch(topic: string) {
// 启动一个 Fiber,这是持久化执行的单元
void this.runFiber("research", async (ctx) => {
const findings: string[] = [];
// 分 5 步做研究
for (let i = 0; i < 5; i++) {
const prompt = `研究步骤 ${i + 1}/5:针对主题「${topic}」,请给出第 ${i + 1} 个关键发现。`;
const result = await this.env.AI.run(
"@cf/meta/llama-3.3-70b-instruct-fp8-fast",
{ messages: [{ role: "user", content: prompt }] }
);
findings.push(result.response);
// 关键:保存 checkpoint
// 如果 Agent 被中断(比如服务器重启),会从这里恢复
ctx.stash({ findings, step: i, topic });
// 通知前端进度
this.broadcast({
type: "progress",
step: i + 1,
total: 5,
current: result.response
});
}
// 研究完成,保存最终结果
ctx.stash({ findings, completed: true });
this.broadcast({ type: "done", findings });
});
}
async onRequest(request: Request): Promise<Response> {
const url = new URL(request.url);
// 如果是研究请求
if (url.pathname === "/research") {
const { topic } = await request.json() as { topic: string };
await this.startResearch(topic);
return Response.json({ status: "started", topic });
}
// 普通对话
const { message } = await request.json() as { message: string };
// ... 同上 ...
return Response.json({ reply: "..." });
}
}
这里有个让我惊艳的点:ctx.stash() 会把当前状态持久化到 Agent 的 SQLite 数据库里。就算 Cloudflare 的服务器把你这个 Agent 驱逐了(内存回收),下次有请求进来的时候,它会自动从上次 stash 的地方继续执行。
我测试的时候故意在循环中间 wrangler deploy 了一下(相当于重启),研究任务居然真的从断点继续了。这在传统的 serverless 函数里是想都不敢想的事情。
第四步:加个前端页面(可选)
光有 API 不直观,加个简单的 HTML 页面看效果:
// 在 fetch handler 里加个 GET 路由
if (request.method === "GET" && url.pathname === "/") {
return new Response(HTML_PAGE, {
headers: { "Content-Type": "text/html; charset=utf-8" }
});
}
// HTML 页面内容(简化版)
const HTML_PAGE = `<!DOCTYPE html>
<html>
<head>
<title>Research Agent</title>
<style>
body { font-family: system-ui; max-width: 640px; margin: 40px auto; padding: 0 20px; }
.msg { padding: 12px; margin: 8px 0; border-radius: 8px; }
.user { background: #e3f2fd; }
.bot { background: #f5f5f5; }
input { width: 70%; padding: 10px; font-size: 16px; }
button { padding: 10px 20px; font-size: 16px; cursor: pointer; }
</style>
</head>
<body>
<h1>Research Agent Demo</h1>
<div id="chat"></div>
<input id="input" placeholder="输入研究主题..." />
<button onclick="send()">开始研究</button>
<script>
async function send() {
const input = document.getElementById("input");
const chat = document.getElementById("chat");
const topic = input.value;
chat.innerHTML += '<div class="msg user">' + topic + '</div>';
const res = await fetch("/research", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ topic })
});
const data = await res.json();
chat.innerHTML += '<div class="msg bot">任务已启动: ' + data.status + '</div>';
}
</script>
</body>
</html>`;
实际效果:你在页面上输入一个研究主题,Agent 会在后台分步执行研究任务,每完成一步都会通过 WebSocket 推送进度到前端。你在终端里看到的是这样:
✅ [1/5] 正在分析主题背景...
✅ [2/5] 正在收集关键数据...
✅ [3/5] 正在整理核心观点...
✅ [4/5] 正在对比不同方案...
✅ [5/5] 正在生成总结报告...
🎉 研究完成!共收集 5 个关键发现
整个流程大概 30 秒到 2 分钟,取决于模型响应速度。
常见问题 FAQ
Q1:部署时报错 Durable Object namespace not found
八成是 wrangler.toml 里的配置没写对。检查两点:[durable_objects] 里的 class_name 要跟你代码里的类名一模一样(区分大小写),[[migrations]] 的 new_classes 也要写上。我第一次就因为类名写成了 myAgent(小写 m),部署上去直接白屏。
Q2:Workers AI 模型调用报 403
Workers AI 绑定需要在 wrangler.toml 里显式声明:
[ai]
binding = "AI"
没加这行的话 this.env.AI 就是 undefined,调用直接炸。这个在官方文档里藏得比较深,我翻了半天 issue 才找到。
Q3:runFiber 里的异步操作超时了
Durable Objects 的单次请求有 30 秒限制(付费版可以更长)。但 runFiber 不受这个限制——它会在后台持续运行,即使 HTTP 请求已经返回了。如果你发现 Fiber 里的任务没跑完就停了,检查一下是不是在 Fiber 外面做了太多操作。
Q4:想用自己的 API Key 调 GPT/Claude 怎么办?
Project Think 的 Agents SDK 不绑死 Workers AI。你可以直接在代码里 fetch 调 OpenAI 或 Anthropic 的 API:
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": `Bearer ${this.env.OPENAI_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "gpt-4o",
messages: history
})
});
API Key 存在 Cloudflare 的 Secrets 里:wrangler secret put OPENAI_API_KEY。
Q5:免费额度够用吗?
亲测够用。Workers 免费版每天 10 万次请求,Durable Objects 现在也有免费额度。我测试了一整天(大概调了 200 多次 Agent),一分钱没花。不过注意 Workers AI 的模型调用有单独的免费额度限制,超过了会返回 429。
以上就是一个最小但完整的 Project Think Agent 了。从零到部署大概 1-2 小时(如果你网络好的话,光 npm install 就能省不少时间)。
Project Think 目前还是 Preview 阶段,API 可能会变。但核心概念——Durable Execution、Session API、Sub-agents——这些方向不会跑偏。Cloudflare 这波操作基本上在说:AI Agent 不该是奢侈品,它应该是基础设施级别的存在。
说实话,我之前一直觉得 serverless 跑 Agent 是个伪命题——没有长连接、状态管理麻烦、冷启动慢。但 Project Think 的 Durable Objects 方案确实解决了这些痛点。空闲零成本、状态自动持久化、自动扩缩容……至少从我这几天的测试来看,它不是 PPT。
如果觉得有帮助,欢迎点赞收藏 ❤️ 更多AI工具实战教程,关注我第一时间获取~