Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

Workflows の実行

最終更新 Markdown で表示Agent セットアップ

Cloudflare Workflows を Agents と統合すると、耐久性のある複数ステップのバックグラウンド処理を Workflows が担い、リアルタイム通信は Agents が担います。

クイックスタート

1. Workflow を定義する

元の Agent へ型付きでアクセスするには AgentWorkflow を継承します。

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. Agent から Workflow を開始する

runWorkflow() でワークフローを開始し、追跡します。

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-09-20",
	"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-09-20"

[[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 クラス

Agents と統合する Workflows の基底クラスです。

型パラメーター

パラメーター 説明
AgentType 型付き RPC 用の Agent クラス型
Params ワークフローに渡すパラメーター
ProgressType 進捗報告の型(デフォルトは DefaultProgress
Env 環境の型(デフォルトは Cloudflare.Env

プロパティ

プロパティ 説明
agent Stub Agent メソッドを呼ぶための型付きスタブ。サブエージェントから開始したワークフローでは、元のファセットへ戻る RPC 専用スタブです。HTTP または WebSocket の fetch() トラフィックにはサブエージェントのルーティングを使います
instanceId string ワークフローインスタンス ID
workflowName string ワークフローのバインディング名
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)

Agent に接続しているすべての WebSocket クライアントへメッセージをブロードキャストします。

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 のワークフローメソッド

Workflow 管理のために Agent クラスで使えるメソッドです。

runWorkflow(workflowName, params, options?)

ワークフローインスタンスを開始し、Agent のデータベースで追跡します。

パラメーター:

パラメーター 説明
workflowName string env のワークフローバインディング名
params object ワークフローへ渡すパラメーター
options.id string カスタムワークフロー ID(未指定時は自動生成)
options.metadata object クエリ用に保存するメタデータ(ワークフローには渡さない)
options.agentBinding string Agent のバインディング名(未指定時は自動検出)。サブエージェントから呼ぶ場合はルート Agent のバインディング名

戻り値: Promise<string> — ワークフローインスタンス ID

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

サブエージェントからワークフローを開始する

サブエージェントは this.runWorkflow() を直接呼べます。ワークフローは、開始元サブエージェントの SQLite データベースで追跡されます。AgentWorkflow 内の this.agent は、RPC 呼び出し、コールバック、状態更新、ブロードキャストのために同じサブエージェントへ戻ります。

親エージェントは、サブエージェントが開始したワークフローを自動では一覧したり制御したりしません。SubAgentStub<T> が公開するのはユーザー定義メソッドだけです。approveWorkflow()getWorkflow() のような継承された 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 メソッドの呼び出しには使えます。外部の HTTP または WebSocket ルーティングには this.agent.fetch() ではなく、routeSubAgentRequest() または /agents/{parent}/{name}/sub/{child}/{name} の URL 形を使います。

ルーティングの制約

開始元の識別情報はワークフローのパラメーターに耐久的に保存され、コールバックのたびに再生されます。そのため、サブエージェント起点でもトップレベルでも、すべてのワークフローに次の制約が適用されます。

  • コールバックは名前で Agent を解決します。 ランタイムは getAgentByName(...) で開始元 Agent を再解決します。名前ではなく生の Durable Object ID(idFromString / get(id))で Agent を指定した場合、コールバックは別インスタンスに届きます。ワークフローは名前でアドレスされる Agent から開始してください。
  • クラス名はバンドル後も残る必要があります。 開始元パスは constructor.name でキー付けされます。進捗、完了、this.agent の RPC が正しいファセットへ戻れるよう、バンドラーでクラス名を保持してください(esbuild の keepNames: true)。
  • agentBinding はルートのバインディングです。 サブエージェントから options.agentBinding を渡すときは、子のバインディングではなく ルート Agent の Durable Object バインディング名を使います。

sendWorkflowEvent(workflowName, instanceId, event)

実行中のワークフローへイベントを送ります。

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)

ワークフローのステータスを取得し、追跡レコードを更新します。

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)

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?)

カーソルベースのページネーションで追跡中のワークフローをクエリします。ワークフロー、総数、次ページ用カーソルを含む WorkflowPage を返します。

// 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)

ワークフローインスタンスの追跡レコードを 1 件削除します。削除できた場合は true、見つからない場合は false を返します。

deleteWorkflows(criteria?)

条件に一致するワークフローインスタンスの追跡レコードを削除します。

// 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)

実行中のワークフローをただちに終了します。ステータスは "terminated" になります。

await this.terminateWorkflow(instanceId);
await this.terminateWorkflow(instanceId);

pauseWorkflow(instanceId)

実行中のワークフローを一時停止します。あとで resumeWorkflow() で再開できます。

await this.pauseWorkflow(instanceId);
await this.pauseWorkflow(instanceId);

resumeWorkflow(instanceId)

一時停止したワークフローを再開します。

await this.resumeWorkflow(instanceId);
await this.resumeWorkflow(instanceId);

restartWorkflow(instanceId, options?)

同じ 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?)

待機中のワークフローを承認します。ワークフロー側の waitForApproval() と組み合わせます。

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?)

待機中のワークフローを拒否します。waitForApproval()WorkflowRejectedError を投げます。

await this.rejectWorkflow(instanceId, { reason: "Request denied" });
await this.rejectWorkflow(instanceId, { reason: "Request denied" });

migrateWorkflowBinding(oldName, newName)

ワークフローバインディングの名前を変えたあと、追跡中のワークフローを移行します。

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 でこれらのメソッドをオーバーライドします。

コールバック パラメーター 説明
onWorkflowProgress workflowName, instanceId, progress ワークフローが進捗を報告したときに呼ばれる
onWorkflowComplete workflowName, instanceId, result? ワークフローが完了したときに呼ばれる
onWorkflowError workflowName, instanceId, error ワークフローがエラーになったときに呼ばれる
onWorkflowEvent workflowName, instanceId, event ワークフローがイベントを送ったときに呼ばれる
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);
	}
}

ワークフローの追跡

runWorkflow() で開始したワークフローは、開始元 Agent の内部データベースで自動追跡されます。上記のメソッド(getWorkflow()getWorkflows()deleteWorkflow() など)でクエリ、フィルタ、管理できます。

ステータス値

ステータス 説明
queued 開始待ち
running 実行中
paused ユーザーが一時停止
waiting イベント待ち
complete 正常終了
errored エラーで失敗
terminated 手動で終了

runWorkflow()metadata オプションで、あとから getWorkflows() でフィルタできる情報(ユーザー ID やタスク種別など)を保存します。

人手承認(human-in-the-loop)

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

状態の同期

ワークフローは step 経由で Agent の状態を耐久的に更新でき、接続中の全クライアントへ自動でブロードキャストされます。

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 から Agent

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

ベストプラクティス

  1. ワークフローは焦点を絞る — 論理的なタスクごとに 1 ワークフロー
  2. 意味のあるステップ名を使う — デバッグと可観測性に役立つ
  3. 進捗を定期的に報告する — ユーザーに状況が伝わる
  4. エラーは丁寧に扱う — 投げる前に reportError() を使う
  5. 完了したワークフローを掃除する — 追跡テーブルに保持ポリシーを実装する
  6. ワークフローバインディングの改名を扱うwrangler.jsonc でバインディング名を変えたら migrateWorkflowBinding() を使う

制限

制約 上限
最大ステップ数 ワークフローあたり 10,000(デフォルト)/ 最大 25,000 まで設定可能
状態サイズ ワークフローあたり 10 MB
イベント待ち時間 最大 1 年
ステップ実行時間 ステップあたり 30 分

Workflows は WebSocket 接続を直接開けません。接続中のクライアントとの通信には、Agent 経由の broadcastToClients() を使います。

関連リソース

役に立ちましたか?