この記事でわかること: Trigger.devを使うと、AIエージェントの長時間タスク・失敗リトライ・冪等性管理をコード数行で解決できます。
- Durable Executionの設計思想と、サーバーレス環境でのタイムアウト問題を根本から解決する仕組み
- v4(2026年GA)で追加されたWaitpoints・Run Engineの実装パターン
- LLM APIを呼び出すタスクのDurable化コードサンプル(TypeScript・Python)
対象読者: AIエージェント・自動化ワークフローを本番運用したい開発者・PM
今日やること: npx trigger.dev@latest init でプロジェクトを初期化し、最初のタスクを5分でデプロイする
「AIエージェントのタスクが途中でタイムアウトした」「OpenAI APIがレート制限で落ちたのに、最初からやり直しになった」——こんな経験はないでしょうか。
VercelやAWS Lambdaのサーバーレス環境では、実行時間に厳しい上限があります。AIエージェントが得意とする「スクレイピング→LLM処理→データ保存」のような多段タスクを信頼性高く動かすには、既存の関数実行基盤では限界があるのが現実です。
この記事では、その問題を解決するOSSプラットフォームTrigger.devを解説します。Durable Executionの設計思想から、2026年にGAになったv4の新機能、LLM APIを呼び出すタスクの実装パターンまで、コピペ可能なコードで紹介していきます。
Trigger.devとは何か
Trigger.devは、バックグラウンドジョブ・AIエージェント・自動化ワークフローをデプロイするためのOSS Durable Executionプラットフォームです。2022年創業のアイルランド発スタートアップで、GitHubスター数は14,000以上(2026年5月時点)。ライセンスはApache 2.0で、セルフホストも可能です。
最大の特徴は「タイムアウトなしの実行」です。AWS LambdaやVercel Functionsは最大15〜900秒の制限がありますが、Trigger.devではタスクを一時停止・再開できる仕組みにより、理論上は無制限に長いタスクを走らせられます。
Durable Executionとは
Durable Execution(耐久実行)とは、タスクの実行状態をチェックポイントとして保存しておき、障害・タイムアウト・意図的な一時停止があっても、その時点から再開できる仕組みです。
Trigger.devはCRIU(Checkpoint/Restore In Userspace)技術を使い、タスクのメモリ・CPUレジスタ・ファイルディスクリプタをそのままスナップショットします。再起動後はスナップショットをロードするだけで、コードは中断した行から続きを実行します。
インストールとプロジェクト初期化
Trigger.devはCLIとSDKで構成されています。まずNode.js 18以上の環境でプロジェクトを初期化します。
# CLI初期化(既存プロジェクトに追加する場合)
npx trigger.dev@latest init
# 新規プロジェクトを作成する場合
npx trigger.dev@latest create-project --name my-agent
CLIがインタラクティブに設定をガイドします。TypeScriptプロジェクトであれば、trigger.config.ts と src/trigger/ ディレクトリが自動作成されます。
# SDKのインストール
npm install @trigger.dev/sdk@latest
# AI SDKとの統合(Vercel AI SDK v6対応)
npm install @trigger.dev/ai @ai-sdk/openai
# ローカル開発サーバーの起動
npx trigger.dev@latest dev
ローカル開発時は、CLIがトンネルを張ってTrigger.dev Cloudと接続します。本番デプロイは npx trigger.dev@latest deploy 一発です。
基本タスクの定義
Trigger.devのタスクは task() 関数で定義します。TypeScriptで書くだけで、あとは自動的にデプロイ・管理されます。
// src/trigger/hello-world.ts
import { task } from "@trigger.dev/sdk/v3";
export const helloWorldTask = task({
id: "hello-world",
run: async (payload: { message: string }) => {
console.log(`受信メッセージ: ${payload.message}`);
return { result: `処理完了: ${payload.message}` };
},
});
タスクをトリガーするには、アプリケーションコードから trigger() を呼ぶだけです。
// Next.js API Routeやサーバーアクションから
import { helloWorldTask } from "@/trigger/hello-world";
const handle = await helloWorldTask.trigger({ message: "こんにちは" });
console.log(`Run ID: ${handle.id}`);
スケジュールタスク(Cron)
// src/trigger/daily-report.ts
import { scheduleTask } from "@trigger.dev/sdk/v3";
export const dailyReportTask = scheduleTask({
id: "daily-report",
cron: "0 9 * * 1-5", // 平日9時に実行
run: async (payload) => {
// レポート生成ロジック
const report = await generateReport(payload.lastTimestamp);
await sendSlackNotification(report);
},
});
Run Hierarchies — 親子タスクで複雑なフローを構成する
実際のAIエージェントでは、複数のタスクをチェーンさせることがほとんどです。Trigger.devは親タスクから子タスクを triggerAndWait() で呼び出すことで、ツリー構造のワークフローを簡単に構築できます。
// src/trigger/research-agent.ts
import { task } from "@trigger.dev/sdk/v3";
import { fetchUrlTask } from "./fetch-url";
import { summarizeTask } from "./summarize";
export const researchAgentTask = task({
id: "research-agent",
run: async (payload: { urls: string[] }) => {
// 子タスクを並列実行
const fetchResults = await Promise.all(
payload.urls.map((url) =>
fetchUrlTask.triggerAndWait({ url })
)
);
// 全ての取得が完了してから要約
const summaries = await Promise.all(
fetchResults.map((r) =>
summarizeTask.triggerAndWait({ content: r.output.content })
)
);
return { summaries: summaries.map((s) => s.output) };
},
});
triggerAndWait() は子タスクの完了を待機しますが、この「待機中」はCRIUチェックポイントが作られ、インフラリソースは消費されません。コストを抑えながら複雑なフローを書けます。
Idempotency(冪等性)とRetry戦略
AIエージェントで一番困るのが「どこまで処理したかわからなくなる」状況です。Trigger.devの冪等性キーはこれを解決します。
import { task, idempotencyKeys } from "@trigger.dev/sdk/v3";
export const processOrderTask = task({
id: "process-order",
retry: {
maxAttempts: 5,
factor: 2,
minTimeoutInMs: 1000,
maxTimeoutInMs: 30000,
},
run: async (payload: { orderId: string }) => {
// 冪等性キーを設定 — 同じキーで2回呼ばれても1回しか実行しない
const key = await idempotencyKeys.create(payload.orderId);
const paymentResult = await chargePaymentTask.triggerAndWait(
{ orderId: payload.orderId },
{ idempotencyKey: key }
);
// 失敗した場合、retry設定に従って自動でリトライ
if (!paymentResult.ok) {
throw new Error(`Payment failed: ${paymentResult.error}`);
}
return paymentResult.output;
},
});
失敗が起きても、冪等性キーのおかげでリトライ時に決済が2重実行される心配がありません。
LLM API呼び出しのDurable化パターン
AIエージェント開発でもっとも実用的なパターンです。OpenAI APIの呼び出しをDurableにラップすることで、レート制限・ネットワーク障害・タイムアウトから安全に保護できます。
// src/trigger/llm-pipeline.ts
import { task } from "@trigger.dev/sdk/v3";
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export const llmAnalysisTask = task({
id: "llm-analysis",
retry: {
maxAttempts: 10,
// OpenAIのレート制限に合わせた指数バックオフ
factor: 2,
minTimeoutInMs: 2000,
maxTimeoutInMs: 120000,
},
run: async (payload: { documents: string[] }) => {
const results = [];
for (const [index, doc] of payload.documents.entries()) {
// 各ドキュメントを冪等性キーで保護
const key = await idempotencyKeys.create(`doc-${index}-${doc.slice(0, 50)}`);
const response = await openai.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "あなたは文書分析の専門家です。" },
{ role: "user", content: doc },
],
});
results.push({
index,
analysis: response.choices[0].message.content,
});
}
return { results };
},
});
このパターンにより、100件のドキュメントを処理中にAPIエラーが起きた場合も、50件目から再開できます。
Vercel AI SDKとの統合(v6対応)
import { task } from "@trigger.dev/sdk/v3";
import { generateText } from "ai";
import { openai } from "@ai-sdk/openai";
export const aiPipelineTask = task({
id: "ai-pipeline",
run: async (payload: { prompt: string }) => {
const { text } = await generateText({
model: openai("gpt-4o"),
prompt: payload.prompt,
});
return { text };
},
});
Waitpoints — 人間のレビューを挟むHITL対応
v4で追加されたWaitpointsは、タスクを外部イベント・人間の承認待ちで一時停止させる仕組みです。AIエージェントと人間のコラボレーション(HITL: Human-in-the-Loop)を実装するのに最適です。
import { task, wait } from "@trigger.dev/sdk/v3";
export const contentModerationTask = task({
id: "content-moderation",
run: async (payload: { content: string; reviewerEmail: string }) => {
// AIによる一次スクリーニング
const aiResult = await screenContentWithAI(payload.content);
if (aiResult.requiresHumanReview) {
// レビュアーにメール通知
await sendReviewEmail(payload.reviewerEmail, payload.content, aiResult);
// 人間の判断を最大24時間待機
// この間はインフラリソースを消費しない
const approval = await wait.forEvent<{ approved: boolean; reason: string }>(
"human-review-complete",
{
timeout: { hours: 24 },
filter: { contentId: payload.content.slice(0, 20) },
}
);
if (!approval.output?.approved) {
return { status: "rejected", reason: approval.output?.reason };
}
}
return { status: "approved", content: payload.content };
},
});
スケジューラ — CronエージェントとEvent Trigger
定期実行のcronエージェントと、WebhookやAPIからのイベントトリガーを組み合わせることで、完全自律のエージェントループを実装できます。
// イベントトリガー付きのエージェントループ
import { task, eventTrigger } from "@trigger.dev/sdk/v3";
export const webhookProcessorTask = task({
id: "webhook-processor",
trigger: eventTrigger({
name: "github.push",
}),
run: async (payload: { repository: string; commits: string[] }) => {
// GitHubのpushイベントを受け取ったら自動実行
const analysis = await analyzeCommits(payload.commits);
await postSlackSummary(payload.repository, analysis);
return { processed: payload.commits.length };
},
});
セルフホスト vs Cloud
Trigger.devはApache 2.0ライセンスで、CloudとセルフホストどちらでもOKです。
| 項目 | Trigger.dev Cloud | セルフホスト |
|---|---|---|
| 無料枠 | 月5,000ラン | 無制限 |
| 並列実行 | Free: 20 / Pro: 200 | インフラ依存 |
| ログ保持 | Free: 1日 / Pro: 14日 | 自由設定 |
| セットアップ | CLI 1コマンド | Docker / Kubernetes |
| データ主権 | Trigger.devサーバー | 自社インフラ |
| 料金 | Pro $50/月〜 | インフラコストのみ |
個人・スタートアップはCloudの無料枠から始めて、月5,000ランを超えたらProへ移行するのが合理的です。GDPRや社内コンプライアンスで自社インフラ必須の場合はDockerセルフホストが現実的な選択肢です。
# Dockerでセルフホストする場合
docker compose up -d
# 設定ファイル(docker-compose.yml)の主要環境変数
# TRIGGER_SECRET_KEY=your-secret-key
# DATABASE_URL=postgresql://...
# REDIS_URL=redis://...
Inngest・Temporalとの比較
| 項目 | Trigger.dev v4 | Inngest | Temporal |
|---|---|---|---|
| 言語 | TypeScript/Node.js, Python | TypeScript/Node.js | 多言語(Go, Java, TS, Python) |
| セルフホスト | 可(Apache 2.0) | 不可 | 可(MIT) |
| 設計哲学 | DX重視、AIネイティブ | イベント駆動 | エンタープライズ耐久性 |
| 学習コスト | 低(TypeScript関数を書くだけ) | 低〜中 | 高(Workflow概念の習得必要) |
| HITL対応 | v4 Waitpoints | waitForEvent | Signal/Query |
| AIエージェント向け | 公式サポート済み | フレームワーク連携 | 手動実装が必要 |
| 無料枠 | 月5,000ラン | 月10,000ラン | なし(セルフホストのみ) |
2026年時点での使い分けを端的に言うと、「AIエージェント・TypeScript中心ならTrigger.dev、イベント駆動アーキテクチャのWebアプリならInngest、巨大エンタープライズで運用コストを厭わないならTemporal」です。Inngest比較の詳細はVercel AI SDK・Inngest・Mastra 比較記事もあわせて参照ください。
実用パターン4選
1. バッチLLM処理
export const batchLlmTask = task({
id: "batch-llm",
run: async (payload: { items: { id: string; text: string }[] }) => {
const results = [];
for (const item of payload.items) {
const key = await idempotencyKeys.create(item.id);
const res = await analyzeWithLLM.triggerAndWait(
{ text: item.text },
{ idempotencyKey: key }
);
results.push({ id: item.id, analysis: res.output });
}
return { results, total: results.length };
},
});
2. Webhook受信+非同期処理
// Next.js API Route (WebhookからTrigger)
export async function POST(req: Request) {
const payload = await req.json();
// すぐ200を返し、処理はバックグラウンドへ
const handle = await processWebhookTask.trigger(payload);
return Response.json({ runId: handle.id });
}
3. Pythonとの組み合わせ(データパイプライン)
# Python SDKでの利用
# pip install triggerdotdev
from triggerdotdev import TriggerClient
client = TriggerClient(api_key="your-api-key")
# TypeScriptで定義したタスクをPythonからトリガー
handle = client.trigger(
task_id="llm-analysis",
payload={"documents": ["doc1", "doc2", "doc3"]}
)
print(f"Run started: {handle.id}")
4. cronエージェント(毎日レポート生成)
export const dailyAgentTask = scheduleTask({
id: "daily-agent",
cron: "0 8 * * 1-5", // 平日の朝8時
run: async (payload) => {
// 1. データ収集(複数ソース)
const data = await collectDailyData();
// 2. LLMで分析
const analysis = await llmAnalysisTask.triggerAndWait({ documents: data });
// 3. Slackへ通知
await notifySlack(analysis.output);
return { date: payload.timestamp, itemsProcessed: data.length };
},
});
よくある失敗パターン4選
失敗1: タスクIDの重複
// ❌ 同じIDを複数のタスクに使うとデプロイ時にエラー
export const task1 = task({ id: "my-task", ... });
export const task2 = task({ id: "my-task", ... }); // ID重複エラー
// ✅ IDはプロジェクト全体でユニークに
export const task1 = task({ id: "user-analysis-task", ... });
export const task2 = task({ id: "report-generation-task", ... });
失敗2: retry設定なしでLLM APIを呼ぶ
// ❌ retryなしだと1回の失敗でタスクが完全停止
export const noRetryTask = task({
id: "fragile-llm",
run: async (payload) => {
const result = await openai.chat.completions.create(...); // 失敗で終了
},
});
// ✅ retryを設定してLLM APIエラーを自動リカバリー
export const robustLlmTask = task({
id: "robust-llm",
retry: { maxAttempts: 5, factor: 2, minTimeoutInMs: 1000 },
run: async (payload) => {
const result = await openai.chat.completions.create(...);
},
});
失敗3: 環境変数の管理ミス
# ❌ ローカルのみの.envを本番に反映し忘れる
# ✅ Trigger.dev CLIで環境変数を安全に管理
npx trigger.dev@latest env set OPENAI_API_KEY sk-xxx --env prod
npx trigger.dev@latest env list --env prod
失敗4: 子タスクを awaithなしで呼ぶ
// ❌ triggerAndWaitを使わないと結果を待てない
childTask.trigger(payload); // 結果が取れない
const res = await childTask.trigger(payload); // これも非推奨
// ✅ 結果を待つなら triggerAndWait()
const res = await childTask.triggerAndWait(payload);
console.log(res.output);
まとめ:今日から始める3つのアクション
- 今日やること:
npx trigger.dev@latest initで既存プロジェクトに組み込み、最初のタスクをCloudにデプロイしてみる - 今週中: 既存のLambda・Vercel FunctionsのうちタイムアウトしているAPIコールをTrigger.devタスクに移行し、retry設定を追加する
- 今月中: 複数ステップのAIエージェントをRun Hierarchiesで構成し、WaitpointsによるHITLフローを実装する
Trigger.devはAIエージェントが抱える「タイムアウト」「重複実行」「リトライの複雑さ」という三大問題を一気に解決します。v4でセルフホストも大幅に改善されたため、本番運用のハードルは下がっています。
あわせて読みたい:
- Vercel AI SDK・Inngest・Mastra TypeScriptエージェントフレームワーク比較2026 — Durable Executionの選択肢を横断比較
- Composio完全ガイド2026|AIエージェント100+ SaaS統合 — エージェントにツール統合を追加する方法
この記事を読んでAIエージェントの本番導入イメージが固まってきた方へ
UravationではAIエージェント導入の研修・コンサルを行っています。Trigger.devを含むエージェント基盤の設計から実装まで、貴社の状況に合わせたサポートが可能です。
参考・出典
- How it works – Trigger.dev公式ドキュメント — Trigger.dev(参照日: 2026-05-06)
- Trigger.dev v4 GA — Trigger.dev(参照日: 2026-05-06)
- triggerdotdev/trigger.dev — GitHub — GitHub(参照日: 2026-05-06)
- Cloud Pricing — Trigger.dev — Trigger.dev(参照日: 2026-05-06)
- Trigger.dev vs Temporal — Trigger.dev(参照日: 2026-05-06)
著者: 佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。X(@SuguruKun_ai)フォロワー10万人超。
100社以上の企業向けAI研修・導入支援。著書累計3万部突破。
SoftBank IT連載7回執筆(NewsPicks最大1,125ピックス)。
ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。