みんな「ループって一体何?」と聞いています。でも、誰も聞いていない質問があります:そのループを動かしているのは何か?
AI に関する議論は、エージェントシステムの中核的なプリミティブとしてループに収束しています。Matt Van Horn(@mvanhorn)は、エージェントループの系譜を ReAct からツール使用、オーケストレーションループ、そしてループがループを監督する段階まで追跡しました。Addy Osmani(@addyosmani)は、ループ内部の構成要素(自動化、ワークツリー、スキル、コネクタ、サブエージェント)を分解しました。Van Horn は 耐久性 に着目し、再起動に耐えられないループはループではないと主張しました。Osmani の重要なテーマは オーケストレーション でした。つまり、あなたに代わってエージェントにプロンプトを与えるシステムを設計するということです。
私は彼らの主張をさらに深めたいと思います。耐久性はループの特性だけではありません。それは、その下にある実行レイヤー全体のことです。重要なのは、耐久性のあるオーケストレーションがエージェントループアーキテクチャを構築するための基盤であるということです。このアーキテクチャを詳しく見ていきましょう。
ループが壊れる場所
/loop と /goal のパターンは、シングルエージェント、シングルセッションの作業をうまく処理します。エージェントはタスクが完了するまでループします。これで多くのケースをカバーできます。しかし、次の段階(Van Horn の枠組みにおけるステージ5)では、この仕組みは破綻します。
- 他のループを監督するループ
- 人間がトリガーするだけでなく、スケジュールで実行されるループ
- プロセスの再起動、デプロイ、クラッシュに耐えるループ
- サブエージェントを起動し、その結果を待つループ(場合によっては数時間後)
- 事後的に観測可能である必要があるループ
これはプロンプトの問題ではありません。インフラストラクチャの問題です。
Van Horn は @runes_leo を引用しています:「AI コーディングにおいて最もコストがかかるのは、もはやコードを書くことではなく、エージェントループを管理することです。」ターミナルでの while True は、これらのどれも提供しません。VM やサンドボックスでの長時間実行プロセスも同様です。
サーバー上でエージェントループを実行すると何が起こるかを考えてみてください。プロセスは停止したり、再起動したりします。デプロイ、OOM、スポットインスタンスの再利用。ループは再起動します。しかし、それは何をしていたのでしょうか?どのステップにいたのでしょうか?その Slack メッセージは既に送信済みだったのでしょうか?サブエージェントは既に呼び出していたのでしょうか?
わかりません。最初からやり直します。既に持っているデータを再取得します。既に下した判断のために LLM を再度呼び出します。重複した通知を送信します。重複したサブエージェントを起動します。目が覚めると、3 通の同じ Slack メッセージと混乱したチームがいます。
修正方法は「より良いエラー処理」ではありません。各ステップがチェックポイントされ、各判断が永続化され、回復とは最後に成功したステップから再開することを意味する実行モデルが必要です。
3 層のエージェントループアーキテクチャ
3 つの層。それぞれが具体的なプリミティブに対応します。
層 1: ループ
ループとは、cron と意思決定者を組み合わせたものです。スケジュール(またはトリガー)で実行され、状態を評価し、次に何をするかを決定します。
これは Van Horn の定義を具体化したものです:cron に欠けていたのは、中間にある判断部分です。エージェントが判断します。あなたではありません。cron は鼓動です。LLM は意思決定者です。ステップは、進捗をチェックポイントする耐久性のある実行です。
1export const infraHealthCheck = inngest.createFunction(2 { id: "infra-health-check" },3 { cron: "*/30 * * * *" }, // 30分ごと4 async ({ step }) => {5 const metrics = await step.run("fetch-service-metrics", async () => {6 return await fetchServiceMetrics(); // エラー率、レイテンシ、メモリ、CPU7 });89 const assessment = await step.run("assess-health", async () => {10 return await callLLM({11 prompt: `与えられたサービスメトリクスに基づき、システム全体の健全性を12 "正常"、"低下"、"重大" のいずれかに分類し、その理由を説明してください。13 メトリクス: ${JSON.stringify(metrics)}`,14 });15 });1617 if (assessment.status === "低下" || assessment.status === "重大") {18 await step.invoke("triage-incident", {19 function: incidentTriage,20 data: { metrics, assessment, services: assessment.affectedServices },21 });22 }23 }24);
毎週月曜日の午前9時にループが起動します。データを取得し、レポートが必要かどうかを LLM に尋ね、必要であればスキルを呼び出します。ステップ間にプロセスが再起動した場合、既に完了したステップは再実行されません。これがループです。LLM ではなく、LLM を 取り巻く ループです。
層 2: スキル
この文脈では、スキルはプロンプトではありません。それは耐久性のあるワークフローです。マルチステップで、再試行可能で、構成可能で、独立してデプロイ可能です。
Van Horn:「ループは配管です。資産はそれが呼び出すスキルです。」これが積み重なる部分です。システムが学習する新しいスキルごとに、すべてのループの能力が向上します。
1export const incidentTriage = inngest.createFunction(2 { id: "incident-triage", retries: 3 },3 { event: "infra.incident.triage" },4 async ({ event, step }) => {5 const details = await step.run("fetch-detailed-metrics", async () => {6 return await fetchDetailedMetrics({ services: event.data.services });7 });89 const deploys = await step.run("fetch-deploy-history", async () => {10 return await fetchRecentDeploys({ since: hoursAgo(2) });11 });1213 const analysis = await step.run("correlate-incident", async () => {14 return await callLLM({15 prompt: `これらのサービスメトリクスと最近のデプロイを関連付けてください。16 可能性のある根本原因と重大度を特定してください。17 メトリクス: ${JSON.stringify(details)}18 最近のデプロイ: ${JSON.stringify(deploys)}`,19 });20 });2122 await step.run("post-triage-summary", async () => {23 await slack.postMessage({24 channel: "#incidents",25 text: formatTriageSummary({26 analysis,27 affectedServices: event.data.services,28 recommendedActions: analysis.recommendations,29 }),30 });31 });3233 return analysis;34 }35);
このスキルは、取得、分類、ルーティングを行います。これは、組み込みのフォールトトレランスを備えた作業単位です。スキルは、LLM を中間に置いた AI ワークフローにも、決定論的なコードにもなります。
層 3: オーケストレーター
オーケストレーターはすべてを実行するエンジンです。cron のスケジュール、ステップの実行、再試行の管理、同時実行制限の適用、実行履歴の保存、実行中のワークフローを中断することなく新しい関数やワークフローをホットデプロイします。
この層は、誰も話したがらない層です。なぜなら、見えないことが前提だからです。しかし、基盤となるものです。
ほとんどの人はエージェントを「LLM + ツール」と考えています。エージェントループアーキテクチャはこれを再定義します。エージェントとは「ループ + スキル + オーケストレーション」です。LLM とツールはループの中にあります。LLM とツールは、アーキテクチャを維持したまま交換したり調整したりできます。オーケストレーションがアーキテクチャを可能にします。
問題が発生したとき
ハッピーパスは簡単です。しかし、これは本番環境で動作するソフトウェアです。物事は本当に計画通りに進むのでしょうか?
インシデントトリアージスキルが起動し、メトリクス API がタイムアウトしました。読み取りはディスクにアクセスする必要があり、インメモリキャッシュにはデータがありませんでした。この API を呼び出すステップが再試行し、API に再度アクセスします。データは部分的にキャッシュされ、API は完了します。スキルは何事もなかったかのように次のステップに進みます。
時には、それほど単純ではないかもしれません。API キーが期限切れになったり、ホスティングプロバイダーが 30 分間ダウンした場合はどうでしょうか?すべての再試行が尽きました。さて、どうなりますか?障害も処理する必要があります。
1export const incidentTriage = inngest.createFunction(2 {3 id: "incident-triage",4 retries: 3,5 onFailure: async ({ error, event, step }) => {6 // 関数は再試行を使い果たした後に失敗しました。7 // 元のイベントデータはまだあります。何も失われていません。8 await step.run("notify-failure", async () => {9 await slack.postMessage({10 channel: "#agent-ops",11 text: `⚠️ インシデントトリアージが失敗しました: ${error.message}。` +12 `次のヘルスチェックサイクルで再試行します。` +13 `影響を受けるサービス: ${event.data.services.join(", ")}`,14 });15 });16 },17 },18 { event: "infra.incident.triage" },19 async ({ event, step }) => {20 /* 上記のスキルと同じロジック */21 }22);
onFailure ハンドラーは、すべての再試行が尽きた後に起動します。運用チャンネルに投稿して、誰かが知ることができるようにします。イベントは保存され、何も失われません。次回のスケジュール実行時に、失敗した箇所から処理を引き継ぎます。
耐久性のあるオーケストレーションは、一時的なエラーに対するステップレベルの再試行と、回復不能なエラーに対する障害処理フックを提供する必要があります。これがないと、障害が発生したとき(実際に発生します)、何時間も何日も経ってから気づくことになります。
一時的なエラーはコストもかかります。スキルやエージェントが最初から再試行すると、LLM を複数回呼び出し、不必要にトークンを消費することになります。LLM の呼び出しはチェックポイント可能です。これをシステム全体の 10 や 30 のエージェントで考えると、それは高額です。
ステップレベルのチェックポイントは、単なる正確性の機能ではありません。コスト削減策でもあります。
自身のスキルを構築するエージェント
ここからがさらに興味深いところです。システムは静的ではなく、進化し、拡張するように設計されています。
エージェントはループ内で実行されるだけではありません。新しいループを作成し、オーケストレーションエンジンに登録します。デプロイされた各関数は耐久性のあるスキルであり、独立して実行され、ループやエージェントからトリガーされたり、スケジュールで実行されたり、独自の再試行ロジックを持ちます。スキルは積み重なります。
これはオーケストレーションを認識するエージェントです。
仕組みは次のとおりです。AI エージェントはツールとしてオーケストレーション SDK にアクセスできます。新しい関数を作成し、エンジンに登録すると、すぐに実行を開始します。エージェントプロセスは、実行中の実行を再起動したり中断したりすることなく、新しい関数をホットリロードします。
具体的な例を見ていきましょう。
1. 人間がニーズを表明します。 エンジニアが言います:「私たちのサービスは夜間にレイテンシが急上昇し続けていて、朝まで誰も気づきません。」これがトリガーです。エージェントは周辺データからあいまいなパターンを推測する必要はありません。明確な指示があります。
2. エージェントがスキルを書きます。 2 つのマルチステップ関数:30 分ごとに実行され、エラー率、レイテンシ、リソース使用量を取得し、LLM がシステムの健全性を正常、低下、重大に分類するヘルスチェックループ。そして、詳細なメトリクスと最近のデプロイ履歴を取得し、LLM で根本原因を関連付け、推奨アクションとともにトリアージサマリーを Slack に投稿するインシデントトリアージスキル。エラー処理:メトリクス API がダウンしている場合は、バックオフして再試行。LLM が失敗した場合は、ルールベースの重大度分類にフォールバック。
3. エージェントがスキルをデプロイします。 エージェントは、サイドカープロセスによって取得される関数コードを記述します。新しい関数は自動的に登録されます。デプロイパイプラインも PR もなしに、すぐに有効になります。
4. スキルが自律的に実行されます。 30 分ごとに、エンジンがヘルスチェックをトリガーします。問題があれば、トリアージスキルを呼び出します。ループ内に人間は介入しません。完全に耐久性があります。
5. エージェントがシグナルに基づいて反復します。 これが人々が軽視する部分なので、「反復」の意味を具体的に説明します。エージェントは魔法のようにパターンに気づくわけではありません。別のレビューループがあります。cron でトリガーされる関数で、毎週実行され、オーケストレーターから実行履歴を読み取り、パフォーマンスを評価します。
1export const reviewSkillPerformance = inngest.createFunction(2 { id: "review-skill-performance" },3 { cron: "0 10 * * 5" }, // 毎週金曜日午前10時4 async ({ step }) => {5 const runs = await step.run("fetch-run-history", async () => {6 return await getInngestRuns({7 functionId: "incident-triage",8 since: daysAgo(7),9 });10 });1112 const analysis = await step.run("analyze-performance", async () => {13 const successRate = runs.filter(r => r.status === "completed").length / runs.length;14 const avgDuration = average(runs.map(r => r.duration));15 const incidents = await fetchIncidentOutcomes(); // インシデントは実際の障害と相関していましたか?1617 return await callLLM({18 prompt: `過去1週間のこのスキルのパフォーマンスをレビューしてください。19 成功率: ${successRate}20 平均実行時間: ${avgDuration}ms21 実際の障害と相関したインシデント: ${incidents.confirmed}/${incidents.total}22 誤検知: ${incidents.falsePositives}23 チームがアラートに対応した件数: ${incidents.actedOn}/${incidents.total}2425 しきい値や分類を調整する必要がありますか?具体的にどのような変更が必要ですか?`,26 });27 });2829 if (analysis.shouldModify) {30 await step.invoke("update-skill", {31 function: coreAgent,32 data: { prompt: `以下の提案された変更に基づいて、インシデントトリアージスキルを更新してください: ${analysis.proposedChanges}` },33 });34 }35 }36);
「レビュー」は関数です。実行履歴を読み取り、インシデントが実際の障害と相関しているかどうかを確認し、そのシグナルを LLM に渡します。ヘルスチェックがサービスを「低下」としてフラグし続けているのに、しきい値が敏感すぎるためにチームが無視している場合、レビューループがそれを検出し、スキルが分類を調整するために更新されます。魔法ではありません。判断の座に LLM がいる cron ジョブです。
検証はどうするのか? エージェントがコードを書くことは、それを取り巻くガードレールと同じくらい重要です。コードは型チェックできます。エージェントは、オーケストレーションエンジン自体と対話できるため、関数自体を呼び出してテストできます。完璧ではありませんが、コアエージェントに、動作するシステム内でネイティブに作成したスキルをデバッグする能力を与えています。レビューループは、最初のデバッグで捕捉されなかった問題を捕捉します。
さらに一歩進めて、エージェントは onFailure フックを使用して、特定の障害を評価するために自身をトリガーできます。これは、改善を続けるフィードバックループです。
競合はどうするのか? フロー制御、具体的には同時実行制御またはシングルトンが単純なケースを処理します(concurrency: { limit: 1, key: "[event.data.service" }])。つまり、サービスごとに一度に実行されるインシデントトリアージは 1 つだけです。しかし、より深い疑問は、2 つのヘルスチェックが同時に同じサービスで問題を検出した場合はどうなるかということです。オーケストレーターがそれらをキューに入れます。2 番目のトリアージは、最初のものが完了するまで待機します。重複アラートもレースコンディションもありません。これは理論上の話ではありません。これは、任意のジョブキューで使用するのと同じ同時実行プリミティブです。
エージェントは単にタスクを実行しているのではありません。自身のためのインフラストラクチャを構築しています。各スキルは、それを作成した会話を超えて持続します。エージェントプロセスを強制終了して再起動します。スキルは実行され続けます。基盤となるモデルを交換します。スキルは実行され続けます。エージェントは一時的なものです。その出力は耐久性があります。

エージェントループアーキテクチャのシステム概要
開発者の視点
これが重要なのは、開発者がエージェントが何をデプロイしたか、何が壊れたかをデバッグし、午前 3 時に何が実行されたかを監査できない場合、アーキテクチャ全体が大きな負債になるからです。
オーケストレーションエンジンは、すべての実行、すべてのステップ、すべての入力、すべての出力、すべての再試行を保存します。先週の火曜日にエージェントがデプロイしたスキルが午前 4 時に失敗しましたか?どのステップが失敗したか、入力は何だったか、どのエラーがスローされたか、諦めるまでに何回再試行したかを正確に確認できます。ステップレベルまでの完全なトレースは、オーケストレーションエンジン自体の出力です。
これは後付けでダッシュボードを追加したものではありません。これは耐久性のある実行に内在するものです。すべての step.run() はチェックポイントです。すべてのチェックポイントは観測可能です。コードを書いたものが人間ではない場合、観測可能性は「あると良い」ものではなく、信頼の基盤です。
日常的に、開発者のワークフローは次のようになります。朝に実行ダッシュボードを確認します。どのスキルが夜間に実行されたか、どれが成功し、どれが失敗したかを確認します。エージェントが作成したスキルが誤動作している場合、コードを直接読み、編集し、削除するか、エージェントに修正を依頼することができます。エージェントが作成しましたが、あなたが所有しています。エージェントとそのスキルは、あなたが手入れすべき庭のようなものです。
耐久性が基本である理由
Van Horn:「これらのものは再起動に耐えなければなりません。」
耐久性が実際に意味するものは次のとおりです。
要件
意味
基本的な while ループが失敗する理由
独立したステップの再試行
5 つのステップのうちステップ 3 が失敗した場合、ステップ 1 と 2 ではなくステップ 3 を再試行する
ループの再起動はすべてを最初から再実行する
サブエージェントのライフサイクル
子タスクを生成し、その完了を待機し(場合によっては数時間)、親がキャンセルされたら子もキャンセルする
組み込みの親子ライフサイクル管理がない
保証されたイベント配信
エージェントがダウンしている間にイベントが発生した場合でも、処理される必要がある
プロセスが実行されていないとイベントは失われる
事後的な観測可能性
事後的に何が起こったかを確認できる:すべてのステップ、すべての判断、すべての再試行
ログしか選択肢がなく、ログは一時的である
ダウンタイムなしのホットデプロイ
実行中の実行を強制終了せずに新しい関数バージョンをデプロイする
プロセスの再起動はすべてを強制終了する
同時実行制御
スキルのインスタンスを一度に N 個だけ実行する
組み込みの同時実行プリミティブがない
「コンテナで実行するだけ」で稼働時間は得られます。正確性は得られません。クラッシュ後に再起動するコンテナはプロセスを復元しますが、実行中のすべてのループは最初からやり直します。すべてのステップが再実行されます。すべての LLM 呼び出しが再度行われます。ループは実行されているように見えますが、目が見えない状態で実行されています。
既存のツールとの比較
この種のシステムに対して、一部のツールは「見栄えの良い」ターンキーソリューションを提供するかもしれません。または、より低レベルのツールを寄せ集めて独自のシステムを作成することを選択するかもしれません。どちらの選択も 間違って はいませんが、適切なアーキテクチャ層により、あなたとあなたのエージェント が時間の経過とともに進化できるようにする必要があります。柔軟で、動的で、耐久性があります。
エージェントに適した耐久性のある実行プリミティブ。エージェントが簡単に記述でき、エージェント自身がオーケストレーションを認識できるようにするための観測可能性と API。
動作例
Inngest では、これらのパターンを社内でテストしており、そのコンセプトは utah プロジェクトリポジトリ(https://github.com/inngest/utah)で確認できます。これは、Inngest の耐久性のあるオーケストレーション上に構築されたエージェントハーネスであり、それ自体もオーケストレーションを認識します。
このシステムにはサイドカープロセスがあり、メインエージェントが自身のワークスペース内で Inngest 関数を作成および編集し、この記事の文脈における「スキル」を使って自身を拡張できるようにします。近い将来、スターターループを例として含む完全なシステムを提供する予定ですが、そこにあるアイデアは、この記事の概念をより明確に示すことができます。
累積的なループ
Satya Nadella の 最近の投稿 は、業界が感じていたものを明確に表現しています。堀はモデルではなく、ループであるというものです。
彼のフレーミング:2 種類の資本があります。人的資本、つまりチームが長年にわたって構築してきた知識と判断力。そして彼が トークン資本 と呼ぶもの、つまり企業が基盤モデルの上に構築する AI ワークフロー、判断パターン、学習されたスキルです。
これらのテーゼは、これらが一緒になって累積するというものです。改善されたワークフローはより良いシグナルを生成します。より良いシグナルはよりシャープな AI の動作を生み出します。よりシャープな動作は、より高度な判断を必要とする作業に人間の注意を解放します。一種の山登りマシンです。
これがエージェントループアーキテクチャが具体的に可能にするものです。
- エージェントがデプロイするすべての耐久性のあるスキルは、実行可能なインフラストラクチャとしてコード化された組織の知識です。それは永続化されます。人間が見ていようが見ていまいが実行されます。
- cron でトリガーされるレビューループは、スキルのパフォーマンスを評価し、反復します。これが現実のものとなった山登りマシンです。デッキの中のフライホイール図ではありません。cron トリガーを持つ関数です。
- スキルがプロセスの再起動で死滅する場合、累積はゼロにリセットされます。耐久性こそが、投資を永続させるものです。
Nadella の重要なポイント:「企業は、学習システムに組み込まれた『会社のベテラン』の専門知識を失うことなく、『ゼネラリスト』モデルを交換できるべきです。」これがスキルライブラリパターンです。耐久性のある関数は、どの LLM がそれらを呼び出すか気にしません。
それに応じて構築する
これまでの議論は、エージェントが何をするかについてでした。ループ、ツール、推論、コンテキストエンジニアリング。次の議論は、何がエージェントを実行するかについてです。
3 つの層:ループ、スキル、オーケストレーター。ループは作業単位です。スキルは資産です。オーケストレーションエンジンは、両方を耐久性のあるものにします。サイドカーパターンがモデルです。エージェントは自身の耐久性のあるスキルを作成し、デプロイし、それらのパフォーマンスをレビューし、反復します。思考実験ではありません。それは動作するモデルです。
私たちは、このためのオーケストレーションエンジンとして Inngest を構築しました。step.run()、step.invoke()、cron トリガー、イベント駆動型制御フロー、同時実行制御、完全なステップレベルの観測可能性を備えています。しかし、アーキテクチャパターンは単一のツールよりも大きいものです。本番環境でエージェントループを構築している場合は、3 つの層を定義してください。
プリミティブは今日存在します。それに応じて構築してください。





