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

Добавить в существующий проект

Это руководство показывает, как добавить агентов в существующий проект Cloudflare Workers. Если вы начинаете с нуля, обратитесь к Создание чат-агента взамен.

Предварительные требования

1. Установите пакет

npm i agents

Для приложений на React дополнительные пакеты не нужны: привязки React включены по умолчанию.

Для приложений на Hono:

npm i agents hono-agents

2. Создайте Agent

Создайте новый файл для своего агента (например, src/agents/counter.ts):

import { Agent, callable } from "agents";

export class CounterAgent extends Agent {
	initialState = { count: 0 };

	@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;
	}
}
import { Agent, callable } from "agents";

export type CounterState = {
	count: number;
};

export class CounterAgent extends Agent<Env, CounterState> {
	initialState: CounterState = { count: 0 };

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

3. Обновите конфигурацию Wrangler

Добавьте привязку Durable Object и миграцию:

{
	"name": "my-existing-project",
	"main": "src/index.ts",
	// Set this to today's date
	"compatibility_date": "2026-08-28",
	"compatibility_flags": ["nodejs_compat"],

	"durable_objects": {
		"bindings": [
			{
				"name": "CounterAgent",
				"class_name": "CounterAgent",
			},
		],
	},

	"migrations": [
		{
			"tag": "v1",
			"new_sqlite_classes": ["CounterAgent"],
		},
	],
}
name = "my-existing-project"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
compatibility_flags = [ "nodejs_compat" ]

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

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

Основные моменты:

4. Настройте TypeScript и Vite

Если вы используете @callable() декораторы (как в примере выше), нужны две конфигурации сборки.

tsconfig.json : расширьте agents/tsconfig (или установите "target": "ES2021" вручную):

{
	"extends": "agents/tsconfig"
}

Если у вас уже есть tsconfig.json с пользовательскими настройками вы можете расширять и переопределять:

{
	"extends": "agents/tsconfig",
	"compilerOptions": {
		"paths": { "~/*": ["./src/*"] }
	}
}

vite.config.ts : добавьте agents() плагин (обрабатывает трансформации декораторов TC39 для Vite 8):

import agents from "agents/vite";

export default defineConfig({
	plugins: [
		agents(),
		// ... your existing plugins
	],
});
import agents from "agents/vite";

export default defineConfig({
	plugins: [
		agents(),
		// ... your existing plugins
	],
});

Если ваш проект не использует Vite, tsconfig.json одного изменения достаточно, но ваш бандлер должен поддерживать декораторы TC39 (stage 3, версия 2023-11).

Подробнее см. в Конфигурация TypeScript и Конфигурация Vite справочник.

5. Экспортируйте класс Agent

Класс вашего агента должен экспортироваться из главной точки входа. Обновите свой src/index.ts:

// Export the agent class (required for Durable Objects)
export { CounterAgent } from "./agents/counter";

// Your existing exports...
export default {
	// ...
};
// Export the agent class (required for Durable Objects)
export { CounterAgent } from "./agents/counter";

// Your existing exports...
export default {
	// ...
} satisfies ExportedHandler<Env>;

6. Настройте маршрутизацию

Выберите подход, который соответствует структуре вашего проекта:

Обычные Workers (fetch handler)

import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";

export default {
	async fetch(request, env, ctx) {
		// Try agent routing first
		const agentResponse = await routeAgentRequest(request, env);
		if (agentResponse) return agentResponse;

		// Your existing routing logic
		const url = new URL(request.url);
		if (url.pathname === "/api/hello") {
			return Response.json({ message: "Hello!" });
		}

		return new Response("Not found", { status: 404 });
	},
};
import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		// Try agent routing first
		const agentResponse = await routeAgentRequest(request, env);
		if (agentResponse) return agentResponse;

		// Your existing routing logic
		const url = new URL(request.url);
		if (url.pathname === "/api/hello") {
			return Response.json({ message: "Hello!" });
		}

		return new Response("Not found", { status: 404 });
	},
} satisfies ExportedHandler<Env>;

Hono

import { Hono } from "hono";
import { agentsMiddleware } from "hono-agents";
export { CounterAgent } from "./agents/counter";

const app = new Hono();

// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
app.use("*", agentsMiddleware());

// Your existing routes continue to work
app.get("/api/hello", (c) => c.json({ message: "Hello!" }));

export default app;
import { Hono } from "hono";
import { agentsMiddleware } from "hono-agents";
export { CounterAgent } from "./agents/counter";

const app = new Hono<{ Bindings: Env }>();

// Add agents middleware - handles WebSocket upgrades and agent HTTP requests
app.use("*", agentsMiddleware());

// Your existing routes continue to work
app.get("/api/hello", (c) => c.json({ message: "Hello!" }));

export default app;

Со статическими ресурсами

Если вы обслуживаете статические ресурсы вместе с агентами, по умолчанию в первую очередь обслуживаются статические ресурсы. Код Worker выполняется только для путей, которые не соответствуют ни одному статическому ресурсу:

import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";

export default {
	async fetch(request, env, ctx) {
		// Static assets are served automatically before this runs
		// This only handles non-asset requests

		// Route to agents
		const agentResponse = await routeAgentRequest(request, env);
		if (agentResponse) return agentResponse;

		return new Response("Not found", { status: 404 });
	},
};
import { routeAgentRequest } from "agents";
export { CounterAgent } from "./agents/counter";

export default {
	async fetch(request: Request, env: Env, ctx: ExecutionContext) {
		// Static assets are served automatically before this runs
		// This only handles non-asset requests

		// Route to agents
		const agentResponse = await routeAgentRequest(request, env);
		if (agentResponse) return agentResponse;

		return new Response("Not found", { status: 404 });
	},
} satisfies ExportedHandler<Env>;

Настройте ресурсы в конфигурационном файле Wrangler:

{
	"assets": {
		"directory": "./public",
	},
}
[assets]
directory = "./public"

7. Сгенерируйте типы TypeScript

Не пишите вручную свой Env интерфейс. Выполните wrangler types чтобы сгенерировать файл определения типов, соответствующий вашей конфигурации Wrangler. Это позволяет находить несоответствия между конфигурацией и кодом на этапе компиляции, а не на этапе развёртывания.

Перезапустите wrangler types каждый раз, когда вы добавляете или переименовываете привязку.

npx wrangler types

Это создаёт файл с определениями типов, в котором типизированы все ваши bindings, включая пространства имён Durable Object вашего агента. Этот Agent класс по умолчанию использует сгенерированный Env тип, поэтому его не нужно передавать как параметр типа: extends Agent достаточно, если вам не нужно передавать второй параметр типа для состояния (например, Agent<Env, CounterState>).

См. Конфигурация для дополнительных подробностей о генерации типов.

8. Подключитесь со стороны фронтенда

React

import { useState } from "react";
import { useAgent } from "agents/react";

function CounterWidget() {
	const [count, setCount] = useState(0);

	const agent = useAgent({
		agent: "CounterAgent",
		onStateUpdate: (state) => setCount(state.count),
	});

	return (
		<>
			{count}
			<button onClick={() => agent.stub.increment()}>+</button>
			<button onClick={() => agent.stub.decrement()}>-</button>
		</>
	);
}
import { useState } from "react";
import { useAgent } from "agents/react";
import type { CounterAgent, CounterState } from "./agents/counter";

function CounterWidget() {
	const [count, setCount] = useState(0);

	const agent = useAgent<CounterAgent, CounterState>({
		agent: "CounterAgent",
		onStateUpdate: (state) => setCount(state.count),
	});

	return (
		<>
			{count}
			<button onClick={() => agent.stub.increment()}>+</button>
			<button onClick={() => agent.stub.decrement()}>-</button>
		</>
	);
}

Основные моменты:

Vanilla JavaScript

import { AgentClient } from "agents/client";

const agent = new AgentClient({
	agent: "CounterAgent",
	name: "user-123", // Optional: unique instance name
	onStateUpdate: (state) => {
		document.getElementById("count").textContent = state.count;
	},
});

// Call methods
document.getElementById("increment").onclick = () => agent.call("increment");
import { AgentClient } from "agents/client";

const agent = new AgentClient({
	agent: "CounterAgent",
	name: "user-123", // Optional: unique instance name
	onStateUpdate: (state) => {
		document.getElementById("count").textContent = state.count;
	},
});

// Call methods
document.getElementById("increment").onclick = () => agent.call("increment");

Как это работает

Когда вы нажали кнопку:

  1. Клиент называется agent.stub.increment() через WebSocket
  2. Agent выполнил increment(), обновлённое состояние с setState()
  3. Состояние автоматически сохраняется в SQLite
  4. Рассылка отправленный всем подключённым клиентам
  5. React обновляется через onStateUpdate
flowchart LR
    A["Browser<br/>(React)"] <-->|WebSocket| B["Agent<br/>(Counter)"]
    B --> C["SQLite<br/>(State)"]

Основные понятия

Понятие Что это значит
Экземпляр Agent Каждое уникальное имя получает собственного агента. CounterAgent:user-123 отделён от CounterAgent:user-456
Постоянное состояние Состояние переживает перезапуски, повторные развёртывания и гибернацию. Оно хранится в SQLite
Синхронизация в реальном времени Все клиенты, подключённые к одному и тому же агенту, мгновенно получают обновления состояния
Гибернация Когда ни один клиент не подключён, агент переходит в спящий режим (бесплатно). Он просыпается при следующем запросе

Deploy to Cloudflare

npm run deploy

Теперь ваш агент работает в глобальной сети Cloudflare, максимально близко к вашим пользователям.

Типичные паттерны интеграции

Agents за аутентификацией

Перед маршрутизацией к агентам проверьте аутентификацию:

export default {
	async fetch(request, env) {
		// Check auth for agent routes
		if (request.url.includes("/agents/")) {
			const authResult = await checkAuth(request, env);
			if (!authResult.valid) {
				return new Response("Unauthorized", { status: 401 });
			}
		}

		const agentResponse = await routeAgentRequest(request, env);
		if (agentResponse) return agentResponse;

		// ... rest of routing
	},
};
export default {
	async fetch(request: Request, env: Env) {
		// Check auth for agent routes
		if (request.url.includes("/agents/")) {
			const authResult = await checkAuth(request, env);
			if (!authResult.valid) {
				return new Response("Unauthorized", { status: 401 });
			}
		}

		const agentResponse = await routeAgentRequest(request, env);
		if (agentResponse) return agentResponse;

		// ... rest of routing
	},
} satisfies ExportedHandler<Env>;

Пользовательский префикс пути агента

По умолчанию агенты маршрутизируются по адресу /agents/{agent-name}/{instance-name}. Вы можете настроить это:

import { routeAgentRequest } from "agents";

const agentResponse = await routeAgentRequest(request, env, {
	prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name}
});
import { routeAgentRequest } from "agents";

const agentResponse = await routeAgentRequest(request, env, {
	prefix: "/api/agents", // Now routes at /api/agents/{agent-name}/{instance-name}
});

См. Маршрутизация для дополнительных опций, включая CORS, пользовательское именование инстансов и подсказки о местоположении.

Доступ к агентам из серверного кода

Вы можете взаимодействовать с агентами прямо из кода Worker:

import { getAgentByName } from "agents";

export default {
	async fetch(request, env) {
		if (request.url.endsWith("/api/increment")) {
			// Get a specific agent instance
			const counter = await getAgentByName(env.CounterAgent, "shared-counter");
			const newCount = await counter.increment();
			return Response.json({ count: newCount });
		}
		// ...
	},
};
import { getAgentByName } from "agents";

export default {
	async fetch(request: Request, env: Env) {
		if (request.url.endsWith("/api/increment")) {
			// Get a specific agent instance
			const counter = await getAgentByName(env.CounterAgent, "shared-counter");
			const newCount = await counter.increment();
			return Response.json({ count: newCount });
		}
		// ...
	},
} satisfies ExportedHandler<Env>;

Добавление нескольких агентов

Добавьте больше агентов, расширив конфигурацию:

// src/agents/chat.ts
export class Chat extends Agent {
	// ...
}

// src/agents/scheduler.ts
export class Scheduler extends Agent {
	// ...
}
// src/agents/chat.ts
export class Chat extends Agent {
	// ...
}

// src/agents/scheduler.ts
export class Scheduler extends Agent {
	// ...
}

Обновите файл конфигурации Wrangler:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "durable_objects": {
    "bindings": [
      {
        "name": "CounterAgent",
        "class_name": "CounterAgent"
      },
      {
        "name": "Chat",
        "class_name": "Chat"
      },
      {
        "name": "Scheduler",
        "class_name": "Scheduler"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": [
        "CounterAgent",
        "Chat",
        "Scheduler"
      ]
    }
  ]
}
[[durable_objects.bindings]]
name = "CounterAgent"
class_name = "CounterAgent"

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

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

[[migrations]]
tag = "v1"
new_sqlite_classes = ["CounterAgent", "Chat", "Scheduler"]

Экспортируйте все агенты из точки входа:

export { CounterAgent } from "./agents/counter";
export { Chat } from "./agents/chat";
export { Scheduler } from "./agents/scheduler";
export { CounterAgent } from "./agents/counter";
export { Chat } from "./agents/chat";
export { Scheduler } from "./agents/scheduler";

Устранение неполадок

Agent не найден, или ошибки 404

  1. Проверьте экспорт - класс Agent должен экспортироваться из главной точки входа.
  2. Проверьте привязку - class_name в конфигурационном файле Wrangler должно точно совпадать с именем экспортируемого класса.
  3. Проверьте маршрут - маршрут по умолчанию: /agents/{'{agent-name}'}/{'{instance-name}'}. Имя агента на клиенте совпадает с именем класса (без учёта регистра).

Ошибка «No such Durable Object class»

Добавьте миграцию в файл конфигурации Wrangler:

{
  "$schema": "./node_modules/wrangler/config-schema.json",
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": [
        "YourAgentClass"
      ]
    }
  ]
}
[[migrations]]
tag = "v1"
new_sqlite_classes = ["YourAgentClass"]

Соединение WebSocket не устанавливается

Убедитесь, что ваша маршрутизация передаёт ответ без изменений:

// Correct - return the response directly
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;

// Wrong - this breaks WebSocket connections
if (agentResponse) return new Response(agentResponse.body);
// Correct - return the response directly
const agentResponse = await routeAgentRequest(request, env);
if (agentResponse) return agentResponse;

// Wrong - this breaks WebSocket connections
if (agentResponse) return new Response(agentResponse.body);

Состояние не сохраняется

Проверьте, что:

  1. Вы вызываете this.setState(), а не изменяя this.state напрямую.
  2. Класс агента находится в new_sqlite_classes в миграциях.
  3. Вы подключаетесь к тому же имени экземпляра агента.
  4. onStateUpdate колбэк подключён в вашем клиенте.
  5. Соединение WebSocket установлено (проверьте инструменты разработчика в браузере).

ошибки вида «Method X is not callable»

Убедитесь, что ваши методы декорированы @callable():

import { Agent, callable } from "agents";

export class MyAgent extends Agent {
	@callable()
	increment() {
		// ...
	}
}
import { Agent, callable } from "agents";

export class MyAgent extends Agent {
	@callable()
	increment() {
		// ...
	}
}

Ошибки типов с agent.stub

Добавьте параметры типов агента и состояния:

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

SyntaxError: Invalid or unexpected token с @callable()

Если ваш dev-сервер завершается ошибкой с SyntaxError: Invalid or unexpected token, задайте "target": "ES2021" в вашем tsconfig.json. Это гарантирует, что транспилятор esbuild в Vite преобразует декораторы TC39 в более раннюю версию синтаксиса, вместо того чтобы передавать их как нативный синтаксис.

{
	"compilerOptions": {
		"target": "ES2021"
	}
}

Следующие шаги

Теперь, когда у вас есть работающий агент, изучите следующие темы:

Типичные следующие шаги

Узнайте, как См.
Добавьте возможности ИИ/LLM Использование ИИ-моделей
Предоставление инструментов через MCP MCP-серверы
Запуск фоновых задач Планирование задач
Обрабатывайте письма Маршрутизация электронной почты
Используйте Cloudflare Workflows Запуск Workflows

Узнать больше

Управление состоянием

Подробный разбор setState(), initialState и onStateChanged().

Клиентский SDK

Полный справочник API по useAgent и AgentClient.

Вызываемые методы

Предоставлять методы клиентам с помощью @callable().

Планирование задач

Запускайте задачи с задержкой, по расписанию или через cron.

Внутреннее устройство класса Agent

Полное справочное описание жизненного цикла и методов.

Agents API

Полный справочник API для Agents SDK.