temporal について
Temporal.io で耐障害性のある分散システムを構築する開発者向けのリファレンススキルです。Python と TypeScript の SDK、ワークフローパターンに加え、AI エージェントや LLM 呼び出し、人間による承認のオーケストレーションも扱います。
主な機能
- 01Temporal の概念と決定性の解説
- 02Python と TypeScript SDK の説明
- 03タイムアウト、リトライ、ワークフローのバージョン管理
- 04ワークフローとアクティビティのパターン
- 05AI エージェントと LLM のオーケストレーション
活用シーン
- 01障害から復旧できる分散プロセスの構築
- 02リトライ付き API または LLM 呼び出しの実装
- 03並列 AI タスクと人間による承認の調整
MCP 対応
Model Context Protocol と、各ツールを Workflow で支える Durable MCP パターンに触れています。MCP サーバーやクライアントの実装については説明していません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ thatjuan/ agent-skills --skill temporalインストール後、Claude Code で/temporalを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
README.mdreferences/ai-patterns.mdcore-concepts.mdpython-sdk.mdtypescript-sdk.md詳細
Temporal ワークフローオーケストレーション
Temporal.io を使って、耐久性と信頼性に優れた分散システムを構築するためのエキスパートレベルの知識です。Python SDK(temporalio)、TypeScript SDK(@temporalio/*)、プラットフォームの中核概念、AI/LLM オーケストレーションのパターンを扱います。
リファレンス
| ファイル | 読むタイミング |
|---|---|
| core-concepts.md | アーキテクチャ、決定性、タイムアウト、再試行ポリシー、高度な機能 |
| python-sdk.md | Python によるワークフロー/アクティビティの実装、デコレーター、テスト、クライアント/ワーカーのセットアップ |
| typescript-sdk.md | TypeScript によるワークフロー/アクティビティの実装、サンドボックス化、バンドル、テスト |
| ai-patterns.md | AI エージェントのオーケストレーション、LLM パイプライン、ヒューマン・イン・ザ・ループ、フレームワークとの統合 |
プラットフォームの概要
Temporal は、イベントソーシングによるリプレイを通じて、プロセスのクラッシュ、インフラストラクチャの障害、デプロイ後もアプリケーションの状態を維持する耐久性のある実行を提供します。プラットフォームは Temporal Service(サーバー+永続ストレージ)とアプリケーションコード(ワーカー+クライアント)で構成されます。ワーカーはユーザーのインフラストラクチャ上でワークフローとアクティビティのコードを実行し、Temporal Service はタスクのディスパッチ、状態の永続化、タイマー管理を調整します。
主なプリミティブ
| プリミティブ | 役割 |
|---|---|
| ワークフロー | 決定論的なオーケストレーション関数。イベント履歴を通じて状態を永続化します |
| アクティビティ | 非決定論的な作業単位(I/O、API 呼び出し、副作用) |
| ワーカー | タスクキューをポーリングし、ワークフローとアクティビティを実行するプロセス |
| タスクキュー | サービスからワーカーへ作業を振り分ける名前付きキュー |
| シグナル | 実行中のワークフローに送る、非同期の応答を待たないメッセージ |
| クエリ | 状態を同期的に読み取り専用で検査します(イベント履歴は変更しません) |
| アップデート | 戻り値と検証を伴う、同期的な状態変更 |
| 子ワークフロー | 別のワークフローが起動するワークフロー。独立したイベント履歴を持ちます |
| スケジュール | サーバーが管理する、ワークフローの定期実行 |
決定性の契約
ワークフローコードは、復旧時にイベント履歴からリプレイされます。同じイベントが与えられた場合、リプレイのたびに同じ一連のコマンドが生成されます。I/O、乱数、システム時刻、スレッド処理などの非決定論的な操作は、アクティビティ内で行います。
| ワークフローで安全に使えるもの | 代わりに使用するもの |
|---|---|
workflow.now()/Date.now()(サンドボックスで置き換えられます) | datetime.now()/ネイティブの Date.now() |
workflow.random()/Math.random()(サンドボックスで置き換えられます) | random.random()/ネイティブの Math.random() |
workflow.uuid4()/@temporalio/workflow の uuid4() | uuid.uuid4()/crypto.randomUUID() |
workflow.sleep()/@temporalio/workflow の sleep() | asyncio.sleep()/setTimeout() |
| アクティビティの実行 | ネットワーク/ファイルへの直接 I/O |
タイムアウトモデル
| タイムアウト | 範囲 | 適用対象 |
|---|---|---|
| ワークフロー実行タイムアウト | 実行の連鎖全体(Continue-As-New の実行を含む) | ワークフロー |
| ワークフロー実行回タイムアウト | 連鎖内の単一の実行回 | ワークフロー |
| ワークフロータスクタイムアウト | SDK による単一の処理ステップ(デフォルト:10s) | ワークフロー |
| 開始から完了までのタイムアウト | アクティビティの単一の試行 | アクティビティ |
| スケジュールから完了までのタイムアウト | 再試行を含むアクティビティ実行全体 | アクティビティ |
| スケジュールから開始までのタイムアウト | ワーカーが取得するまでキューで待機する時間 | アクティビティ |
| ハートビートタイムアウト | 長時間実行されるアクティビティからのハートビート間隔 | アクティビティ |
開始から完了までのタイムアウトは、ワーカーの障害を検出する主要な手段であり、アクティビティに設定すべき最も重要なタイムアウトです。
再試行ポリシーのデフォルト値
| プロパティ | デフォルト値 |
|---|---|
| 初期間隔 | 1 second |
| バックオフ係数 | 2.0 |
| 最大間隔 | 初期間隔の 100 倍 |
| 最大試行回数 | 無制限 |
| 再試行不可のエラー種別 | なし |
アクティビティはデフォルトで再試行されます。ワークフローはデフォルトでは再試行されません。
SDK クイックリファレンス
Python
パッケージ: temporalio(pip install temporalio)
from temporalio import workflow, activity
from temporalio.client import Client
from temporalio.worker import Worker
@activity.defn
async def greet(name: str) -> str:
return f"Hello, {name}!"
@workflow.defn
class GreetingWorkflow:
@workflow.run
async def run(self, name: str) -> str:
return await workflow.execute_activity(
greet, name,
start_to_close_timeout=timedelta(seconds=10),
)
# Client
client = await Client.connect("localhost:7233")
result = await client.execute_workflow(
GreetingWorkflow.run, "World",
id="greeting-1", task_queue="my-queue",
)
# Worker
worker = Worker(client, task_queue="my-queue",
workflows=[GreetingWorkflow], activities=[greet])
await worker.run()
Python デコレーター: @workflow.defn、@workflow.run、@workflow.init、@workflow.signal、@workflow.query、@workflow.update、@activity.defn
サンドボックス: ワークフローコードは分離されたサンドボックス内で実行されます。アクティビティの型やデータクラスをインポートする場合は、with workflow.unsafe.imports_passed_through(): を使用してください。
TypeScript
パッケージ: @temporalio/client、@temporalio/worker、@temporalio/workflow、@temporalio/activity、@temporalio/common
// activities.ts
export async function greet(name: string): Promise<string> {
return `Hello, ${name}!`;
}
// workflows.ts
import { proxyActivities } from '@temporalio/workflow';
import type * as activities from './activities';
const { greet } = proxyActivities<typeof activities>({
startToCloseTimeout: '10s',
});
export async function greetingWorkflow(name: string): Promise<string> {
return await greet(name);
}
// client.ts
const handle = await client.workflow.start(greetingWorkflow, {
workflowId: 'greeting-1', taskQueue: 'my-queue', args: ['World'],
});
// worker.ts
const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
activities, taskQueue: 'my-queue',
});
await worker.run();
主なパターン: ワークフローでは、アクティビティから 型 のみをインポートします(import type * as activities)。実際の実装は Worker に登録します。アクティビティには proxyActivities<typeof activities>() を介してアクセスします。
サンドボックス: ワークフローコードは Webpack でバンドルされ、V8 分離環境で実行されます。Math.random()、Date.now()、setTimeout は決定論的なバージョンに置き換えられます。Node.js の組み込み機能(fs、path、crypto、http)はワークフローコードでは使用できません。
パターン選択ガイド
| シナリオ | パターン | 詳細 |
|---|---|---|
| 外部 API 呼び出し、DB クエリ、ファイル I/O | アクティビティ | 自動リトライに対応した非決定的な処理 |
| 複数のステップを状態とともに調整する | ワークフロー | 永続的な状態を用いた決定論的な連携 |
| サービス/チーム間で作業を分割する | 子ワークフロー | 独立したイベント履歴とワーカー |
| イベント履歴が 50K イベントに近づいている | Continue-As-New | ワークフロー ID を維持したまま新しい履歴を開始 |
| 定期的な実行 | スケジュール | サーバー管理、一時停止可能、重複実行ポリシー |
| 外部入力を待つ | シグナル + wait_condition/condition | 永続的で、待機中の計算コストがゼロ |
| ワークフローの状態を外部から読み取る | クエリ | イベント履歴のコストなし |
| 応答を返しながら状態を変更する | 更新 | 検証済み、同期的、追跡可能 |
| ロールバックを伴う複数ステップの処理 | Saga(補償処理リスト) | 失敗時に補償処理を逆順で実行 |
| 進捗を伴う長時間実行 | ハートビート付きアクティビティ | 障害検出と再開可能な進捗 |
| 外部での完了(人間による承認) | 非同期アクティビティ完了 | タスクトークンを使った帯域外での完了 |
| AI エージェントループ | ワークフロー + LLM アクティビティ | 永続的なエージェント状態と、リトライされる LLM 呼び出し |
| 並列 AI 処理 | ファンアウトする子ワークフロー/アクティビティ | 集約を伴う並行実行 |
| 人間参加型 AI | シグナル/更新ハンドラー | 永続的な承認ゲート |
AI オーケストレーションの概要
Temporal は、本番環境向けの AI エージェントシステムを支えるインフラストラクチャです。基本パターンでは、ワークフロー が永続的なエージェント状態と会話履歴を管理し、アクティビティ が非決定的な LLM 呼び出し、ツールの呼び出し、外部 API リクエストを処理します。
AI ワークロードにおける主な特性:
- 完了したアクティビティの結果はイベントソーシングによって永続化されます。障害からの復旧時には、LLM に再問い合わせるのではなく、記録された応答を再生します(コストを削減し、決定性を維持します)
- リトライポリシーがレート制限(429)、一時的な障害、API タイムアウトを自動的に処理します
- シグナルにより、計算コストをかけずに待機できる、人間参加型の承認ゲートを実現します
- タスクキューが推論処理を GPU 搭載ワーカーに振り分けます
- ワークフロー状態として保存された会話履歴は、自動的に永続化されます
フレームワーク連携
| フレームワーク | 連携の種類 |
|---|---|
| OpenAI Agents SDK | カスタムランナー、activity_as_tool ヘルパー |
| Pydantic AI | TemporalAgent ラッパー、I/O を自動的にアクティビティへオフロード |
| Vercel AI SDK | Worker 用の AiSDKPlugin、generateText() をアクティビティでラップ |
| Anthropic Claude API | アクティビティ内で直接 AsyncAnthropic クライアントを使用、max_retries=0(リトライは Temporal が処理) |
| LangChain | アクティビティでラップしたチェーン実行 |
| Model Context Protocol | 永続的な MCP:各ツールをワークフローで実装 |
AI 固有の設定
LLM クライアントライブラリには、Temporal のリトライ管理と競合する組み込みのリトライロジックがあります。クライアントレベルのリトライを無効にし、Temporal に処理させてください。
# Python - Anthropic
client = AsyncAnthropic(max_retries=0)
# Python - OpenAI
client = AsyncOpenAI(max_retries=0)
// TypeScript - OpenAI
const client = new OpenAI({ maxRetries: 0 });
LLM アクティビティに推奨されるリトライポリシー:
RetryPolicy(
initial_interval=timedelta(seconds=1),
backoff_coefficient=2.0,
maximum_interval=timedelta(seconds=120),
maximum_attempts=50,
non_retryable_error_types=["InvalidInput", "AuthenticationError"],
)
429 レスポンスの Retry-After ヘッダーは、ApplicationError / ApplicationFailure の next_retry_delay を使用して解析します。
開発環境
# Install Temporal CLI
brew install temporal
# Start local dev server (gRPC :7233, Web UI :8233)
temporal server start-dev
# Persistent storage between restarts
temporal server start-dev --db-filename temporal.db
# Common CLI commands
temporal workflow start --task-queue my-queue --type MyWorkflow --input '{"key":"val"}'
temporal workflow list
temporal workflow describe --workflow-id my-id
temporal workflow signal --workflow-id my-id --name my-signal --input '"data"'
temporal workflow query --workflow-id my-id --name my-query
temporal workflow cancel --workflow-id my-id
バージョン管理戦略
どちらの SDK も、既存の実行が進行中の間にワークフローコードを変更するための patched() / deprecate_patch() の仕組みを備えています。
- パッチを追加:
patched("change-id")で分岐します。新しい実行では新しいパスを通り、リプレイ中の実行では古いパスを通ります - パッチを非推奨化:すべての古い実行が完了したら、
deprecate_patch("change-id")を呼び出し、古いコードパスを削除します - パッチを削除:保持期間が過ぎたら、非推奨化の呼び出しを完全に削除します