メインコンテンツへスキップ
モジュール 2:マルチエージェント 4 / 6
中級 Session 10 プロトコル FSM

チームプロトコル

request_id を用いたリクエスト・レスポンス FSM をマスターし、信頼性の高いマルチエージェント連携を実現する。

2026年3月20日 17 分で読む

学ぶこと

複数のエージェントが協力する際には、共通の言語が必要です。構造化されたプロトコルがなければ、メッセージが失われ、レスポンスが順不同で届き、エージェントは既に届いた応答を待つためにサイクルを無駄にします。チームプロトコルは、すべてのインタラクションを管理する有限状態機械(FSM)によってこれを解決します。

このセッションが終わるまでに、以下のことを理解できるようになります:

  • リクエスト・レスポンス FSM とその5つの状態
  • request_id によるメッセージの相関付け
  • チームコミュニケーションのための構造化メッセージフォーマット
  • エラー処理:タイムアウト、リトライ、フォールバック戦略

問題

2つのエージェントが一緒に作業している場面を想像してください。エージェント A がエージェント B にコードレビューを依頼します。エージェント B が完了してフィードバックを返します。しかし、エージェント A は既に別のタスクに移っており、別のリクエストを送信しています。ここでエージェント B のレスポンスが届きます — しかし、どのリクエストへの回答なのでしょうか?最初のもの?2番目のもの?

プロトコルがなければ、チームコミュニケーションには3つの障害モードがあります:

1. LOST MESSAGES
   Agent A sends request → Agent B never sees it
   Agent A waits forever

2. MISMATCHED RESPONSES
   Agent A sends request #1 → Agent B responds
   Agent A sends request #2 → Agent B responds
   Which response goes with which request?

3. RACE CONDITIONS
   Agent A and Agent B both edit the same file
   Neither knows the other is working on it
   Results: merge conflict or overwritten work

これらは理論上の問題ではありません。複数のアクターが構造なしに非同期で通信するあらゆるシステムで発生します。

仕組み

リクエスト・レスポンス FSM

エージェント間のすべてのインタラクションは、5状態の有限状態機械に従います:

                    send request
    ┌──────┐      (with request_id)     ┌──────────────┐
    │      │ ──────────────────────────▶ │              │
    │ IDLE │                             │ REQUEST_SENT │
    │      │ ◀────────┐                 │              │
    └──────┘          │                 └──────┬───────┘
       ▲              │                        │
       │              │                        │ acknowledgment
       │              │ timeout/error          │ received
       │              │                        ▼
       │              │                 ┌──────────────┐
       │              ├──────────────── │              │
       │              │                 │   WAITING    │
       │              │                 │              │
       │              │                 └──────┬───────┘
       │              │                        │
       │              │                        │ response arrives
       │              │                        ▼
       │              │                 ┌──────────────────┐
       │              │                 │                  │
       │              └──────────────── │ RESPONSE_RECEIVED│
       │                                │                  │
       │                                └────────┬─────────┘
       │                                         │
       │              process result             │
       └─────────────────────────────────────────┘

各状態には明確なルールがあります:

  • IDLE — エージェントは新しい作業に対応可能。リクエストの送受信が可能。
  • REQUEST_SENT — エージェントはメッセージを送信済み。確認応答を待つ。
  • WAITING — リクエストが確認された。エージェントは実際のレスポンスを待つ。
  • RESPONSE_RECEIVED — 結果が届いた。エージェントはそれを処理し、IDLE に戻る。

FSM は、すべてのリクエストが正確に1つのライフサイクルを持つことを保証します。メッセージが同時に2つの状態にあることはありません。対応するリクエストなしにレスポンスが届くことはありません。

request_id パターン

request_id はすべてを結びつけるキーです。すべてのリクエストは一意の識別子を取得し、すべてのレスポンスは同じ識別子を含みます:

Agent A (Implementer)              Agent B (Reviewer)
       │                                  │
       │  ┌──────────────────────────┐    │
       │  │ type: "request"          │    │
       │  │ from: "implementer"      │    │
       ├─▶│ to: "reviewer"           │───▶│
       │  │ request_id: "req-0042"   │    │
       │  │ action: "review_code"    │    │
       │  │ payload: { file, diff }  │    │
       │  └──────────────────────────┘    │
       │                                  │
       │         ... time passes ...      │
       │                                  │
       │  ┌──────────────────────────┐    │
       │  │ type: "response"         │    │
       │◀─│ from: "reviewer"         │◀───│
       │  │ to: "implementer"        │    │
       │  │ request_id: "req-0042"   │    │
       │  │ status: "completed"      │    │
       │  │ payload: { feedback }    │    │
       │  └──────────────────────────┘    │
       │                                  │

エージェント A が request_id: "req-0042" を含むレスポンスを受け取ると、たとえその間に他のリクエストを送信していても、これがどのリクエストへの回答かを正確に把握できます。

構造化メッセージフォーマット

すべてのプロトコルメッセージは一貫した構造に従います:

{
  "type": "request | response | status | broadcast",
  "from": "agent-name",
  "to": "agent-name | all",
  "request_id": "req-0042",
  "timestamp": "2026-03-20T10:15:30Z",
  "action": "review_code | run_tests | fix_issue",
  "payload": {
    "description": "What this message contains",
    "data": {}
  },
  "metadata": {
    "priority": "normal | high | critical",
    "timeout_ms": 30000
  }
}

4つのメッセージタイプがそれぞれ異なる目的を果たします:

  • request — 「この作業をしてください」(レスポンスが必要)
  • response — 「結果です」(常に request_id を参照)
  • status — 「現在の状態です」(情報提供、レスポンス不要)
  • broadcast — 「全員が知るべきことです」(全チームメンバーに送信)

実践でのプロトコルメッセージタイプ

チーム連携で最も一般的なプロトコルパターン:

TASK DELEGATION
  Orchestrator → Implementer: "Implement feature X"
  Implementer → Orchestrator: "Feature X complete, 3 files changed"

STATUS QUERY
  Orchestrator → Implementer: "What is your current progress?"
  Implementer → Orchestrator: "75% complete, working on tests"

RESULT SHARING
  Implementer → Reviewer: "Here are my changes for review"
  Reviewer → Implementer: "2 issues found: [details]"

BROADCAST
  Orchestrator → all: "Priority changed. Stop current work."

チームプロトコルにおけるエラー処理

実世界のコミュニケーションは失敗します。プロトコルは3つのエラーシナリオを処理する必要があります:

┌────────────────────────────────────────────────┐
│           Error Handling Strategy               │
│                                                 │
│  1. TIMEOUT                                     │
│     Request sent → no response in 30s           │
│     Action: retry once, then escalate           │
│                                                 │
│     ┌───────┐  req   ┌─────────┐  timeout      │
│     │ Agent │──────▶ │ Waiting │──────────┐     │
│     │   A   │        │  30s    │          │     │
│     └───────┘        └─────────┘          │     │
│         ▲                                 │     │
│         │            retry (1x)           │     │
│         └─────────────────────────────────┘     │
│                                                 │
│  2. REJECTION                                   │
│     Agent B cannot handle the request           │
│     Response: { status: "rejected",             │
│                 reason: "out_of_scope" }         │
│     Action: reassign to another agent           │
│                                                 │
│  3. PARTIAL FAILURE                             │
│     Agent B completed part of the work          │
│     Response: { status: "partial",              │
│                 completed: [...],                │
│                 failed: [...] }                  │
│     Action: retry failed parts or fallback      │
│                                                 │
└────────────────────────────────────────────────┘

すべてのリトライが失敗した場合、フォールバック戦略は明確です:リクエスト元のエージェントが無期限にブロックするのではなく、自分で作業を試みます(ソロ実行)。

キーインサイト

プロトコルがなければ、チームコミュニケーションは混乱に陥ります。FSM は、すべてのインタラクションを決定論的にすることで、メッセージの紛失と競合状態を防ぎます。

request_id パターンは一見単純ですが、分散システムにおける最も困難な問題、つまり相関を解決します。3つのエージェントが同時にメッセージを送信している場合、どのレスポンスがどのリクエストに属するかを知ることが、連携と混乱の違いを生みます。

これは HTTP のリクエスト・レスポンス、データベーストランザクション、メッセージキューの確認応答と同じ原理です。FSM はその上に状態追跡のレイヤーを追加し、リクエストが忘れられず、レスポンスが誤ったルートに送られず、すべてのタイムアウトが処理されることを保証します。

実用的な利点:エージェントは追跡を失うことなく非同期で作業できます。実装者はコードをレビューに送り、次のタスクの作業を続け、レビューのフィードバックが届いたときに正しく処理できます。なぜなら、request_id がフィードバックの対象を正確に伝えるからです。

実践例

プロトコルを使用した完全なチームインタラクションを示します。実装者がレビュアーにコードを送り、フィードバックを受け取り、問題を修正します:

STEP 1: Implementer sends review request
─────────────────────────────────────────
{
  type: "request",
  from: "implementer",
  to: "reviewer",
  request_id: "req-0042",
  action: "review_code",
  payload: {
    file: "src/auth/login.ts",
    diff: "+function validateToken(token) { ... }"
  }
}
State: IDLE → REQUEST_SENT

STEP 2: Reviewer acknowledges receipt
─────────────────────────────────────────
{
  type: "status",
  from: "reviewer",
  to: "implementer",
  request_id: "req-0042",
  payload: { status: "acknowledged", eta_ms: 15000 }
}
State: REQUEST_SENT → WAITING

STEP 3: Reviewer sends feedback
─────────────────────────────────────────
{
  type: "response",
  from: "reviewer",
  to: "implementer",
  request_id: "req-0042",
  status: "completed",
  payload: {
    approved: false,
    issues: [
      { line: 12, severity: "high",
        message: "Token expiry not checked" },
      { line: 25, severity: "medium",
        message: "Missing error handling for malformed tokens" }
    ]
  }
}
State: WAITING → RESPONSE_RECEIVED

STEP 4: Implementer processes and returns to IDLE
─────────────────────────────────────────
Implementer reads issues, applies fixes,
then sends a new request (req-0043) for re-review.
State: RESPONSE_RECEIVED → IDLE

各ステップに明確な状態遷移があることに注目してください。実装者は、レビュアーがリクエストを受け取ったかどうか、またはフィードバックが何を指しているかを推測する必要はありません。request_id が会話全体を結びつけています。

独自のプロトコルメッセージの設計

チームのアクションを定義する際は、以下の原則に従ってください:

DO:
  - Use specific action names: "review_code", "run_tests", "fix_lint"
  - Include enough context in the payload for standalone execution
  - Set realistic timeouts based on expected work duration
  - Always include a request_id, even for simple queries

DON'T:
  - Use generic actions like "do_work" (ambiguous)
  - Send large file contents when a file path suffices
  - Set timeouts too short (causes unnecessary retries)
  - Omit the "from" field (makes debugging impossible)

変更点

チームプロトコルなしチームプロトコルあり
メッセージに構造がないすべてのメッセージがスキーマに従う
レスポンスとリクエストを対応付ける方法がないrequest_id が相関を提供
エージェントは応答待ちでブロックFSM が状態を追跡し、非同期作業を可能に
失敗がサイレントなハングを引き起こすタイムアウトとリトライがエラーを処理
競合状態が状態を破壊決定論的な状態遷移
デバッグが推測に頼るメッセージログが完全なインタラクション履歴を表示

次のセッション

セッション11では自律エージェントを扱います。エージェントが WORK フェーズと IDLE フェーズの間を遷移し、共有キューからタスクを自動的に取得し、人間のループ内監視なしで独立して動作する方法を学びます。