← Создание агентов на Cloudflare / agents / harnesses / think
Инструменты
На каждом ходу Think предоставляет встроенные инструменты для работы с файлами workspace, а также точки интеграции для собственных инструментов, выполнения кода и динамических расширений.
Порядок объединения инструментов
При каждом ходе 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(), см. Agent Skills) - MCP-инструменты : из подключённых MCP-серверов, когда
includeMcpToolsэтоtrue - Клиентские инструменты : из браузера (см. Клиентские инструменты)
Инструменты принадлежат агенту, который выполняет ход. Для оркестрации родитель/потомок используйте Agents как инструменты вместо передачи одноразовых инструментов через chat().
Встроенные инструменты рабочего пространства
Каждый Think agent получает this.workspace : виртуальная файловая система на базе SQLite в Durable Object. Инструменты workspace автоматически доступны модели без какой-либо настройки.
| Инструмент | Описание |
|---|---|
read |
Считывайте текст с номерами строк; передавайте изображения и PDF мультимодальным моделям |
write |
Записать содержимое в файл (создаёт родительские каталоги) |
edit |
Применить правку типа «найти и заменить» к существующему файлу (поддерживает нечёткое сопоставление) |
list |
Выводит список файлов и каталогов по указанному пути |
find |
Найдите файлы, соответствующие glob-шаблону |
grep |
Поиск в содержимом файлов по регулярному выражению или фиксированной строке |
delete |
Удалить файл или каталог |
bash |
Запуск изолированного скрипта Bash для файлов рабочего пространства |
bash инструмент включён по умолчанию. Он монтирует файлы рабочего пространства в just-bash виртуальную файловую систему, выполняется с отключённым доступом к сети и записывает созданные, изменённые и удалённые файлы, а также пустые каталоги обратно в рабочее пространство. Используйте его для рабочих процессов в стиле shell, которые объединяют несколько файловых операций; для простых операций чтения, записи и редактирования используйте более узкоспециализированные инструменты.
Чтобы ограничить объём вызовов инструмента, инструмент Bash по умолчанию делает снимок не более 1000 файлов рабочей области и пропускает файлы крупнее 1 МБ. Пропущенные файлы указываются в результате инструмента и считаются защищёнными при обратной записи, поэтому скрипт не может случайно перезаписать или удалить содержимое, которое не было смонтировано. Вы можете настроить maxWorkspaceFiles, maxWorkspaceFileBytes, maxOutputBytes, timeout, а также network через workspaceBash.
Для консервативных развёртываний отключите используемый по умолчанию инструмент Bash:
export class MyAgent extends Think {
workspaceBash = false;
getModel() {
/* ... */
}
}export class MyAgent extends Think<Env> {
workspaceBash = false;
getModel() {
/* ... */
}
}Выгрузка в R2
По умолчанию рабочее пространство хранит всё в SQLite. Для больших файлов переопределите workspace чтобы добавить перелив в R2:
import { Think } from "@cloudflare/think";
import { Workspace } from "@cloudflare/shell";
export class MyAgent extends Think {
workspace = new Workspace({
sql: this.ctx.storage.sql,
r2: this.env.R2,
name: () => this.name,
});
getModel() {
/* ... */
}
}import { Think } from "@cloudflare/think";
import { Workspace } from "@cloudflare/shell";
export class MyAgent extends Think<Env> {
override workspace = new Workspace({
sql: this.ctx.storage.sql,
r2: this.env.R2,
name: () => this.name,
});
getModel() {
/* ... */
}
}Для этого требуется привязка R2-бакета:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"r2_buckets": [
{
"binding": "R2",
"bucket_name": "agent-files"
}
]
}[[r2_buckets]]
binding = "R2"
bucket_name = "agent-files"Пользовательские инструменты
Переопределить getTools() чтобы добавить собственные инструменты. Это стандартные AI SDK tool() определения со схемами из библиотеки вроде Zod или Valibot:
import { Think } from "@cloudflare/think";
import { tool } from "ai";
import { z } from "zod";
export class MyAgent extends Think {
getModel() {
/* ... */
}
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.weather.com/v1/current?q=${city}&key=${this.env.WEATHER_KEY}`,
);
return res.json();
},
}),
};
}
}import { Think } from "@cloudflare/think";
import { tool } from "ai";
import type { ToolSet } from "ai";
import { z } from "zod";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
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.weather.com/v1/current?q=${city}&key=${this.env.WEATHER_KEY}`,
);
return res.json();
},
}),
};
}
}Пользовательские инструменты автоматически объединяются с инструментами рабочего пространства. Если пользовательский инструмент имеет то же имя, что и инструмент рабочего пространства, приоритет получает пользовательский инструмент.
Подтверждение инструмента
Инструменты могут требовать подтверждения пользователя перед выполнением с помощью needsApproval параметр:
getTools(): ToolSet {
return {
deleteFile: tool({
description: "Delete a file from the system",
inputSchema: z.object({ path: z.string() }),
needsApproval: async ({ path }) => path.startsWith("/important/"),
execute: async ({ path }) => {
await this.workspace.rm(path);
return { deleted: path };
},
}),
};
}Когда needsApproval возвращает true, вызов инструмента отправляется клиенту на подтверждение. Разговор приостанавливается, пока клиент не ответит CF_AGENT_TOOL_APPROVAL.
Переопределения инструментов для отдельного хода
beforeTurn хук может ограничивать или добавлять инструменты для конкретного хода:
beforeTurn(ctx: TurnContext) {
return {
activeTools: ["read", "write", "getWeather"],
tools: { emergencyTool: this.createEmergencyTool() },
};
}activeTools ограничивает, какие инструменты может вызывать модель. tools добавляет дополнительные инструменты только для этого хода (объединяются с уже существующими инструментами).
MCP-инструменты
Think наследует поддержку MCP-клиента от Agent базовый класс. По умолчанию Think преобразует инструменты подключённых MCP-серверов в инструменты AI SDK и добавляет их к каждому ходу.
Задайте waitForMcpConnections чтобы убедиться, что MCP-серверы подключены до запуска вывода:
export class MyAgent extends Think {
waitForMcpConnections = true; // default 10s timeout
// or: waitForMcpConnections = { timeout: 5000 };
getModel() {
/* ... */
}
}export class MyAgent extends Think<Env> {
waitForMcpConnections = true; // default 10s timeout
// or: waitForMcpConnections = { timeout: 5000 };
getModel() {
/* ... */
}
}Если вы предоставляете инструменты MCP через Code Mode или другим способом за пределами автоматического набора инструментов Think, отключите прямое предоставление инструментов AI SDK:
export class MyAgent extends Think {
includeMcpTools = false;
waitForMcpConnections = true;
getModel() {
/* ... */
}
}export class MyAgent extends Think<Env> {
includeMcpTools = false;
waitForMcpConnections = true;
getModel() {
/* ... */
}
}includeMcpTools управляет только автоматическим слиянием инструментов модели. Подключения MCP по-прежнему регистрируются, восстанавливаются, обнаруживаются и ожидают. Прямой доступ к каталогу, прямые вызовы, коннекторы Code Mode и явные this.mcp.getAITools() вызовы продолжают работать.
Используйте это свойство вместо удаления имён инструментов MCP через activeTools в beforeTurn. Think преобразует схемы MCP перед вызовом beforeTurn, поэтому activeTools не может избежать этого преобразования. Чтобы настроить runtime коннектора, см. Используйте инструменты MCP с Code Mode.
Добавьте MCP-серверы программно или через @callable методы:
import { callable } from "agents";
export class MyAgent extends Think {
getModel() {
/* ... */
}
@callable()
async addServer(name, url) {
return await this.addMcpServer(name, url);
}
@callable()
async removeServer(serverId) {
await this.removeMcpServer(serverId);
}
}import { callable } from "agents";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
@callable()
async addServer(name: string, url: string) {
return await this.addMcpServer(name, url);
}
@callable()
async removeServer(serverId: string) {
await this.removeMcpServer(serverId);
}
}Инструмент для выполнения кода
Позвольте LLM писать и выполнять JavaScript в изолированном Worker, фиксируя всё в устойчивой среде выполнения Code Mode (прерывание с повтором, подтверждения человеком, журнал аудита, переиспользуемые сниппеты). Требует @cloudflare/codemode и worker_loaders привязку.
npm install @cloudflare/codemodeОднострочник выводит всё необходимое из агента: state.* от this.workspace, executor из env.LOADER, и живой браузер (cdp.*) из env.BROWSER если привязано:
import { Think } from "@cloudflare/think";
import { createExecuteTool } from "@cloudflare/think/tools/execute";
export class MyAgent extends Think {
getModel() {
/* ... */
}
getTools() {
return {
execute: createExecuteTool(this),
};
}
}import { Think } from "@cloudflare/think";
import { createExecuteTool } from "@cloudflare/think/tools/execute";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
getTools() {
return {
execute: createExecuteTool(this),
};
}
}Контрольный список настройки:
{
"$schema": "./node_modules/wrangler/config-schema.json",
"worker_loaders": [
{
"binding": "LOADER"
}
],
"browser": {
"binding": "BROWSER"
}
}[[worker_loaders]]
binding = "LOADER"
[browser]
binding = "BROWSER" # optional — enables cdp.*// worker entry — the runtime lives in a Durable Object facet, so the class
// must be exported (the @cloudflare/codemode/vite plugin does this
// automatically; the Think framework's generated entry already includes it)
export { CodemodeRuntime } from "@cloudflare/codemode";// worker entry — the runtime lives in a Durable Object facet, so the class
// must be exported (the @cloudflare/codemode/vite plugin does this
// automatically; the Think framework's generated entry already includes it)
export { CodemodeRuntime } from "@cloudflare/codemode";Каждая отсутствующая часть завершается ошибкой с указанием названия шага.
Внутри песочницы модель видит типизированные пространства имён и SDK платформы:
tools.*: ваши инструменты AI SDK (аргументы в виде объекта, проверяемые по их схемам). Только инструменты сexecuteфункции доступны. Инструменты на стороне клиента не могут выполняться в песочнице.state.*: файловая система рабочей области (state.readFile({ path }),state.glob({ pattern }),state.planEdits(...), и так далее).cdp.*: браузер, если настроена привязка Browser Run. Инструмент execute по умолчанию используетsession: { mode: "dynamic" }: сеансы существуют в рамках одного выполнения, если только модель не продвигает один из них с помощьюcdp.startSession().codemode.search/codemode.describe/codemode.step/codemode.run: обнаружение, границы побочных эффектов и сохранённые фрагменты.
Передайте переопределения для всего, что выходит за рамки значений по умолчанию, например для настраиваемых tools.* наряду с состоянием, производным от агента:
execute: createExecuteTool(this, { tools: myDomainTools });execute: createExecuteTool(this, { tools: myDomainTools });Или полностью явные варианты (без автоматического определения агентом):
import { createWorkspaceStateBackend } from "@cloudflare/shell";
createExecuteTool({
ctx: this.ctx,
tools: myDomainTools,
state: createWorkspaceStateBackend(this.workspace),
browser: this.env.BROWSER,
loader: this.env.LOADER,
});import { createWorkspaceStateBackend } from "@cloudflare/shell";
createExecuteTool({
ctx: this.ctx,
tools: myDomainTools,
state: createWorkspaceStateBackend(this.workspace),
browser: this.env.BROWSER,
loader: this.env.LOADER,
});Подтверждения (human-in-the-loop)
Инструмент AI SDK с needsApproval не выполняется сразу внутри песочницы: вызов этого устойчиво приостанавливает запуск. Пауза возвращается как обычный результат инструмента ({ status: "paused", executionId, pending }), модель сообщает пользователю, что ей нужно, и ход завершается. Это отличается от клиентского процесса подтверждения для обычного getTools() инструменты: внутри sandbox функциональное значение needsApproval нельзя заранее вычислить относительно аргументов вызова, поэтому из осторожности всегда требует подтверждения. Think поставляется со встроенными callable-объектами для его разрешения:
approveExecution(executionId): возобновляет запуск с того места, где он остановился. Уже выполненная работа воспроизводится, а не выполняется заново. Результат заменяет приостановленный вывод в транскрипте, и чат продолжается автоматически.rejectExecution(executionId, reason?): завершает запуск с{ status: "rejected", reason }чтобы модель могла адаптироваться.pendingExecutions(): ожидающие действия (с полными аргументами) для отрисовки UI подтверждения.
Рабочий пример карточки подтверждения см. в assistant пример ↗.
Хендл runtime
createExecuteRuntime возвращает внутренние компоненты, когда host нуждается в большем, чем просто инструмент, а handle также присваивается this.codemode при создании из агента:
import { createExecuteRuntime } from "@cloudflare/think/tools/execute";
const { runtime, connectors, tool } = createExecuteRuntime(this);
await runtime.executions(); // audit trail
await runtime.expirePaused(); // reclaim stale never-approved pauses (call from a scheduled task)
await runtime.saveSnippet("name", { executionId }); // promote a script for reuseimport { createExecuteRuntime } from "@cloudflare/think/tools/execute";
const { runtime, connectors, tool } = createExecuteRuntime(this);
await runtime.executions(); // audit trail
await runtime.expirePaused(); // reclaim stale never-approved pauses (call from a scheduled task)
await runtime.saveSnippet("name", { executionId }); // promote a script for reuseИнструменты браузера
Предоставьте своему агенту доступ к Chrome DevTools Protocol (CDP) для проверки веб-страниц, извлечения данных, создания скриншотов и отладки. Требуется @cloudflare/codemode и привязку Browser Run.
import { Think } from "@cloudflare/think";
import { createBrowserTools } from "@cloudflare/think/tools/browser";
export class MyAgent extends Think {
getModel() {
/* ... */
}
getTools() {
return {
...createBrowserTools({
ctx: this.ctx,
browser: this.env.BROWSER,
loader: this.env.LOADER,
}),
};
}
}import { Think } from "@cloudflare/think";
import { createBrowserTools } from "@cloudflare/think/tools/browser";
export class MyAgent extends Think<Env> {
getModel() {
/* ... */
}
getTools() {
return {
...createBrowserTools({
ctx: this.ctx,
browser: this.env.BROWSER,
loader: this.env.LOADER,
}),
};
}
}{
"$schema": "./node_modules/wrangler/config-schema.json",
"browser": {
"binding": "BROWSER"
},
"worker_loaders": [
{
"binding": "LOADER"
}
]
}[browser]
binding = "BROWSER"
[[worker_loaders]]
binding = "LOADER"Это добавляет устойчивый CDP-инструмент, а также инструмент без сохранения состояния Quick Action инструменты, когда browser привязка присутствует:
| Инструмент | Описание |
|---|---|
browser_execute |
Выполняйте JavaScript в живом браузере через CDP (снимки экрана, чтение DOM, выполнение JS). |
browser_markdown |
Прочитайте страницу или необработанный HTML как Markdown. |
browser_extract |
Извлекайте структурированные данные со страницы с помощью ИИ. |
browser_links |
Выводит список ссылок на странице. |
browser_scrape |
Извлекайте определённые элементы по CSS-селектору. |
Передайте quickActions: false чтобы оставить только browser_execute, либо передайте quickActions: { actions, maxChars, options } чтобы настроить инструменты без сохранения состояния. Инструменты Quick Action используют общий browser привязку, не нуждаются в Worker Loader и разрешают ctx из текущего Agent автоматически. Чтобы использовать только инструменты без сохранения состояния, импортируйте createQuickActionTools от @cloudflare/think/tools/browser.
Инструмент работает на основе runtime Code Mode с cdp коннектор: модель пишет асинхронные arrow-функции, которые выполняются в изолированной среде Worker isolate, с cdp.send(), cdp.attachToTarget(), cdp.spec() (актуальное нормализованное описание протокола), вспомогательные функции сессии (cdp.startSession(), cdp.sessionInfo(), cdp.closeSession()), и вспомогательные функции для отладочного протоколирования. Выполнения записываются для прерывания и повторного воспроизведения, поэтому сеансы браузера переживают паузы на подтверждение.
По умолчанию каждое выполнение получает новую сессию браузера (one-shot), который уничтожается по завершении запуска. Передайте session: { mode: "dynamic" } чтобы позволить модели повысить статус сессии с cdp.startSession() чтобы последующие выполнения продолжались в том же браузере, либо session: { mode: "reuse", key } для именованной долгоживущей сессии. Устаревшие сессии удаляются в ходе очистки коннектора sweep() : вызовите её из запланированной задачи.
Для пользовательского эндпойнта Chrome передайте cdpUrl вместо browser:
createBrowserTools({
ctx: this.ctx,
cdpUrl: "http://localhost:9222",
loader: this.env.LOADER,
});createBrowserTools({
ctx: this.ctx,
cdpUrl: "http://localhost:9222",
loader: this.env.LOADER,
});Полное описание API CDP-коннектора см. в Просматривать веб.
Расширения
Расширения представляют собой динамически загружаемые sandbox Workers, которые добавляют инструменты во время выполнения. LLM может написать исходный код расширения, загрузить его и использовать новые инструменты в следующем ходу.
Для расширений требуется worker_loaders в качестве привязки:
import { Think } from "@cloudflare/think";
export class MyAgent extends Think {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
}import { Think } from "@cloudflare/think";
export class MyAgent extends Think<Env> {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
}Статические расширения
Определите расширения, которые загружаются при запуске:
export class MyAgent extends Think {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getExtensions() {
return [
{
manifest: {
name: "math",
version: "1.0.0",
permissions: { network: false },
},
source: `({
tools: {
add: {
description: "Add two numbers",
parameters: { a: { type: "number" }, b: { type: "number" } },
execute: async ({ a, b }) => ({ result: a + b })
}
}
})`,
},
];
}
}export class MyAgent extends Think<Env> {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getExtensions() {
return [
{
manifest: {
name: "math",
version: "1.0.0",
permissions: { network: false },
},
source: `({
tools: {
add: {
description: "Add two numbers",
parameters: { a: { type: "number" }, b: { type: "number" } },
execute: async ({ a, b }) => ({ result: a + b })
}
}
})`,
},
];
}
}Инструменты расширений находятся в отдельном пространстве имён : например, math расширение с помощью add инструмент становится math_add в наборе инструментов модели.
Расширения на основе LLM
Предоставьте модели createExtensionTools чтобы он мог динамически загружать расширения:
import { createExtensionTools } from "@cloudflare/think/tools/extensions";
export class MyAgent extends Think {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getTools() {
return {
...createExtensionTools({ manager: this.extensionManager }),
...this.extensionManager.getTools(),
};
}
}import { createExtensionTools } from "@cloudflare/think/tools/extensions";
export class MyAgent extends Think<Env> {
extensionLoader = this.env.LOADER;
getModel() {
/* ... */
}
getTools() {
return {
...createExtensionTools({ manager: this.extensionManager! }),
...this.extensionManager!.getTools(),
};
}
}Это даёт модели два инструмента:
load_extension: загружает новое расширение из исходного кода JavaScriptlist_extensions: выводит список текущих загруженных расширений
Блоки контекста расширения
Расширения могут объявлять блоки контекста в своём манифесте. Они автоматически регистрируются в Session:
getExtensions() {
return [{
manifest: {
name: "notes",
version: "1.0.0",
permissions: { network: false },
context: [
{ label: "scratchpad", description: "Extension scratch space", maxTokens: 500 },
],
},
source: `({ tools: { /* ... */ } })`,
}];
}Контекстный блок регистрируется как notes_scratchpad (разделённые на пространства имён по названию расширения).
Пользовательские бэкенды рабочего пространства
Отдельные factory для инструментов экспортируются для использования с собственными backend хранения:
import {
createReadTool,
createWriteTool,
createEditTool,
createListTool,
createFindTool,
createGrepTool,
createDeleteTool,
createWorkspaceTools,
} from "@cloudflare/think/tools/workspace";import {
createReadTool,
createWriteTool,
createEditTool,
createListTool,
createFindTool,
createGrepTool,
createDeleteTool,
createWorkspaceTools,
} from "@cloudflare/think/tools/workspace";Реализуйте интерфейс операций для вашего бэкенда хранилища:
const myReadOps = {
readFile: async (path) => fetchFromMyStorage(path),
stat: async (path) => getFileInfo(path),
};
const readTool = createReadTool({ ops: myReadOps });import type { ReadOperations } from "@cloudflare/think/tools/workspace";
const myReadOps: ReadOperations = {
readFile: async (path) => fetchFromMyStorage(path),
stat: async (path) => getFileInfo(path),
};
const readTool = createReadTool({ ops: myReadOps });Или создайте полный набор из Workspace, при необходимости отключив инструмент Bash:
import { createWorkspaceTools } from "@cloudflare/think/tools/workspace";
const tools = createWorkspaceTools(myCustomWorkspace);
const toolsWithoutBash = createWorkspaceTools(myCustomWorkspace, {
bash: false,
});import { createWorkspaceTools } from "@cloudflare/think/tools/workspace";
const tools = createWorkspaceTools(myCustomWorkspace);
const toolsWithoutBash = createWorkspaceTools(myCustomWorkspace, {
bash: false,
});