← Создание агентов на Cloudflare / agents / harnesses / think
Начало работы
Создайте чат-агента с постоянной памятью, встроенными инструментами для работы с файлами и потоковой передачей, шаг за шагом.
Если вы совсем новичок в Cloudflare Agents, бегло просмотрите Что такое агенты? сначала, чтобы понять базовые идеи. В противном случае можете следовать этому руководству с самого начала.
К концу этого туториала у вас будет агент Think, который:
- Передаёт ответы потоком в чат-интерфейс на React
- Обладает постоянной памятью, которую модель может читать и записывать
- Включает инструменты для работы с файлами рабочего пространства (read, write, edit, find, grep, delete)
- Поддерживает пользовательские серверные инструменты
Предварительные требования
- Node.js 24+
- Учётная запись Cloudflare с доступом к Workers AI
- Знание TypeScript и Cloudflare Workers
1. Создайте проект
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 vite2. Настройте wrangler
Создайте 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"
}3. Определите агента
Создайте 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>;Это работающий агент. Think автоматически предоставляет:
- Протокол чата WebSocket (совместимый с
useAgentChat) - Персистентность сообщений в SQLite
- Возобновляемый стриминг (обновление страницы воспроизводит буферизованные чанки)
- Инструменты для работы с файлами рабочей области (чтение, запись, редактирование, список, поиск, grep, удаление)
- Поддержка прерывания и отмены
- Обработка ошибок с частичным сохранением сообщений
4. Подключите клиент React
Создайте 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>5. Запустите
npx vite devОткройте браузер и отправьте сообщение. Агент отвечает потоковым текстом, а инструменты для работы с файлами рабочего пространства автоматически доступны модели.
6. Добавьте постоянную память
Переопределить 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" блок доступен для записи, и модель самостоятельно обновляет его, когда узнаёт что-то полезное.
См. Документация Sessions для блоков контекста, сжатия, search, skills и поддержки нескольких сессий.
7. Добавьте пользовательские инструменты
Переопределить 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() - Инструменты расширений : из загруженных расширений
- Инструменты Session : set_context, load_context, search_context (из
configureSession) - Инструменты skills : activate_skill, read_skill_resource и необязательный run_skill_script (из
getSkills()) - MCP-инструменты : из подключённых MCP-серверов (если они есть)
- Клиентские инструменты : из браузера (если он есть)
8. Добавьте хуки жизненного цикла
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`);
}
}См. Хуки жизненного цикла для полного справочника.
Следующие шаги
- Хуки жизненного цикла : управление поведением модели, переключение моделей на каждом ходу, ограничение инструментов
- Инструменты : инструменты рабочей области, выполнение кода, расширения
- Клиентские инструменты : инструменты на стороне браузера, процессы подтверждения, параллелизм
- RPC дочернего агента и программные ходы : потоковая передача RPC, запланированные ходы, восстановление
- Сеансы : блоки контекста, компакция, поиск, несколько сессий