分步构建带持久内存、内置文件工具与流式传输的聊天 Agent。
若你刚接触 Cloudflare Agents,请先浏览什么是 Agent?了解核心概念。否则可直接从这里从零开始。
本教程结束时你将拥有:
- 向 React 聊天 UI 流式返回响应
- 模型可读写的持久内存
- 工作区文件工具(read、write、edit、find、grep、delete)
- 自定义服务端工具支持
- Node.js 24+
- 具有 Workers AI 访问权限的 Cloudflare 账户
- 熟悉 TypeScript 与 Cloudflare Workers
mkdir my-think-agent && cd my-think-agent
npm init -y安装依赖:
npm install @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-provider react react-dom
npm install -D wrangler @cloudflare/vite-plugin @cloudflare/workers-types @vitejs/plugin-react @tailwindcss/vite tailwindcss typescript vite创建 wrangler.jsonc:
{
"name": "my-think-agent",
"compatibility_date": "2026-01-28",
"compatibility_flags": ["nodejs_compat"],
"ai": { "binding": "AI" },
"assets": {
"not_found_handling": "single-page-application",
"run_worker_first": ["/agents/*"]
},
"durable_objects": {
"bindings": [{ "class_name": "MyAgent", "name": "MyAgent" }]
},
"migrations": [{ "new_sqlite_classes": ["MyAgent"], "tag": "v1" }],
"main": "src/server.ts"
}name = "my-think-agent"
compatibility_date = "2026-01-28"
compatibility_flags = [ "nodejs_compat" ]
main = "src/server.ts"
[ai]
binding = "AI"
[assets]
not_found_handling = "single-page-application"
run_worker_first = [ "/agents/*" ]
[[durable_objects.bindings]]
class_name = "MyAgent"
name = "MyAgent"
[[migrations]]
new_sqlite_classes = [ "MyAgent" ]
tag = "v1"创建 vite.config.ts:
import { cloudflare } from "@cloudflare/vite-plugin";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [react(), cloudflare(), tailwindcss()],
});import { cloudflare } from "@cloudflare/vite-plugin";
import tailwindcss from "@tailwindcss/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [react(), cloudflare(), tailwindcss()],
});创建 tsconfig.json:
{
"extends": "agents/tsconfig"
}创建 src/server.ts:
import { Think } from "@cloudflare/think";
import { createWorkersAI } from "workers-ai-provider";
import { routeAgentRequest } from "agents";
export class MyAgent extends Think {
getModel() {
return createWorkersAI({ binding: this.env.AI })(
"@cf/moonshotai/kimi-k2.6",
);
}
getSystemPrompt() {
return "You are a helpful assistant with access to a workspace filesystem.";
}
}
export default {
async fetch(request, env) {
return (
(await routeAgentRequest(request, env)) ||
new Response("Not found", { status: 404 })
);
},
};import { Think } from "@cloudflare/think";
import { createWorkersAI } from "workers-ai-provider";
import { routeAgentRequest } from "agents";
export class MyAgent extends Think<Env> {
getModel() {
return createWorkersAI({ binding: this.env.AI })(
"@cf/moonshotai/kimi-k2.6",
);
}
getSystemPrompt() {
return "You are a helpful assistant with access to a workspace filesystem.";
}
}
export default {
async fetch(request: Request, env: Env) {
return (
(await routeAgentRequest(request, env)) ||
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;这是可工作的 Agent。Think 自动提供:
- WebSocket 聊天协议(兼容
useAgentChat) - SQLite 消息持久化
- 可恢复流式传输(页面刷新重放缓冲 chunk)
- 工作区文件工具(read、write、edit、list、find、grep、delete)
- Abort/cancel 支持
- 带部分消息持久化的错误处理
创建 src/client.tsx:
import { createRoot } from "react-dom/client";
import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
function Chat() {
const agent = useAgent({ agent: "MyAgent" });
const { messages, sendMessage, status } = useAgentChat({ agent });
return (
<div>
<h1>Think Agent</h1>
{messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong>
{msg.parts.map((part, i) =>
part.type === "text" ? <span key={i}>{part.text}</span> : null,
)}
</div>
))}
<form
onSubmit={(e) => {
e.preventDefault();
const input = e.currentTarget.elements.namedItem("input");
if (!input.value.trim()) return;
sendMessage({ text: input.value });
input.value = "";
}}
>
<input name="input" placeholder="Send a message..." />
<button type="submit">Send</button>
</form>
<p>Status: {status}</p>
</div>
);
}
const root = document.getElementById("root");
if (root) {
createRoot(root).render(<Chat />);
}import { createRoot } from "react-dom/client";
import { useAgent } from "agents/react";
import { useAgentChat } from "@cloudflare/ai-chat/react";
function Chat() {
const agent = useAgent({ agent: "MyAgent" });
const { messages, sendMessage, status } = useAgentChat({ agent });
return (
<div>
<h1>Think Agent</h1>
{messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong>
{msg.parts.map((part, i) =>
part.type === "text" ? <span key={i}>{part.text}</span> : null,
)}
</div>
))}
<form
onSubmit={(e) => {
e.preventDefault();
const input = e.currentTarget.elements.namedItem(
"input",
) as HTMLInputElement;
if (!input.value.trim()) return;
sendMessage({ text: input.value });
input.value = "";
}}
>
<input name="input" placeholder="Send a message..." />
<button type="submit">Send</button>
</form>
<p>Status: {status}</p>
</div>
);
}
const root = document.getElementById("root");
if (root) {
createRoot(root).render(<Chat />);
}创建 index.html:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Think Agent</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/client.tsx"></script>
</body>
</html>npx vite dev打开浏览器并发送消息。Agent 以流式文本响应,工作区文件工具自动对模型可用。
重写 configureSession 为模型提供在重启后仍保留的可写内存:
export class MyAgent extends Think {
getModel() {
return createWorkersAI({ binding: this.env.AI })(
"@cf/moonshotai/kimi-k2.6",
);
}
configureSession(session) {
return session
.withContext("soul", {
provider: {
get: async () =>
"You are a helpful assistant. Remember important facts about the user.",
},
})
.withContext("memory", {
description: "Important facts about the user and conversation.",
maxTokens: 2000,
})
.withCachedPrompt();
}
}export class MyAgent extends Think<Env> {
getModel(): LanguageModel {
return createWorkersAI({ binding: this.env.AI })(
"@cf/moonshotai/kimi-k2.6",
);
}
configureSession(session: Session) {
return session
.withContext("soul", {
provider: {
get: async () =>
"You are a helpful assistant. Remember important facts about the user.",
},
})
.withContext("memory", {
description: "Important facts about the user and conversation.",
maxTokens: 2000,
})
.withCachedPrompt();
}
}模型在系统提示词中看到 MEMORY 部分并获得 set_context 工具更新它。写入内存的事实持久化在 SQLite,在 Durable Object 休眠与重启后仍然保留。
使用 configureSession 时,系统提示词由上下文块构建而非 getSystemPrompt()。上述 "soul" 块作为系统身份 — 只读且始终首先出现。"memory" 块可写,模型学到有用信息时主动更新。
上下文块、压缩、搜索、技能与多会话请参阅 会话文档。
重写 getTools() 在内置工作区工具旁添加自有工具:
import { tool } from "ai";
import { z } from "zod";
export class MyAgent extends Think {
getModel() {
/* ... */
}
configureSession(session) {
/* ... */
}
getTools() {
return {
getWeather: tool({
description: "Get the current weather for a city",
inputSchema: z.object({
city: z.string().describe("City name"),
}),
execute: async ({ city }) => {
const res = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${this.env.WEATHER_KEY}&q=${city}`,
);
return res.json();
},
}),
};
}
}import { tool } from "ai";
import { z } from "zod";
export class MyAgent extends Think<Env> {
getModel(): LanguageModel {
/* ... */
}
configureSession(session: Session) {
/* ... */
}
getTools(): ToolSet {
return {
getWeather: tool({
description: "Get the current weather for a city",
inputSchema: z.object({
city: z.string().describe("City name"),
}),
execute: async ({ city }) => {
const res = await fetch(
`https://api.weatherapi.com/v1/current.json?key=${this.env.WEATHER_KEY}&q=${city}`,
);
return res.json();
},
}),
};
}
}Think 自动合并多源工具。每轮模型可访问:
- 工作区工具 — read、write、edit、list、find、grep、delete、bash(内置)
- 你的工具 — 来自
getTools() - 扩展工具 — 来自已加载扩展
- 会话工具 — set_context、load_context、search_context(来自
configureSession) - 技能工具 — activate_skill、read_skill_resource 与可选 run_skill_script(来自
getSkills()) - MCP 工具 — 来自已连接 MCP 服务器(如有)
- 客户端工具 — 来自浏览器(如有)
Think 提供无论入口路径均在每轮触发的钩子:
export class MyAgent extends Think {
getModel() {
/* ... */
}
beforeTurn(ctx) {
console.log(
`Turn starting: ${Object.keys(ctx.tools).length} tools available`,
);
}
onChatResponse(result) {
console.log(`Turn ${result.status}: ${result.message.parts.length} parts`);
}
}import type {
TurnContext,
TurnConfig,
ChatResponseResult,
} from "@cloudflare/think";
export class MyAgent extends Think<Env> {
getModel(): LanguageModel {
/* ... */
}
beforeTurn(ctx: TurnContext): TurnConfig | void {
console.log(
`Turn starting: ${Object.keys(ctx.tools).length} tools available`,
);
}
onChatResponse(result: ChatResponseResult) {
console.log(`Turn ${result.status}: ${result.message.parts.length} parts`);
}
}完整参考请参阅生命周期钩子。
- 生命周期钩子 — 控制模型行为、按轮切换模型、限制工具
- 工具 — 工作区工具、代码执行、扩展
- 客户端工具 — 浏览器端工具、审批流程、并发
- Sub-agent RPC 与编程式轮次 — RPC 流式、定时轮次、recovery
- 会话 — 上下文块、压缩、搜索、多会话