← Создание агентов на Cloudflare / agents / runtime / execution
Запуск Workflows
Интеграция Cloudflare Workflows с Agents для устойчивой многошаговой фоновой обработки, пока Agents занимаются коммуникацией в реальном времени.
быстрый старт
1. Определите Workflow
Расширить AgentWorkflow для типизированного доступа к исходному Agent:
import { AgentWorkflow } from "agents/workflows";
export class ProcessingWorkflow extends AgentWorkflow {
async run(event, step) {
const params = event.payload;
const result = await step.do("process-data", async () => {
return processData(params.data);
});
// Non-durable: progress reporting (may repeat on retry)
await this.reportProgress({
step: "process",
status: "complete",
percent: 0.5,
});
// Broadcast to connected WebSocket clients
this.broadcastToClients({ type: "update", taskId: params.taskId });
await step.do("save-results", async () => {
// Call Agent methods via RPC
await this.agent.saveResult(params.taskId, result);
});
// Durable: idempotent, won't repeat on retry
await step.reportComplete(result);
return result;
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
import type { MyAgent } from "./agent";
type TaskParams = { taskId: string; data: string };
export class ProcessingWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
const params = event.payload;
const result = await step.do("process-data", async () => {
return processData(params.data);
});
// Non-durable: progress reporting (may repeat on retry)
await this.reportProgress({
step: "process",
status: "complete",
percent: 0.5,
});
// Broadcast to connected WebSocket clients
this.broadcastToClients({ type: "update", taskId: params.taskId });
await step.do("save-results", async () => {
// Call Agent methods via RPC
await this.agent.saveResult(params.taskId, result);
});
// Durable: idempotent, won't repeat on retry
await step.reportComplete(result);
return result;
}
}2. Запустите Workflow из Agent
Используйте runWorkflow() чтобы запускать workflows и отслеживать их:
import { Agent } from "agents";
export class MyAgent extends Agent {
async startTask(taskId, data) {
const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {
taskId,
data,
});
return { instanceId };
}
async onWorkflowProgress(workflowName, instanceId, progress) {
this.broadcast(JSON.stringify({ type: "workflow-progress", progress }));
}
async onWorkflowComplete(workflowName, instanceId, result) {
console.log(`Workflow completed:`, result);
}
async saveResult(taskId, result) {
this
.sql`INSERT INTO results (task_id, data) VALUES (${taskId}, ${JSON.stringify(result)})`;
}
}import { Agent } from "agents";
export class MyAgent extends Agent {
async startTask(taskId: string, data: string) {
const instanceId = await this.runWorkflow("PROCESSING_WORKFLOW", {
taskId,
data,
});
return { instanceId };
}
async onWorkflowProgress(
workflowName: string,
instanceId: string,
progress: unknown,
) {
this.broadcast(JSON.stringify({ type: "workflow-progress", progress }));
}
async onWorkflowComplete(
workflowName: string,
instanceId: string,
result?: unknown,
) {
console.log(`Workflow completed:`, result);
}
async saveResult(taskId: string, result: unknown) {
this
.sql`INSERT INTO results (task_id, data) VALUES (${taskId}, ${JSON.stringify(result)})`;
}
}3. Настройте Wrangler
{
"name": "my-app",
"main": "src/index.ts",
// Set this to today's date
"compatibility_date": "2026-08-28",
"durable_objects": {
"bindings": [{ "name": "MY_AGENT", "class_name": "MyAgent" }],
},
"workflows": [
{
"name": "processing-workflow",
"binding": "PROCESSING_WORKFLOW",
"class_name": "ProcessingWorkflow",
},
],
"migrations": [{ "tag": "v1", "new_sqlite_classes": ["MyAgent"] }],
}name = "my-app"
main = "src/index.ts"
# Set this to today's date
compatibility_date = "2026-08-28"
[[durable_objects.bindings]]
name = "MY_AGENT"
class_name = "MyAgent"
[[workflows]]
name = "processing-workflow"
binding = "PROCESSING_WORKFLOW"
class_name = "ProcessingWorkflow"
[[migrations]]
tag = "v1"
new_sqlite_classes = [ "MyAgent" ]Класс AgentWorkflow
Базовый класс для Workflows, которые интегрируются с Agents.
Параметры типа
| Параметр | Описание |
|---|---|
AgentType |
Тип класса Agent для типизированного RPC |
Params |
Параметры, передаваемые workflow |
ProgressType |
Тип для отчётов о ходе выполнения (по умолчанию DefaultProgress) |
Env |
Тип окружения (по умолчанию Cloudflare.Env) |
Свойства
| Свойство | Тип | Описание |
|---|---|---|
agent |
Заглушка | Типизированная заглушка для вызова методов Agent. Для workflow, запущенных из дочернего агента, это заглушка только для RPC обратно к исходной грани; для HTTP или WebSocket используйте маршрутизацию дочерних агентов fetch() трафик |
instanceId |
string | ID экземпляра workflow |
workflowName |
string | Имя биндинга workflow |
env |
Env | Привязки окружения |
Методы экземпляра (неустойчивые)
Эти методы могут выполняться повторно при повторной попытке. Используйте их для лёгких, частых обновлений.
reportProgress(progress)
Сообщите о прогрессе Agent. Запускает onWorkflowProgress колбэк.
await this.reportProgress({
step: "processing",
status: "running",
percent: 0.5,
});await this.reportProgress({
step: "processing",
status: "running",
percent: 0.5,
});broadcastToClients(message)
Рассылает сообщение всем клиентам WebSocket, подключённым к Agent.
this.broadcastToClients({ type: "update", data: result });this.broadcastToClients({ type: "update", data: result });waitForApproval(step, options?)
Дождитесь события подтверждения. Выбрасывает WorkflowRejectedError если отклонено.
const approval = await this.waitForApproval(step, {
timeout: "7 days",
});const approval = await this.waitForApproval<{ approvedBy: string }>(step, {
timeout: "7 days",
});Методы шагов (устойчивые)
Эти методы идемпотентны и не выполняются повторно при повторной попытке. Используйте их для изменений состояния, которые должны сохраняться.
| Метод | Описание |
|---|---|
step.reportComplete(result?) |
Сообщить об успешном завершении |
step.reportError(error) |
Сообщите об ошибке |
step.sendEvent(event) |
Отправить пользовательское событие Agent |
step.updateAgentState(state) |
Заменить состояние Agent (рассылается клиентам) |
step.mergeAgentState(partial) |
Слить с состоянием Agent (рассылается клиентам) |
step.resetAgentState() |
Сбросить состояние Agent до initialState |
Тип DefaultProgress
type DefaultProgress = {
step?: string;
status?: "pending" | "running" | "complete" | "error";
message?: string;
percent?: number;
[key: string]: unknown;
};Методы рабочего процесса Agent
Методы, доступные на Agent класс для управления Workflow.
runWorkflow(workflowName, params, options?)
Запустите экземпляр workflow и отслеживайте его в базе данных Agent.
Параметры:
| Параметр | Тип | Описание |
|---|---|---|
workflowName |
string | Имя привязки Workflow из env |
params |
объект | Параметры для передачи в workflow |
options.id |
string | Пользовательский ID workflow (создаётся автоматически, если не указан) |
options.metadata |
объект | Метаданные, хранимые для запросов (не передаются в workflow) |
options.agentBinding |
string | Имя привязки Agent (определяется автоматически, если не указано). При вызове из субагента это имя привязки корневого Agent |
Возвращает: Promise<string> - Идентификатор экземпляра Workflow
const instanceId = await this.runWorkflow(
"MY_WORKFLOW",
{ taskId: "123" },
{
metadata: { userId: "user-456", priority: "high" },
},
);const instanceId = await this.runWorkflow(
"MY_WORKFLOW",
{ taskId: "123" },
{
metadata: { userId: "user-456", priority: "high" },
},
);Запуск workflow из дочерних агентов
Дочерние агенты могут вызывать this.runWorkflow() напрямую. Workflow отслеживается в базе данных SQLite исходного дочернего агента, и this.agent внутри AgentWorkflow направляет обратно к тому же sub-agent для вызовов RPC, callbacks, обновлений состояния и broadcasts.
Родительские агенты не могут автоматически просматривать или управлять workflows, которые запускает дочерний агент. SubAgentStub<T> предоставляет только определённые пользователем методы, но не унаследованные Agent методы, такие как approveWorkflow() или getWorkflow(). Чтобы управлять workflow, запущенным дочерним объектом, из родительского, определите на дочернем объекте небольшие методы-обёртки и вызывайте их через стаб sub-agent.
export class ParentAgent extends Agent {
async startChildWorkflow(childName, task) {
const child = await this.subAgent(ChildAgent, childName);
return child.startWorkflow(task);
}
async approveChildWorkflow(childName, workflowId) {
const child = await this.subAgent(ChildAgent, childName);
return child.approveChildWorkflow(workflowId);
}
}
export class ChildAgent extends Agent {
async startWorkflow(task) {
return this.runWorkflow("CHILD_WORKFLOW", { task });
}
async approveChildWorkflow(workflowId) {
return this.approveWorkflow(workflowId);
}
async getChildWorkflow(workflowId) {
return this.getWorkflow(workflowId);
}
}export class ParentAgent extends Agent {
async startChildWorkflow(childName: string, task: string) {
const child = await this.subAgent(ChildAgent, childName);
return child.startWorkflow(task);
}
async approveChildWorkflow(childName: string, workflowId: string) {
const child = await this.subAgent(ChildAgent, childName);
return child.approveChildWorkflow(workflowId);
}
}
export class ChildAgent extends Agent {
async startWorkflow(task: string) {
return this.runWorkflow("CHILD_WORKFLOW", { task });
}
async approveChildWorkflow(workflowId: string) {
return this.approveWorkflow(workflowId);
}
async getChildWorkflow(workflowId: string) {
return this.getWorkflow(workflowId);
}
}Для источников от дочернего агента, AgentWorkflow.agent это заглушка, доступная только по RPC. Используйте её для вызова методов Agent, но используйте routeSubAgentRequest() или /agents/{parent}/{name}/sub/{child}/{name} структуру URL для внешней маршрутизации по HTTP или WebSocket вместо this.agent.fetch().
Ограничения маршрутизации
Поскольку исходная идентичность устойчиво сохраняется в параметрах workflow и воспроизводится при каждом callback, на все workflows, будь то дочерних агентов или агентов верхнего уровня, распространяется несколько ограничений:
- Колбэки разрешают Agent по имени. Среда выполнения повторно разрешает исходный Agent с помощью
getAgentByName(...). Если вы обратились к Agent по необработанному ID Durable Object (idFromString/get(id)) вместо имени, колбэки попадают на другой экземпляр. Запускайте workflow из Agent, адресованных по имени. - Имена классов должны сохраняться после бандлинга. Исходный путь индексируется по
constructor.name. Настройте бандлер так, чтобы он сохранял имена классов (esbuildkeepNames: true) поэтому прогресс, завершение иthis.agentRPC можно направить обратно к нужной грани. agentBindingэто корневая привязка. Когда вы передаётеoptions.agentBindingиз дочернего агента используйте корень Имя привязки Durable Object агента, а не дочерней привязки.
sendWorkflowEvent(workflowName, instanceId, event)
Отправьте событие выполняющемуся workflow.
await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});getWorkflowStatus(workflowName, instanceId)
Получить статус workflow и обновить запись отслеживания.
const status = await this.getWorkflowStatus("MY_WORKFLOW", instanceId);
// { status: 'running', output: null, error: null }const status = await this.getWorkflowStatus("MY_WORKFLOW", instanceId);
// { status: 'running', output: null, error: null }getWorkflow(instanceId)
Получить отслеживаемый workflow по ID.
const workflow = this.getWorkflow(instanceId);
// { instanceId, workflowName, status, metadata, error, createdAt, ... }const workflow = this.getWorkflow(instanceId);
// { instanceId, workflowName, status, metadata, error, createdAt, ... }getWorkflows(criteria?)
Запрашивайте отслеживаемые workflow с курсорной пагинацией. Возвращает WorkflowPage с workflows, общим количеством и курсором для следующей страницы.
// Get running workflows (default limit is 50, max is 100)
const { workflows, total } = this.getWorkflows({ status: "running" });
// Filter by metadata
const { workflows: userWorkflows } = this.getWorkflows({
metadata: { userId: "user-456" },
});
// Pagination with cursor
const page1 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
});
console.log(`Showing ${page1.workflows.length} of ${page1.total} workflows`);
// Get next page using cursor
if (page1.nextCursor) {
const page2 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
cursor: page1.nextCursor,
});
}// Get running workflows (default limit is 50, max is 100)
const { workflows, total } = this.getWorkflows({ status: "running" });
// Filter by metadata
const { workflows: userWorkflows } = this.getWorkflows({
metadata: { userId: "user-456" },
});
// Pagination with cursor
const page1 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
});
console.log(`Showing ${page1.workflows.length} of ${page1.total} workflows`);
// Get next page using cursor
if (page1.nextCursor) {
const page2 = this.getWorkflows({
status: ["complete", "errored"],
limit: 20,
orderBy: "desc",
cursor: page1.nextCursor,
});
} WorkflowPage тип:
type WorkflowPage = {
workflows: WorkflowInfo[];
total: number; // Total matching workflows
nextCursor: string | null; // null when no more pages
};deleteWorkflow(instanceId)
Удаляет одну запись отслеживания экземпляра workflow. Возвращает true если удалено, false если не найдено.
deleteWorkflows(criteria?)
Удаляет записи отслеживания экземпляров workflow, соответствующие критериям.
// Delete completed workflow instances older than 7 days
this.deleteWorkflows({
status: "complete",
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
// Delete all errored and terminated workflows
this.deleteWorkflows({
status: ["errored", "terminated"],
});// Delete completed workflow instances older than 7 days
this.deleteWorkflows({
status: "complete",
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
// Delete all errored and terminated workflows
this.deleteWorkflows({
status: ["errored", "terminated"],
});terminateWorkflow(instanceId)
Немедленно завершает выполняющийся workflow. Устанавливает статус "terminated".
await this.terminateWorkflow(instanceId);await this.terminateWorkflow(instanceId);pauseWorkflow(instanceId)
Приостановите выполняющийся workflow. Позже его можно возобновить с помощью resumeWorkflow().
await this.pauseWorkflow(instanceId);await this.pauseWorkflow(instanceId);resumeWorkflow(instanceId)
Возобновите приостановленный Workflow.
await this.resumeWorkflow(instanceId);await this.resumeWorkflow(instanceId);restartWorkflow(instanceId, options?)
Перезапустите экземпляр Workflow с начала с тем же ID.
// Reset tracking (default) - clears timestamps and error fields
await this.restartWorkflow(instanceId);
// Preserve original timestamps
await this.restartWorkflow(instanceId, { resetTracking: false });// Reset tracking (default) - clears timestamps and error fields
await this.restartWorkflow(instanceId);
// Preserve original timestamps
await this.restartWorkflow(instanceId, { resetTracking: false });approveWorkflow(instanceId, options?)
Подтвердить ожидающий workflow. Используйте с waitForApproval() в workflow.
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});rejectWorkflow(instanceId, options?)
Отклоните ожидающий workflow. Это вызывает waitForApproval() выбросить WorkflowRejectedError.
await this.rejectWorkflow(instanceId, { reason: "Request denied" });await this.rejectWorkflow(instanceId, { reason: "Request denied" });migrateWorkflowBinding(oldName, newName)
Мигрировать отслеживаемые workflow после переименования привязки workflow.
class MyAgent extends Agent {
async onStart() {
this.migrateWorkflowBinding("OLD_WORKFLOW", "NEW_WORKFLOW");
}
}class MyAgent extends Agent {
async onStart() {
this.migrateWorkflowBinding("OLD_WORKFLOW", "NEW_WORKFLOW");
}
}Колбэки жизненного цикла
Переопределите эти методы в своём Agent, чтобы обрабатывать события workflow:
| Callback | Параметры | Описание |
|---|---|---|
onWorkflowProgress |
workflowName, instanceId, progress |
Вызывается, когда workflow сообщает о прогрессе |
onWorkflowComplete |
workflowName, instanceId, result? |
Вызывается при завершении workflow |
onWorkflowError |
workflowName, instanceId, error |
Вызывается при ошибке workflow |
onWorkflowEvent |
workflowName, instanceId, event |
Вызывается, когда workflow отправляет событие |
onWorkflowCallback |
callback: WorkflowCallback |
Вызывается для всех типов колбэков |
class MyAgent extends Agent {
async onWorkflowProgress(workflowName, instanceId, progress) {
this.broadcast(
JSON.stringify({ type: "progress", workflowName, instanceId, progress }),
);
}
async onWorkflowComplete(workflowName, instanceId, result) {
console.log(`${workflowName}/${instanceId} completed`);
}
async onWorkflowError(workflowName, instanceId, error) {
console.error(`${workflowName}/${instanceId} failed:`, error);
}
}class MyAgent extends Agent {
async onWorkflowProgress(
workflowName: string,
instanceId: string,
progress: unknown,
) {
this.broadcast(
JSON.stringify({ type: "progress", workflowName, instanceId, progress }),
);
}
async onWorkflowComplete(
workflowName: string,
instanceId: string,
result?: unknown,
) {
console.log(`${workflowName}/${instanceId} completed`);
}
async onWorkflowError(
workflowName: string,
instanceId: string,
error: string,
) {
console.error(`${workflowName}/${instanceId} failed:`, error);
}
}Отслеживание Workflow
Workflows, запущенные с помощью runWorkflow() автоматически отслеживаются во внутренней базе данных исходного Agent. Вы можете запрашивать, фильтровать и управлять workflow с помощью описанных выше методов (getWorkflow(), getWorkflows(), deleteWorkflow(), и т. д.).
Значения статуса
| Статус | Описание |
|---|---|
queued |
Ожидание запуска |
running |
Выполняется в данный момент |
paused |
Приостановлено пользователем |
waiting |
Ожидание события |
complete |
Успешно завершено |
errored |
Завершилось с ошибкой |
terminated |
Завершено вручную |
Используйте metadata параметр в runWorkflow() чтобы хранить информацию, доступную для запросов (например, ID пользователей или типы задач), по которой позже можно фильтровать с помощью getWorkflows().
Примеры
Подтверждение с участием человека
import { AgentWorkflow } from "agents/workflows";
export class ApprovalWorkflow extends AgentWorkflow {
async run(event, step) {
const request = await step.do("prepare", async () => {
return { ...event.payload, preparedAt: Date.now() };
});
await this.reportProgress({
step: "approval",
status: "pending",
message: "Awaiting approval",
});
// Throws WorkflowRejectedError if rejected
const approval = await this.waitForApproval(step, {
timeout: "7 days",
});
console.log("Approved by:", approval?.approvedBy);
const result = await step.do("execute", async () => {
return executeRequest(request);
});
await step.reportComplete(result);
return result;
}
}
class MyAgent extends Agent {
async handleApproval(instanceId, userId) {
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
}
async handleRejection(instanceId, reason) {
await this.rejectWorkflow(instanceId, { reason });
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
export class ApprovalWorkflow extends AgentWorkflow<MyAgent, RequestParams> {
async run(event: AgentWorkflowEvent<RequestParams>, step: AgentWorkflowStep) {
const request = await step.do("prepare", async () => {
return { ...event.payload, preparedAt: Date.now() };
});
await this.reportProgress({
step: "approval",
status: "pending",
message: "Awaiting approval",
});
// Throws WorkflowRejectedError if rejected
const approval = await this.waitForApproval<{ approvedBy: string }>(step, {
timeout: "7 days",
});
console.log("Approved by:", approval?.approvedBy);
const result = await step.do("execute", async () => {
return executeRequest(request);
});
await step.reportComplete(result);
return result;
}
}
class MyAgent extends Agent {
async handleApproval(instanceId: string, userId: string) {
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
}
async handleRejection(instanceId: string, reason: string) {
await this.rejectWorkflow(instanceId, { reason });
}
}Повтор с задержкой
import { AgentWorkflow } from "agents/workflows";
export class ResilientWorkflow extends AgentWorkflow {
async run(event, step) {
const result = await step.do(
"call-api",
{
retries: { limit: 5, delay: "10 seconds", backoff: "exponential" },
timeout: "5 minutes",
},
async () => {
const response = await fetch("https://api.example.com/process", {
method: "POST",
body: JSON.stringify(event.payload),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
);
await step.reportComplete(result);
return result;
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
export class ResilientWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
const result = await step.do(
"call-api",
{
retries: { limit: 5, delay: "10 seconds", backoff: "exponential" },
timeout: "5 minutes",
},
async () => {
const response = await fetch("https://api.example.com/process", {
method: "POST",
body: JSON.stringify(event.payload),
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
);
await step.reportComplete(result);
return result;
}
}Синхронизация состояния
Workflows могут надёжно обновлять состояние Agent через step, которая автоматически рассылается всем подключённым клиентам:
import { AgentWorkflow } from "agents/workflows";
export class StatefulWorkflow extends AgentWorkflow {
async run(event, step) {
// Replace entire state (durable, broadcasts to clients)
await step.updateAgentState({
currentTask: {
id: event.payload.taskId,
status: "processing",
startedAt: Date.now(),
},
});
const result = await step.do("process", async () =>
processTask(event.payload),
);
// Merge partial state (durable, keeps existing fields)
await step.mergeAgentState({
currentTask: { status: "complete", result, completedAt: Date.now() },
});
await step.reportComplete(result);
return result;
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
export class StatefulWorkflow extends AgentWorkflow<MyAgent, TaskParams> {
async run(event: AgentWorkflowEvent<TaskParams>, step: AgentWorkflowStep) {
// Replace entire state (durable, broadcasts to clients)
await step.updateAgentState({
currentTask: {
id: event.payload.taskId,
status: "processing",
startedAt: Date.now(),
},
});
const result = await step.do("process", async () =>
processTask(event.payload),
);
// Merge partial state (durable, keeps existing fields)
await step.mergeAgentState({
currentTask: { status: "complete", result, completedAt: Date.now() },
});
await step.reportComplete(result);
return result;
}
}Пользовательские типы прогресса
Определите пользовательские типы прогресса для отчётности, специфичной для предметной области:
import { AgentWorkflow } from "agents/workflows";
// Custom progress type for data pipeline
// Workflow with custom progress type (3rd type parameter)
export class ETLWorkflow extends AgentWorkflow {
async run(event, step) {
await this.reportProgress({
stage: "extract",
recordsProcessed: 0,
totalRecords: 1000,
currentTable: "users",
});
// ... processing
}
}
// Agent receives typed progress
class MyAgent extends Agent {
async onWorkflowProgress(workflowName, instanceId, progress) {
const p = progress;
console.log(`Stage: ${p.stage}, ${p.recordsProcessed}/${p.totalRecords}`);
}
}import { AgentWorkflow } from "agents/workflows";
import type { AgentWorkflowEvent, AgentWorkflowStep } from "agents/workflows";
// Custom progress type for data pipeline
type PipelineProgress = {
stage: "extract" | "transform" | "load";
recordsProcessed: number;
totalRecords: number;
currentTable?: string;
};
// Workflow with custom progress type (3rd type parameter)
export class ETLWorkflow extends AgentWorkflow<
MyAgent,
ETLParams,
PipelineProgress
> {
async run(event: AgentWorkflowEvent<ETLParams>, step: AgentWorkflowStep) {
await this.reportProgress({
stage: "extract",
recordsProcessed: 0,
totalRecords: 1000,
currentTable: "users",
});
// ... processing
}
}
// Agent receives typed progress
class MyAgent extends Agent {
async onWorkflowProgress(
workflowName: string,
instanceId: string,
progress: unknown,
) {
const p = progress as PipelineProgress;
console.log(`Stage: ${p.stage}, ${p.recordsProcessed}/${p.totalRecords}`);
}
}Стратегия очистки
Внутренний cf_agents_workflows таблица может расти неограниченно, поэтому реализуйте политику хранения:
class MyAgent extends Agent {
// Option 1: Delete on completion
async onWorkflowComplete(workflowName, instanceId, result) {
// Process result first, then delete
this.deleteWorkflow(instanceId);
}
// Option 2: Scheduled cleanup (keep recent history)
async cleanupOldWorkflows() {
this.deleteWorkflows({
status: ["complete", "errored"],
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
}
// Option 3: Keep all history for compliance/auditing
// Don't call deleteWorkflows() - query historical data as needed
}class MyAgent extends Agent {
// Option 1: Delete on completion
async onWorkflowComplete(
workflowName: string,
instanceId: string,
result?: unknown,
) {
// Process result first, then delete
this.deleteWorkflow(instanceId);
}
// Option 2: Scheduled cleanup (keep recent history)
async cleanupOldWorkflows() {
this.deleteWorkflows({
status: ["complete", "errored"],
createdBefore: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
});
}
// Option 3: Keep all history for compliance/auditing
// Don't call deleteWorkflows() - query historical data as needed
}Двунаправленная связь
Workflow к агенту
// Direct RPC call (typed)
await this.agent.updateTaskStatus(taskId, "processing");
const data = await this.agent.getData(taskId);
// Non-durable callbacks (may repeat on retry, use for frequent updates)
await this.reportProgress({ step: "process", percent: 0.5 });
this.broadcastToClients({ type: "update", data });
// Durable callbacks via step (idempotent, won't repeat on retry)
await step.reportComplete(result);
await step.reportError("Something went wrong");
await step.sendEvent({ type: "custom", data: {} });
// Durable state synchronization via step (broadcasts to clients)
await step.updateAgentState({ status: "processing" });
await step.mergeAgentState({ progress: 0.5 });// Direct RPC call (typed)
await this.agent.updateTaskStatus(taskId, "processing");
const data = await this.agent.getData(taskId);
// Non-durable callbacks (may repeat on retry, use for frequent updates)
await this.reportProgress({ step: "process", percent: 0.5 });
this.broadcastToClients({ type: "update", data });
// Durable callbacks via step (idempotent, won't repeat on retry)
await step.reportComplete(result);
await step.reportError("Something went wrong");
await step.sendEvent({ type: "custom", data: {} });
// Durable state synchronization via step (broadcasts to clients)
await step.updateAgentState({ status: "processing" });
await step.mergeAgentState({ progress: 0.5 });От Agent к Workflow
// Send event to waiting workflow
await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});
// Approve/reject workflows using convenience methods
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
await this.rejectWorkflow(instanceId, { reason: "Request denied" });// Send event to waiting workflow
await this.sendWorkflowEvent("MY_WORKFLOW", instanceId, {
type: "custom-event",
payload: { action: "proceed" },
});
// Approve/reject workflows using convenience methods
await this.approveWorkflow(instanceId, {
reason: "Approved by admin",
metadata: { approvedBy: userId },
});
await this.rejectWorkflow(instanceId, { reason: "Request denied" });Рекомендации
- Делайте workflows сфокусированными : один workflow на логическую задачу
- Используйте осмысленные имена шагов : помогает с отладкой и наблюдаемостью
- Регулярно сообщайте о прогрессе : держит пользователей в курсе
- Корректная обработка ошибок : используйте
reportError()перед тем как выбросить исключение - Очистите завершённые Workflows : реализуйте политику хранения для таблицы отслеживания
- Обрабатывайте переименования привязок workflow : используйте
migrateWorkflowBinding()при переименовании привязок workflow вwrangler.jsonc
Ограничения
| Ограничение | Лимит |
|---|---|
| Максимальное количество шагов | 10,000 на workflow (по умолчанию) / настраивается до 25,000 |
| Размер состояния | 10 MB на workflow |
| Время ожидания события | максимум 1 год |
| Время выполнения шага | 30 минут на шаг |
Workflows не могут открывать WebSocket-соединения напрямую. Используйте broadcastToClients() чтобы взаимодействовать с подключёнными клиентами через Agent.