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

Вызов LLM

Agents меняют подход к работе с LLM. В Worker без сохранения состояния каждый запрос начинается с нуля: вы заново собираете контекст, вызываете модель, возвращаете ответ и забываете обо всём. Agent сохраняет состояние между вызовами, остаётся подключённым к клиентам через WebSocket и может вызывать модели по собственному расписанию даже без присутствия пользователя.

Эта страница описывает паттерны, которые становятся возможны, когда вызовы LLM происходят внутри Agent с сохранением состояния. Настройку провайдера и примеры кода см. Использование ИИ-моделей.

Состояние как контекст

У каждого Agent есть встроенный База данных SQL и состояние в виде пар ключ-значение. Вместо передачи всей истории разговора от клиента при каждом запросе Agent сохраняет её самостоятельно и формирует промпты на основе собственного хранилища.

import { Agent } from "agents";

export class ResearchAgent extends Agent {
	async buildPrompt(userMessage) {
		const history = this.sql`
			SELECT role, content FROM messages
			ORDER BY timestamp DESC LIMIT 50`;

		const preferences = this.sql`
			SELECT key, value FROM user_preferences`;

		return [
			{ role: "system", content: this.systemPrompt(preferences) },
			...history.reverse(),
			{ role: "user", content: userMessage },
		];
	}
}
import { Agent } from "agents";

export class ResearchAgent extends Agent<Env> {
	async buildPrompt(userMessage: string) {
		const history = this.sql<{ role: string; content: string }>`
			SELECT role, content FROM messages
			ORDER BY timestamp DESC LIMIT 50`;

		const preferences = this.sql<{ key: string; value: string }>`
			SELECT key, value FROM user_preferences`;

		return [
			{ role: "system", content: this.systemPrompt(preferences) },
			...history.reverse(),
			{ role: "user", content: userMessage },
		];
	}
}

Это означает, что клиенту не нужно отправлять всю беседу с каждым сообщением. Agent владеет историей и может обрезать её, обогащать найденными документами или сокращать более ранние ходы перед отправкой модели.

Переживание разрывов соединения

Рассуждающим моделям вроде DeepSeek R1 или GLM-4 может требоваться от 30 секунд до нескольких минут на ответ. В архитектуре запрос-ответ без сохранения состояния клиенту приходится оставаться на связи всё это время. Если соединение обрывается, ответ теряется.

Agent продолжает работу после отключения клиента. Когда приходит ответ, Agent может сохранить его в состоянии и доставить, как только клиент снова подключится, даже если это произойдёт через часы или дни.

import { Agent } from "agents";
import { streamText } from "ai";
import { createWorkersAI } from "workers-ai-provider";

export class MyAgent extends Agent {
	async onMessage(connection, message) {
		const { prompt } = JSON.parse(message);
		const workersai = createWorkersAI({ binding: this.env.AI });

		const result = streamText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt,
		});

		for await (const chunk of result.textStream) {
			connection.send(JSON.stringify({ type: "chunk", content: chunk }));
		}

		this.sql`INSERT INTO responses (prompt, response, timestamp)
			VALUES (${prompt}, ${await result.text}, ${Date.now()})`;
	}
}
import { Agent } from "agents";
import { streamText } from "ai";
import { createWorkersAI } from "workers-ai-provider";

export class MyAgent extends Agent<Env> {
	async onMessage(connection: Connection, message: WSMessage) {
		const { prompt } = JSON.parse(message as string);
		const workersai = createWorkersAI({ binding: this.env.AI });

		const result = streamText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt,
		});

		for await (const chunk of result.textStream) {
			connection.send(JSON.stringify({ type: "chunk", content: chunk }));
		}

		this.sql`INSERT INTO responses (prompt, response, timestamp)
			VALUES (${prompt}, ${await result.text}, ${Date.now()})`;
	}
}

С AIChatAgent, это обрабатывается автоматически: сообщения сохраняются в SQLite, а потоки возобновляются при повторном подключении.

Автономные вызовы модели

Agents не нуждаются в запросе пользователя, чтобы вызвать модель. Вызовы модели можно запланировать в фоновом режиме, например для ночной суммаризации, периодической классификации, мониторинга или любой другой задачи, которая должна выполняться без участия человека.

import { Agent } from "agents";

export class DigestAgent extends Agent {
	async onStart() {
		this.schedule("0 8 * * *", "generateDailyDigest", {});
	}

	async generateDailyDigest() {
		const articles = this.sql`
			SELECT title, body FROM articles
			WHERE created_at > datetime('now', '-1 day')`;

		const workersai = createWorkersAI({ binding: this.env.AI });
		const { text } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt: `Summarize these articles:\n${articles.map((a) => a.title + ": " + a.body).join("\n\n")}`,
		});

		this.sql`INSERT INTO digests (summary, created_at)
			VALUES (${text}, ${Date.now()})`;

		this.broadcast(JSON.stringify({ type: "digest", summary: text }));
	}
}
import { Agent } from "agents";

export class DigestAgent extends Agent<Env> {
	async onStart() {
		this.schedule("0 8 * * *", "generateDailyDigest", {});
	}

	async generateDailyDigest() {
		const articles = this.sql<{ title: string; body: string }>`
			SELECT title, body FROM articles
			WHERE created_at > datetime('now', '-1 day')`;

		const workersai = createWorkersAI({ binding: this.env.AI });
		const { text } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt: `Summarize these articles:\n${articles.map((a) => a.title + ": " + a.body).join("\n\n")}`,
		});

		this.sql`INSERT INTO digests (summary, created_at)
			VALUES (${text}, ${Date.now()})`;

		this.broadcast(JSON.stringify({ type: "digest", summary: text }));
	}
}

Конвейеры с несколькими моделями

Поскольку Agent сохраняет состояние между вызовами, в одном методе можно выстроить цепочку из нескольких моделей: быстрой модели для классификации, модели с рассуждением для планирования и embedding-модели для поиска, не теряя контекст между шагами.

import { Agent } from "agents";
import { generateText, embed } from "ai";
import { createWorkersAI } from "workers-ai-provider";

export class TriageAgent extends Agent {
	async triage(ticket) {
		const workersai = createWorkersAI({ binding: this.env.AI });

		const { text: category } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt: `Classify this support ticket into one of: billing, technical, account. Ticket: ${ticket}`,
		});

		const { embedding } = await embed({
			model: workersai("@cf/baai/bge-base-en-v1.5"),
			value: ticket,
		});
		const similar = await this.env.VECTOR_DB.query(embedding, { topK: 5 });

		const { text: response } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt: `Draft a response for this ${category} ticket. Similar resolved tickets: ${JSON.stringify(similar)}. Ticket: ${ticket}`,
		});

		this.sql`INSERT INTO tickets (content, category, response, created_at)
			VALUES (${ticket}, ${category}, ${response}, ${Date.now()})`;

		return { category, response };
	}
}
import { Agent } from "agents";
import { generateText, embed } from "ai";
import { createWorkersAI } from "workers-ai-provider";

export class TriageAgent extends Agent<Env> {
	async triage(ticket: string) {
		const workersai = createWorkersAI({ binding: this.env.AI });

		const { text: category } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt: `Classify this support ticket into one of: billing, technical, account. Ticket: ${ticket}`,
		});

		const { embedding } = await embed({
			model: workersai("@cf/baai/bge-base-en-v1.5"),
			value: ticket,
		});
		const similar = await this.env.VECTOR_DB.query(embedding, { topK: 5 });

		const { text: response } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt: `Draft a response for this ${category} ticket. Similar resolved tickets: ${JSON.stringify(similar)}. Ticket: ${ticket}`,
		});

		this.sql`INSERT INTO tickets (content, category, response, created_at)
			VALUES (${ticket}, ${category}, ${response}, ${Date.now()})`;

		return { category, response };
	}
}

Каждый промежуточный результат остаётся в памяти Agent на всё время выполнения метода, а конечный результат сохраняется в SQL для последующего использования.

Кэширование и контроль затрат

Постоянное хранилище позволяет кешировать ответы модели и избегать повторных вызовов. Это особенно полезно для затратных операций, таких как эмбеддинги или длинные цепочки рассуждений.

import { Agent } from "agents";

export class CachingAgent extends Agent {
	async cachedGenerate(prompt) {
		const cached = this.sql`
			SELECT response FROM llm_cache WHERE prompt = ${prompt}`;

		if (cached.length > 0) {
			return cached[0].response;
		}

		const workersai = createWorkersAI({ binding: this.env.AI });
		const { text } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt,
		});

		this.sql`INSERT INTO llm_cache (prompt, response, created_at)
			VALUES (${prompt}, ${text}, ${Date.now()})`;

		return text;
	}
}
import { Agent } from "agents";

export class CachingAgent extends Agent<Env> {
	async cachedGenerate(prompt: string) {
		const cached = this.sql<{ response: string }>`
			SELECT response FROM llm_cache WHERE prompt = ${prompt}`;

		if (cached.length > 0) {
			return cached[0].response;
		}

		const workersai = createWorkersAI({ binding: this.env.AI });
		const { text } = await generateText({
			model: workersai("@cf/zai-org/glm-4.7-flash"),
			prompt,
		});

		this.sql`INSERT INTO llm_cache (prompt, response, created_at)
			VALUES (${prompt}, ${text}, ${Date.now()})`;

		return text;
	}
}

Для кэширования на уровне провайдера и управления лимитами запросов сразу для нескольких агентов используйте AI Gateway.

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

Использование ИИ-моделей

Настройка провайдера, потоковая передача и примеры кода для Workers AI, OpenAI, Anthropic и других.

Чат-агенты

AIChatAgent автоматически берёт на себя сохранение сообщений, возобновляемую потоковую передачу и работу с инструментами.

Хранение и синхронизация состояния

API базы данных SQL и состояния ключ-значение для построения контекста и кэширования.

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

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