AIエージェントを複数つなぐと、最初に壊れるのは精度ではなく責任境界です。triage agent が調査を専門 agent に渡す。review agent が修正 agent に渡す。incident agent が rollback agent に渡す。見た目は自然ですが、渡す情報が多すぎると不要な秘密情報まで流れ、少なすぎると次の agent が勝手に推測します。
この記事では、AIエージェントの 引き継ぎ設計 を handoff contract として整理します。複数エージェントの全体パターンは マルチエージェントオーケストレーション4パターン、本番前の評価は AIエージェント評価設計ガイド、実行直前の人間承認は AIエージェント承認フロー設計ガイド、稼働中に止める監視は AIエージェント本番監視設計ガイド に分けています。
今回扱うのは、agent A から agent B へ仕事を渡す瞬間に何を固定するかです。つまり「複数 agent をどう並べるか」ではなく、「渡した後に責任が溶けないようにする設計」です。
先に結論: handoff は会話転送ではなく契約として扱う
handoff を「今までの会話を全部渡すこと」と捉えると、ほぼ失敗します。次の agent は前段のノイズ、途中仮説、不要な tool output、secret に近い値まで受け取り、何を根拠に作業すべきか判断しにくくなります。
私なら、handoff は次の6項目を持つ契約として扱います。
| 項目 | 書くこと | 失敗すると起きること |
|---|---|---|
handoff_id | 引き継ぎ単位のID | 監査ログで追えない |
from / to | 渡す側と受ける側 | 責任者が曖昧になる |
task | 次の agent がやる作業 | 作業範囲が広がる |
context_summary | 判断に必要な要約だけ | 会話全文依存になる |
allowed_actions | read / draft / write / external action | 権限が前段から漏れる |
return_contract | 返す形式と戻し先 | 親 agent が統合できない |
OpenAI Agents SDK では handoff は agent 間の delegation であり、LLM から見ると handoff 用 tool として表現されます。さらに input_type で reason や priority のような小さな構造化メタデータを渡せます。ここから分かるのは、handoff は「雰囲気で任せる」機能ではなく、渡す理由と入力形を設計する場所だということです。
まず「渡すもの」と「渡さないもの」を分ける
handoff で一番重要なのは、次の agent が見る入力を絞ることです。
渡すべきものは、次の判断に直接必要な情報だけです。
- ユーザーの最終目的
- 現在の状態
- 既に試したこと
- 失敗した仮説
- 使ってよい tool と使ってはいけない tool
- 返してほしい形式
逆に、渡さない方がよいものもあります。
- 長い会話履歴全文
- 古い仮説や却下済み案
- secret、token、個人情報を含む raw log
- 前段 agent の内部 reasoning
- 次の agent が使えない tool の出力全文
OpenAI Agents SDK の handoff input filter は、handoff 先に見せる履歴を調整できます。公式 docs でも、必要なら tool call を履歴から除去する例が示されています。Claude Code の subagents でも、subagent は独立 context で動き、main conversation へ戻すのは結果要約です。この考え方は同じです。全部渡すのではなく、次の agent が判断できる最小の文脈へ圧縮するべきです。
最小の handoff contract
実務では、最初から複雑な schema を作る必要はありません。まずは YAML で十分です。
handoff_id: hf-20260610-pr-review-01
from: triage-agent
to: code-review-agent
task: "PR #123 の認証まわりだけを read-only で確認する"
context_summary:
user_goal: "release 前に auth regression がないか確認したい"
current_state: "CI は green。差分は auth middleware と session refresh"
tried:
- "unit test は通過"
- "manual QA は未実施"
known_risks:
- "session refresh path が 401 を握りつぶす可能性"
allowed_actions:
read: true
draft: true
write: false
external_action: false
forbidden:
- "branch push"
- "package install"
- "production API call"
return_contract:
format: "findings"
severity: ["P0", "P1", "P2", "P3"]
must_include:
- "根拠ファイル"
- "再現条件"
- "修正が必要かどうか"
on_failure:
ask_back_to: triage-agent
stop_if: "対象外ファイルの変更が必要になった"
この程度でも、handoff の事故はかなり減ります。特に allowed_actions と on_failure が効きます。agent は失敗すると「自分で進めよう」としがちなので、戻す条件を先に書きます。
権限は引き継がず、必ず狭め直す
handoff で危ないのは、前段 agent の権限をそのまま次へ渡すことです。
triage agent が read-only で調べていたのに、修正 agent に渡した瞬間に write できる。さらに外部 issue tracker や MCP server も読める。これでは、handoff が権限昇格の抜け道になります。
私なら、handoff のたびに権限を次の4段階で再評価します。
| 権限 | 例 | handoff 時の判断 |
|---|---|---|
| read | repo、log、docs を読む | 原則ここから始める |
| draft | 修正案、PRコメント案、runbook案を書く | 人間確認前の標準 |
| write | branch、file、issue を変更する | 明示承認がある時だけ |
| external action | deploy、token revoke、顧客通知 | 別承認と監査ログ必須 |
Claude Code subagents の docs でも、subagent には tool や MCP server、permission mode を個別に持たせられます。background subagent では permission prompt が必要な tool call は自動 deny されるため、広い権限を前提にした handoff は壊れます。これは制約というより、設計上の良い警告です。
handoff 先が必要とする最小権限を contract に書き、足りない場合は勝手に拡張させず、親 agent か人間へ戻す。この形にしておくと、監査ログと事故対応も追いやすくなります。
return contract を決めないと親 agent が壊れる
引き継ぎは渡す側だけでなく、戻す側も設計します。
よくある失敗は、専門 agent が長い調査メモを返し、親 agent がそれをまとめ直せず、重要なリスクを落とすことです。subagent を使って main context を汚さないつもりが、最後の返却で巨大な summary を戻してしまうパターンです。
return contract は、最低限これだけ決めます。
return_contract:
status: "ok | blocked | needs_human | out_of_scope"
summary: "3行以内"
findings:
- severity: "P0 | P1 | P2 | P3"
claim: "何が問題か"
evidence: "file / trace / tool log"
next_action: "誰が何をするか"
artifacts:
- type: "diff | report | command_output"
location: "URL or path"
confidence: "high | medium | low"
この形式なら、親 agent は次の判断をしやすくなります。
okなら次の工程へ進むblockedなら不足情報を取りに行くneeds_humanなら承認者へ戻すout_of_scopeなら routing をやり直す
Google ADK の delegation でも、agent の description が routing に使われるため、役割の違いを明確にすることが重要です。routing だけでなく戻り値も明確でないと、複数 agent の出力はすぐ読みにくくなります。
監査ログには handoff 自体を残す
handoff は内部処理に見えますが、運用上は監査対象です。どの agent が、なぜ、どの agent へ渡したのかが分からないと、失敗時に原因を追えません。
監査ログには、少なくとも次を残します。
| 項目 | 例 |
|---|---|
agent_run_id | 20260610-support-agent-01 |
handoff_id | hf-20260610-02 |
from_agent | triage-agent |
to_agent | refund-agent |
reason | billing decision required |
input_summary_hash | raw input を残さないための hash |
allowed_actions | read,draft |
result_status | ok, blocked, needs_human |
ここで raw input を全部保存する必要はありません。むしろ secret や個人情報を含む履歴を丸ごと保存すると、監査ログ自体がリスクになります。要約、hash、成果物の location、承認IDを残す方が扱いやすいです。
eval では「正しい agent に渡したか」を見る
handoff はプロンプトで祈るだけでは安定しません。eval に入れます。
最低限見るべきケースは5つです。
| ケース | 落としたい失敗 |
|---|---|
| 正常 routing | 適切な専門 agent へ渡せるか |
| 渡しすぎ | 不要な raw log や secret を渡さないか |
| 渡さなすぎ | 次の agent が判断不能にならないか |
| 権限昇格 | read-only task を write agent へ渡さないか |
| 戻し先 | scope 外を人間または親 agent へ戻せるか |
AIエージェント評価設計ガイド で書いた通り、final output だけを見る eval では足りません。handoff の eval では、trace 上の from、to、handoff reason、渡した input summary、戻り値 status を見ます。
実務でよくある失敗
私が一番警戒する失敗は、次の4つです。
1. specialist を増やしすぎる
専門 agent を増やすほど賢く見えますが、routing は難しくなります。最初は triage、worker、reviewer の3つで十分です。billing、legal、security、SRE のような細分化は、routing eval が通るようになってからでよいです。
2. handoff reason が曖昧
need help のような理由では監査にも eval にも使えません。auth regression suspected、external action approval required、PII detected のように、後から集計できる粒度にします。
3. 親 agent が最終責任を捨てる
handoff しても、最終責任は親 agent または人間側に残します。専門 agent の出力をそのままユーザーに出すのではなく、親 agent が task と allowed_actions に照らして統合します。
4. 失敗時の戻し先がない
handoff 先が判断できないとき、勝手に仮説を作って進めるのが一番危険です。on_failure.ask_back_to と stop_if を contract に入れておくと、この失敗を減らせます。
まとめ
AIエージェント引き継ぎ設計は、マルチエージェントを高度に見せるための飾りではありません。責任、文脈、権限、戻り値を小さく固定し、失敗時に止まれるようにするための運用設計です。
私なら、最初の handoff contract は次の順で作ります。
- 受け側 agent の役割を1文で書く
- 渡す context summary を3から5項目に絞る
- read / draft / write / external action を明示する
- return contract を status / findings / artifacts に分ける
- handoff を監査ログと eval dataset に入れる
この5つがないまま agent を増やすと、便利さより先に責任境界が崩れます。逆にここを固定すれば、複数 agent は「なんとなく分業する仕組み」ではなく、レビュー可能なワークフローになります。