快速开始
构建可以持久化、思考和行动的 AI agent。Agent 运行在 Cloudflare 全球网络上,跨请求维持状态,并通过 WebSockets 与客户端实时连接。
你将构建什么: 一个计数器 agent,带持久状态,实时同步到 React 前端。
预计时间: 约 10 分钟
创建新项目
npm yarn pnpm
npm create cloudflare@latest -- --template cloudflare/agents-starter
yarn create cloudflare --template cloudflare/agents-starter
pnpm create cloudflare@latest --template cloudflare/agents-starter
然后安装依赖并启动开发服务器:
Terminal 窗口
cd my-agent
npm install
npm run dev
这会创建一个项目,包含:
src/server.ts— 你的 agent 代码src/client.tsx— React 前端wrangler.jsonc— Cloudflare 配置tsconfig.json— 继承自agents/tsconfig,确保 decorator 和模块设置正确vite.config.ts— 包含agents/vite插件以支持 decorator
starter 模板包含两个重要的 SDK 集成。如果你是手动配置项目,这两项都需要加上:
tsconfig.json — 继承 agents/tsconfig,设置了 target: "ES2021" 等推荐选项:
{
"extends": "agents/tsconfig"
}
vite.config.ts — 包含 agents() 插件,处理 TC39 decorator 转换(在 Vite 8 中 @callable() 必需):
TypeScript
import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
import agents from "agents/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [agents(), react(), cloudflare()],
});
打开 http://localhost:5173 ↗ 查看你的 agent 运行效果。
你的第一个 agent
从零构建一个简单的计数器 agent。替换 src/server.ts:
JavaScript
import { Agent, routeAgentRequest, callable } from "agents";
// Define the state shape
// Create the agent
export class CounterAgent extends Agent {
// Initial state for new instances
initialState = { count: 0 };
// Methods marked with @callable can be called from the client
@callable()
increment() {
this.setState({ count: this.state.count + 1 });
return this.state.count;
}
@callable()
decrement() {
this.setState({ count: this.state.count - 1 });
return this.state.count;
}
@callable()
reset() {
this.setState({ count: 0 });
}
}
// Route requests to agents
export default {
async fetch(request, env, ctx) {
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
};
Explain Code
TypeScript
import { Agent, routeAgentRequest, callable } from "agents";
// Define the state shape
export type CounterState = {
count: number;
};
// Create the agent
export class CounterAgent extends Agent<Env, CounterState> {
// Initial state for new instances
initialState: CounterState = { count: 0 };
// Methods marked with @callable can be called from the client
@callable()
increment() {
this.setState({ count: this.state.count + 1 });
return this.state.count;
}
@callable()
decrement() {
this.setState({ count: this.state.count - 1 });
return this.state.count;
}
@callable()
reset() {
this.setState({ count: 0 });
}
}
// Route requests to agents
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
return (
(await routeAgentRequest(request, env)) ??
new Response("Not found", { status: 404 })
);
},
} satisfies ExportedHandler<Env>;
Explain Code
更新 wrangler.jsonc 以注册 agent:
JSONC
{
"name": "my-agent",
"main": "src/server.ts",
// Set this to today's date
"compatibility_date": "2026-04-29",
"compatibility_flags": ["nodejs_compat"],
"durable_objects": {
"bindings": [
{
"name": "CounterAgent",
"class_name": "CounterAgent",
},
],
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["CounterAgent"],
},
],
}
Explain Code
TOML
name = "my-agent"
main = "src/server.ts"
# Set this to today's date
compatibility_date = "2026-04-29"
compatibility_flags = [ "nodejs_compat" ]
[[durable_objects.bindings]]
name = "CounterAgent"
class_name = "CounterAgent"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "CounterAgent" ]
Explain Code
从 React 连接
替换 src/client.tsx:
src/client.tsx
import "./styles.css";
import { createRoot } from "react-dom/client";
import { useState } from "react";
import { useAgent } from "agents/react";
import type { CounterAgent, CounterState } from "./server";
export default function App() {
const [count, setCount] = useState(0);
// Connect to the Counter agent
const agent = useAgent<CounterAgent, CounterState>({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
return (
<div style={{ padding: "2rem", fontFamily: "system-ui" }}>
<h1>Counter Agent</h1>
<p style={{ fontSize: "3rem" }}>{count}</p>
<div style={{ display: "flex", gap: "1rem" }}>
<button onClick={() => agent.stub.decrement()}>-</button>
<button onClick={() => agent.stub.reset()}>Reset</button>
<button onClick={() => agent.stub.increment()}>+</button>
</div>
</div>
);
}
const root = createRoot(document.getElementById("root")!);
root.render(<App />);
Explain Code
要点:
useAgent通过 WebSocket 连接到你的 agentonStateUpdate在 agent 状态变化时触发agent.stub.methodName()调用 agent 上用@callable()标记的方法
刚才发生了什么?
当你点击按钮时:
- 客户端 通过 WebSocket 调用
agent.stub.increment() - Agent 运行
increment(),通过setState()更新状态 - 状态 自动持久化到 SQLite
- 广播 发送给所有连接的客户端
- React 通过
onStateUpdate更新
flowchart LR
A[“Browser
(React)”] <–>|WebSocket| B[“Agent
(Counter)”]
B –> C[“SQLite
(State)”]
关键概念
| 概念 | 含义 |
|---|---|
| Agent 实例 | 每个唯一的名称对应一个独立的 agent。CounterAgent:user-123 与 CounterAgent:user-456 是分开的 |
| 持久状态 | 状态挺过重启、部署和休眠,存储在 SQLite 中 |
| 实时同步 | 连接到同一个 agent 的所有客户端会立即收到状态更新 |
| 休眠 | 没有客户端连接时,agent 进入休眠(零成本)。下次请求时被唤醒 |
从 vanilla JavaScript 连接
如果你不使用 React:
JavaScript
import { AgentClient } from "agents/client";
const agent = new AgentClient({
agent: "CounterAgent",
name: "my-counter", // optional, defaults to "default"
onStateUpdate: (state) => {
console.log("New count:", state.count);
},
});
// Call methods
await agent.call("increment");
await agent.call("reset");
Explain Code
TypeScript
import { AgentClient } from "agents/client";
const agent = new AgentClient({
agent: "CounterAgent",
name: "my-counter", // optional, defaults to "default"
onStateUpdate: (state) => {
console.log("New count:", state.count);
},
});
// Call methods
await agent.call("increment");
await agent.call("reset");
Explain Code
部署到 Cloudflare
Terminal 窗口
npm run deploy
你的 agent 现在已经上线 Cloudflare 全球网络,运行在靠近用户的位置。
故障排查
“Agent not found” 或 404 错误
确保:
- Agent 类已从你的服务端文件中导出
wrangler.jsonc中包含 binding 和 migration- 客户端中的 agent 名称与类名匹配(不区分大小写)
状态没有同步
检查:
- 你调用的是
this.setState(),而不是直接修改this.state - 客户端中已经接好
onStateUpdate回调 - WebSocket 连接已建立(检查浏览器开发者工具)
“Method X is not callable” 错误
确保你的方法用 @callable() 装饰:
JavaScript
import { Agent, callable } from "agents";
export class MyAgent extends Agent {
@callable()
increment() {
// ...
}
}
TypeScript
import { Agent, callable } from "agents";
export class MyAgent extends Agent {
@callable()
increment() {
// ...
}
}
agent.stub 的类型错误
加上 agent 和 state 的类型参数:
JavaScript
import { useAgent } from "agents/react";
// Pass the agent and state types to useAgent
const agent = useAgent({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
// Now agent.stub is fully typed
agent.stub.increment();
TypeScript
import { useAgent } from "agents/react";
import type { CounterAgent, CounterState } from "./server";
// Pass the agent and state types to useAgent
const agent = useAgent<CounterAgent, CounterState>({
agent: "CounterAgent",
onStateUpdate: (state) => setCount(state.count),
});
// Now agent.stub is fully typed
agent.stub.increment();
Explain Code
@callable() 引发 SyntaxError: Invalid or unexpected token
如果开发服务器报 SyntaxError: Invalid or unexpected token,在 tsconfig.json 中设置 "target": "ES2021"。这能让 Vite 的 esbuild 转换器把 TC39 装饰器降级,而不是当作原生语法直接传递。
{
"compilerOptions": {
"target": "ES2021"
}
}
警告
不要在 tsconfig.json 中设置 "experimentalDecorators": true。Agents SDK 使用的是 TC39 标准装饰器 ↗,不是 TypeScript 旧版装饰器。开启 experimentalDecorators 会应用一种不兼容的转换,在运行时静默破坏 @callable()。
下一步
现在你已经有一个可用的 agent,可以继续探索这些主题:
常见模式
| 学习如何 | 参阅 |
|---|---|
| 添加 AI/LLM 能力 | 使用 AI 模型 |
| 通过 MCP 暴露工具 | MCP 服务器 |
| 运行后台任务 | 调度任务 |
| 处理邮件 | 邮件路由 |
| 使用 Cloudflare Workflows | 运行 Workflows |
进一步探索
状态管理 深入了解 setState()、initialState 和 onStateChanged()。
客户端 SDK 完整的 useAgent 和 AgentClient API 参考。
可调用方法 用 @callable() 把方法暴露给客户端。
调度任务 按延迟、调度或 cron 运行任务。