@cloudflare/think 让你通过扩展单个基类构建有状态的 AI 聊天 Agent——可流式回复、记住对话并调用工具。你通过 getModel() 提供模型,Think 为你接入其余聊天生命周期:智能体循环(模型调用工具、读取结果并持续直到有答案)、消息持久化、流式传输、客户端工具、流恢复与扩展——均由 Durable Object SQLite 支撑。
Think 既可作为顶层 Agent(通过 useAgentChat 向浏览器客户端 WebSocket 聊天),也可作为子 Agent(由另一 Agent 通过 chat() RPC 驱动)。
npm i @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-provideryarn add @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-providerpnpm add @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-providerbun add @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-providerThink 支持 AI SDK v6 与 v7。使用 ai@^6 配合 @ai-sdk/react@^3,或 ai@^7 配合 @ai-sdk/react@^4。整个项目保持 AI SDK 包主版本一致。
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",
);
}
}
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",
);
}
}
export default {
async fetch(request: Request, env: Env) {
return (
(await routeAgentRequest(request, env)) ||
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;仅此而已。Think 处理 WebSocket 聊天协议、消息持久化、智能体循环、消息清理、流恢复、客户端工具支持与工作区文件工具。
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>
{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");
sendMessage({ text: input.value });
input.value = "";
}}
>
<input name="input" placeholder="Send a message..." />
<button type="submit">Send</button>
</form>
</div>
);
}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>
{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;
sendMessage({ text: input.value });
input.value = "";
}}
>
<input name="input" placeholder="Send a message..." />
<button type="submit">Send</button>
</form>
</div>
);
}{
"$schema": "./node_modules/wrangler/config-schema.json",
// Set this to today's date
"compatibility_date": "2026-08-17",
"compatibility_flags": [
"nodejs_compat"
],
"ai": {
"binding": "AI"
},
"durable_objects": {
"bindings": [
{
"class_name": "MyAgent",
"name": "MyAgent"
}
]
},
"migrations": [
{
"new_sqlite_classes": [
"MyAgent"
],
"tag": "v1"
}
]
}# Set this to today's date
compatibility_date = "2026-08-17"
compatibility_flags = ["nodejs_compat"]
[ai]
binding = "AI"
[[durable_objects.bindings]]
class_name = "MyAgent"
name = "MyAgent"
[[migrations]]
new_sqlite_classes = ["MyAgent"]
tag = "v1"Think 与 AIChatAgent 均扩展 Agent 并使用相同 cf_agent_chat_* WebSocket 协议,但目标不同。
AIChatAgent 是协议适配器。你重写 onChatMessage 并负责调用 streamText、接入工具、转换消息并返回 Response。AIChatAgent 处理管道——消息持久化、流式、中止、恢复——但 LLM 调用完全由你负责。
Think 是约定式框架。它替你决策:getModel() 返回模型,getSystemPrompt() 或 configureSession() 设置提示词,getTools() 返回工具。默认 onChatMessage 运行完整智能体循环。你重写各个部分,而非整条管道。
| 关注点 | AIChatAgent | Think |
|---|---|---|
| 最小子类 | ~15 行(接入 streamText + 工具 + 系统提示词 + 响应) |
3 行(仅 getModel()) |
| 存储 | 扁平 SQL 表 | Session:树形消息、上下文块、压缩、FTS5 |
| 重新生成 | 破坏性(旧响应删除) | 非破坏性分支(保留旧响应) |
| 上下文管理 | 手动 | 带 LLM 可写持久化记忆的上下文块 |
| 子 Agent RPC | 未内置 | 带 StreamCallback 的 chat() |
| 程序化轮次 | saveMessages() |
saveMessages()、submitMessages()、continueLastTurn() |
| 压缩 | maxPersistedMessages(删除最旧) |
通过覆盖层的非破坏性摘要 |
| 搜索 | 不可用 | 每会话与跨会话的 FTS5 全文搜索 |
- 需要完全控制 LLM 调用(RAG、多模型、自定义流式)
- 需要
Response返回类型用于 HTTP 中间件或测试 - 构建无记忆需求的简单聊天机器人
- 想快速交付(3 行子类全部接好)
- 需要持久化记忆(模型可读写的上下文块)
- 需要长对话(非破坏性压缩)
- 需要对话搜索(FTS5)
- 构建子 Agent 系统(带流式的父子 RPC)
- 需要主动式 Agent(来自调度任务或 webhook 的程序化轮次)
- 需要 webhook 或 RPC 调用方的持久异步提交
Think 有多种启动或继续轮次的方式。它们都汇入一个公共入口——runTurn(options)——旧方法仍作为便捷快捷方式保留。
runTurn() 是统一的轮次准入 API。一个方法、三种模式,由 options.mode 选择:
| 模式 | 适用场景 | 返回 | 快捷方式 |
|---|---|---|---|
"wait"(默认) |
调用方可阻塞至模型响应完成 | Promise<TurnResult> |
saveMessages() |
"submit" |
调用方需要快速持久接受与后续状态 | Promise<SubmitMessagesResult> |
submitMessages() |
"stream" |
调用方希望响应流式到回调(RPC) | Promise<void> |
chat() |
input 接受字符串、UIMessage、消息数组,或在 wait 与 stream 模式下接受在准入时求值的函数 (current) => UIMessage[]。(submit 不接受函数形式的输入。)
export class Assistant extends Think {
async examples(inboundEventId) {
// wait — block for the result
const result = await this.runTurn({ input: "Summarize the latest thread" });
if (result.status === "completed") {
// result.message is the assistant message; result.continuation is false
}
// submit — durable acceptance, check status later
const submission = await this.runTurn({
mode: "submit",
input: "Process this webhook",
idempotencyKey: inboundEventId, // dedupe; safe to retry
});
// submission.accepted is true on first accept; submission.status is "pending"
// stream — drive a callback (the same surface as chat())
await this.runTurn({
mode: "stream",
input: "Stream me",
callback: {
onStart({ requestId }) {},
onEvent(json) {}, // UIMessageChunk JSON
onDone() {},
onError(error) {},
},
});
// continuation — continue the last assistant turn instead of sending input
await this.runTurn({ continuation: true });
}
}export class Assistant extends Think<Env> {
async examples(inboundEventId: string) {
// wait — block for the result
const result = await this.runTurn({ input: "Summarize the latest thread" });
if (result.status === "completed") {
// result.message is the assistant message; result.continuation is false
}
// submit — durable acceptance, check status later
const submission = await this.runTurn({
mode: "submit",
input: "Process this webhook",
idempotencyKey: inboundEventId, // dedupe; safe to retry
});
// submission.accepted is true on first accept; submission.status is "pending"
// stream — drive a callback (the same surface as chat())
await this.runTurn({
mode: "stream",
input: "Stream me",
callback: {
onStart({ requestId }) {},
onEvent(json) {}, // UIMessageChunk JSON
onDone() {},
onError(error) {},
},
});
// continuation — continue the last assistant turn instead of sending input
await this.runTurn({ continuation: true });
}
}关键行为:
- 阻塞模式不能嵌套。 在活跃轮次内调用
wait/stream/continuation(或等效快捷方式)——例如从工具的execute内——会抛出异常,因会死锁轮次队列。轮次内使用runTurn({ mode: "submit" })(持久,当前轮次释放队列后运行)或addMessages()(仅写入对话记录,无推理)。 submit幂等。 传入submissionId和/或idempotencyKey;用已知键重提交返回accepted: false的现有记录而非启动第二个轮次。见程序化提交。- 恢复安全。 启用
chatRecovery时,wait、stream与已排空的submit路径均在恢复 fiber 内运行推理,被中断的轮次可在驱逐后继续。
runTurn 与其选项、结果类型一并导出:RunTurnOptions、RunTurnWait、RunTurnSubmit、RunTurnStream、TurnInputMessages、TurnResult。
下表将各场景映射到最直接调用。各快捷方式签名不变;需要更窄接口时使用它们,或需要单一心智模型时用 runTurn()。
| 用例 | API |
|---|---|
| 浏览器用户发送聊天消息 | 通过 WebSocket 聊天协议的 useAgentChat |
| 服务端代码可等待模型响应 | saveMessages() |
| 服务端代码需要快速持久接受与后续状态 | submitMessages() |
| 代码应创建周期性提示词驱动的轮次或处理函数 | getScheduledTasks() |
| 父代码需要对特定子 Agent 的直接流式 RPC | subAgent(...).chat() |
| 父 Agent 将工作委托给保留的子 Agent | agentTool() 或 runAgentTool() |
| 围绕轮次的幂等应用自有副作用 | startFiber() |
| 协调多步持久编排 | Workflows |
| 添加上下文或消息而不启动模型轮次 | addMessages() |
| 高级子类或恢复代码继续助手轮次 | continueLastTurn() |
调用方拥有触发条件且可等待轮次完成时用 saveMessages()。超时歧义会使重试不安全时用 submitMessages()。
用 addMessages() 写入对话记录 而不启动模型轮次——用于导入先前历史或注入下一轮次应看到的背景上下文:
export class Assistant extends Think {
async importContext() {
await this.addMessages([
{
id: crypto.randomUUID(),
role: "user",
parts: [{ type: "text", text: "Imported context" }],
},
]);
}
}export class Assistant extends Think<Env> {
async importContext() {
await this.addMessages([
{
id: crypto.randomUUID(),
role: "user",
parts: [{ type: "text", text: "Imported context" }],
},
]);
}
}addMessages() 追加(或 upsert)到 Session 树:
- 不运行推理,不进入轮次队列,因此可在工具的
execute内安全调用而不死锁。 - 数组条目线性追加(每条挂接到前一条),导入历史保持单一路径。默认首条消息挂接到最新已提交叶子;传
parentId挂接到其他位置,或null为根消息。 - 追加按消息 id 幂等。传
{ mode: "upsert" }原地更新现有消息。
支持的模式是「添加上下文,再运行轮次」:调用 addMessages(),然后 runTurn()。
代码拥有转发、取消与重放策略时用 chat() 做底层父子流式。父模型或 workflow 委托子 Agent 且需要保留的子运行、事件重放、中止桥接与 UI 下钻时用 Agent 作为工具。
Think 外的持久单元是轮次周围的应用任务时用 startFiber():一次接受 webhook、恢复序列化渠道或线程目标、发布可见回复或记录应用级恢复策略。Think 提交拥有对话准入与轮次序列化;托管 fiber 拥有外部任务接受、幂等副作用与应用恢复。
快速入门
配置
工具
操作
频道
生命周期钩子
客户端工具
信使
调度任务
Workflows
子 Agent RPC
程序化提交
持久恢复
Agent Skills
Think 的设计受 Pi ↗ 启发。