INTEGRITY Документация

Think

@cloudflare/think позволяет создать чат-агента ИИ с сохранением состояния, то есть агента, который передаёт ответы потоково, запоминает разговор и вызывает инструменты, путём расширения одного базового класса. Модели вы передаёте getModel(), а остальной жизненный цикл чата настраивает за вас Think: агентный цикл (модель вызывает инструменты, читает результаты и продолжает работу, пока не получит ответ), сохранение сообщений, потоковую передачу, клиентские инструменты, возобновление потока и расширения. Всё это на базе Durable Object SQLite.

Think работает одновременно как агент верхнего уровня (обмен сообщениями по WebSocket с браузерными клиентами через useAgentChat) и дочерний агент (дочерний агент, которым другой агент управляет по RPC через chat()).

быстрый старт

Установить

npm i @cloudflare/think @cloudflare/ai-chat agents ai @cloudflare/shell zod workers-ai-provider

Think поддерживает 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, сохранение сообщений, agentic-цикл, санацию сообщений, возобновление потока, поддержку клиентских инструментов и файловые инструменты рабочего пространства.

Клиент

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-28",
  "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-28"
compatibility_flags = ["nodejs_compat"]

[ai]
binding = "AI"

[[durable_objects.bindings]]
class_name = "MyAgent"
name = "MyAgent"

[[migrations]]
new_sqlite_classes = ["MyAgent"]
tag = "v1"

Трассировка

Think использует wrapAISDK() внутренне для инструментирования ходов модели, вызовов инструментов и сегментов жизненного цикла подтверждений. Оборачивать AI SDK или настраивать адаптер не требуется. Чтобы отправить invoke_agent, chat, execute_tool, а также tool_approval spans в Workers Observability, включите Workers traces:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "observability": {
    "traces": {
      "enabled": true
    }
  }
}
[observability.traces]
enabled = true

Трассировки отображаются на вкладке Agents в Cloudflare Dashboard. Вы можете просматривать диалоги и временные шкалы трассировок. Чтобы отключить трассировку, задайте observability.traces.enabled к false. Вам не нужно менять класс агента Think.

Сведения об атрибутах span, управлении полезной нагрузкой, экспорте трасировок и прямой настройке AI SDK v6 или v7 см. в Трассировка.

Think в сравнении с AIChatAgent

И Think, и AIChatAgent расширьте Agent и используют один и тот же cf_agent_chat_* протокол WebSocket. У них разные задачи.

AIChatAgent это адаптер протокола. Вы переопределяете onChatMessage и отвечают за вызов streamText, подключая инструменты, преобразуя сообщения и возвращая Response. AIChatAgent берёт на себя всю техническую часть: сохранение сообщений, стриминг, прерывание, возобновление, но вызов LLM остаётся полностью на вашей стороне.

Think это фреймворк с готовыми решениями. Он принимает решения за вас: getModel() возвращает model, getSystemPrompt() или configureSession() задаёт промпт, getTools() возвращает tools. По умолчанию onChatMessage выполняет полный agentic loop. Вы переопределяете отдельные части, а не весь pipeline целиком.

Проблема AIChatAgent Think
Минимальный подкласс ~15 строк (wire streamText + инструменты + системный промпт + ответ) 3 строки (getModel() только)
Хранилище Плоская таблица SQL Session: древовидные сообщения, контекстные блоки, компактизация, FTS5
Регенерация Деструктивное (старый ответ удаляется) Неразрушающее ветвление (старые ответы сохраняются)
Управление контекстом Вручную Блоки контекста с постоянной памятью, доступной для записи LLM
RPC дочернего агента Не встроено chat() с StreamCallback
Программные ходы saveMessages() saveMessages(), submitMessages(), continueLastTurn()
Компактизация maxPersistedMessages (удаляет самые старые) Неразрушающие сводки через оверлеи
Поиск Недоступно Полнотекстовый поиск FTS5 в рамках сессии и между сессиями

Когда использовать AIChatAgent

Когда использовать Think

Выберите API ходов

У Think есть несколько способов начать ход или продолжить его. Все они сходятся в одной публичной точке входа: runTurn(options) : а старые методы остаются как удобные сокращения.

runTurn()

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 });
	}
}

Основные особенности поведения:

runTurn экспортируется вместе с типами его опций и результата: RunTurnOptions, RunTurnWait, RunTurnSubmit, RunTurnStream, TurnInputMessages, а также TurnResult.

Выберите сокращение

Таблица ниже сопоставляет каждый сценарий с наиболее прямым вызовом. Сигнатура каждого шортката не меняется, используйте их, когда нужна более узкая поверхность, или используйте runTurn() когда нужна единая ментальная модель.

Сценарий использования API
Пользователь браузера отправляет сообщения чата useAgentChat через протокол чата WebSocket
Код сервера может дождаться ответа модели saveMessages()
Коду сервера нужны быстрое устойчивое принятие запроса и последующий статус submitMessages()
Код должен создавать повторяющиеся ходы, управляемые промптом, или обработчики getScheduledTasks()
Родительскому коду требуется прямой потоковый RPC к конкретному дочернему агенту subAgent(...).chat()
Родительский агент делегирует работу удерживаемому дочернему агенту 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:

Поддерживаемый паттерн: «добавить контекст, затем выполнить ход». Вызовите addMessages(), затем runTurn().

Используйте chat() для низкоуровневой потоковой передачи от родителя к потомку, когда ваш код сам управляет пересылкой, отменой и политикой повтора. Используйте Agents как инструменты когда родительская модель или workflow делегирует работу дочернему агенту и вам нужны сохранённые дочерние запуски, event replay, проброс abort и детализация в UI.

Используйте startFiber() вне Think, когда устойчивой единицей является задача приложения вокруг хода: однократный приём webhook, восстановление сериализованного канала или целевого треда, публикация видимого ответа либо запись политики восстановления на уровне приложения. Отправки в Think отвечают за приём разговора и сериализацию хода; управляемые fiber отвечают за приём внешних задач, идемпотентные побочные эффекты и восстановление приложения.

В этом разделе

Конфигурация

Переопределение конфигурации, динамическая конфигурация и интеграция с Session.

Инструменты

Инструменты рабочей области, выполнение кода, инструменты браузера и расширения.

Действия

Server actions с идемпотентностью, подтверждениями, авторизацией и вложениями ответа.

Каналы

Политика для отдельных каналов, выбор канала и уведомления out-of-band.

Мессенджеры

Получайте вебхуки мессенджеров Chat SDK и отвечайте на них.

Workflows

Устойчивые шаги рассуждений, управляемые моделью, внутри Cloudflare Workflows.

Agent Skills

Инструкции, ресурсы и скрипты по запросу через getSkills().

Благодарности

Дизайн Think вдохновлён Pi ↗.

Пример

Пример assistant

Изучите многосессионного ассистента Think с маршрутизацией по подагентам, общим рабочим пространством, MCP, восстановлением чата и GitHub OAuth.