チームプロトコル
request_id を用いたリクエスト・レスポンス FSM をマスターし、信頼性の高いマルチエージェント連携を実現する。
学ぶこと
複数のエージェントが協力する際には、共通の言語が必要です。構造化されたプロトコルがなければ、メッセージが失われ、レスポンスが順不同で届き、エージェントは既に届いた応答を待つためにサイクルを無駄にします。チームプロトコルは、すべてのインタラクションを管理する有限状態機械(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 フェーズの間を遷移し、共有キューからタスクを自動的に取得し、人間のループ内監視なしで独立して動作する方法を学びます。