この記事で考えてみたこと
- オーケストレーション(複数AIエージェントの連携)が流行している理由は?
- それはトークン削減・性能(精度/レイテンシ/コスト)に効くのか?という素朴な疑問を考える
- OpenAI Agents SDK が何を提供してくれるのか
- 実際に作ったリテール接客エージェントのアーキテクチャを紹介
動くデモとソースはGitHubに公開しておきました。
- リポジトリ: https://github.com/soonbig/agent-orchestrator
- ライブデモ: https://re-dc062bcbbe4e41e09b3d0be82ae3c820.ecs.ap-northeast-1.on.aws
(テキストは/、音声は/voice。コスト管理のため停止する場合があります)
1. まず「オーケストレーション」とは何か
一言でいうと、1体の万能AIに全部やらせるのをやめて、役割を分けた複数の小さなAIを、司令塔(オーケストレーター)が交通整理する設計です。
レストランに例えると分かりやすいです。
| 役割 | 担当 |
|---|---|
| オーケストレーター | フロア責任者。お客様の用件を聞いて「これは厨房」「これは会計」と振り分ける。自分では料理しない |
| 専門エージェント | 各担当(注文担当・商品担当・有人対応担当)。狭い守備範囲のプロ |
| ツール | 厨房の道具(在庫DBを見る、注文状況を引く等)。実際の作業はここで起きる |
今回作ったリテール接客エージェントも、この構造です。
- 🧭 オーケストレーター: 用件を読み取り、適切な専門家へ引き継ぐ(ルーティングに専念)
- 📦 注文管理: 注文状況・追跡・到着予定
- 🔎 商品サポート: 商品検索+倉庫横断のリアルタイム在庫
- 🙋 有人エスカレーション: 返金・クレーム等を、要約つきで人間に引き継ぐ
そして重要なのが、この同じ"頭脳"をテキストチャットでも音声でも使うという点です。デモには文字で会話する画面(/)と、マイクでしゃべって会話するライブ音声画面(/voice)の両方があり、裏側のエージェントとツールはまったく同じものを共有しています。(音声の仕組みはセクション4で触れます)
2. なぜ設計を「分ける」のか(定性的な理由)
「なぜ役割を分けるといいことがあるのか」というのをまとめてみました。
(1) 1つの巨大プロンプトは「壊れやすい」
「注文も商品も返金も多言語も全部こなして」という指示を1つのプロンプトに詰め込むと、指示同士が干渉し、ツールの数も増え、モデルがどれを使うべきか迷い始めます。結果、誤ったツール選択・指示の取りこぼし・予測不能な振る舞いが増えます。
役割を分ければ、各エージェントの責務が1つに絞られ、指示も明快になります。「このエージェントは注文のことだけ考えればよい」という状態は、人間にとっても読みやすく、AIにとっても判断しやすい。複雑さを"1か所に集中させない"ことがポイントです。
(2) 各部品を個別に改善・テスト・差し替えできる
商品検索だけ精度を上げたい、返金フローだけ厳格にしたい――オーケストレーションなら該当エージェントだけを直せます。モノリシックな1プロンプトだと、1か所いじると別の挙動が崩れる「もぐら叩き」になりがちです。
(3) 「人間に渡す」を design に組み込める
AIが万能であるかのように振る舞うのは危険です。返金・苦情のような領域は、有人エスカレーション専用エージェントに、会話の要約と適切なキューを付けて渡す。この「うまく人間に劣化させる(graceful degradation)」が設計として自然に書けます。
(4) チャネル/モダリティをまたいで再利用できる
同じ「エージェントの頭脳」を、テキストチャットでも音声でも使い回せます。今回もテキストUIと音声UIが同一のエージェント+ツールを共有しています。
3. トークンコストは安くなるのか?
色々調べる限り、「やり方次第で得する。ただし"常に安くなる魔法"ではない」ということのようです。
✅ 得するポイント
(a) プロンプトが短くなる(=入力トークン減)
専門エージェントは守備範囲が狭いので、システムプロンプトも、列挙するツール定義も小さい。1ターンあたりの入力トークンが減ります。万能プロンプト+全ツール定義を毎回送るより軽い。
(b) モデルを"階層化"できる(=コスト・レイテンシ減)
タスクの難しさに応じて、強いモデルと軽いモデルを使い分けます。
| レイヤー | モデルの種別 | 理由 |
|---|---|---|
| オーケストレーター | 高性能(賢い)モデル | ルーティングを間違えると以降が全部ズレる。推論力の高いモデルで品質を守る |
| 専門エージェント | 軽量(小型・高速・低コスト)モデル | 範囲が狭くツールも数個。引数を埋めて根拠ある回答を整える程度なら軽量モデルで十分 |
会話の大半のターンは専門エージェント側で起きます。 だから、そこを軽量モデルに落とすと、トラフィックの大部分でコストとレイテンシが下がる。一方で、最も間違いが高くつくルーティングにだけ高性能モデルを使う。「お金をかける場所を選ぶ」のがミソです。
具体的なモデル名と「賢さ」の関係について
今回はオーケストレーターに gpt-4o-mini、専門エージェントに gpt-4.1-nano を割り当てました。「4o-mini の方が 4.1-nano より新しそうなのに賢い扱い?」と混乱しがちですが、数字の大小やリリース時期ではなく、モデルの"サイズ感(nano < mini)"が判断の指標です。OpenAI のラインナップでは概ね nano が最小・最速・最安、mini はその一段上に位置づけられます。重要なのは固有名ではなく**「難所に強いモデル、量をさばく所に軽いモデル」という役割分担**です。SDK ではエージェントごとに model を指定でき、今回は環境変数 APP_ORCHESTRATOR_MODEL / APP_SUBAGENT_MODEL で切り替え可能にしました。
(c) ツール選択が速く・正確になる
候補ツールが少ないほど、モデルは正しいツールを選びやすい。やり直し(無駄なAPI往復)が減り、体感速度も上がります。
⚠️ 注意点(オーケストレーションのコスト)
- ホップが増えると往復も増える: オーケストレーター→専門家、と段が増えれば、その分のモデル呼び出しが発生します。1ターンで完結する単純タスクなら、むしろ単一エージェントの方が安いことも。
- だから「常に得」ではない: 効くのは、役割が明確に分かれ、会話が多ターンで、かつトラフィックが多いケース。今回のリテール接客はまさにそれに当てはまります。
まとめると、"賢く設計すれば"トークンもコストもレイテンシも下げられる。鍵は「短いプロンプト」「狭いツール」「モデルの階層化」です。
4. OpenAI Agents SDK は何をしてくれるのか
ゼロから組むと大変な「エージェントのループ」を、SDKが面倒みてくれます。今回使った機能は次の通りです。
Agent(エージェントの定義)
指示(instructions)+ツール+モデルをまとめた単位。「あなたは注文管理担当です。これらのツールを使えます」を宣言的に書くだけ。
ハンドオフ(handoffs)
エージェント同士を引き継ぎ線でつなぐ機能。オーケストレーターに「注文の用件なら注文管理へ」と handoff を登録しておけば、ルーティングは自動。専門家同士が相互に引き継いだり、いつでも有人対応へ渡すことも線を引くだけ。
関数ツール(function tools)
ただのPython関数にデコレーターを付けると、モデルが呼べるツールになります。SDKが型ヒントやdocstringから引数スキーマを自動生成してくれるので、面倒な定義書きが不要。今回は get_order_status / check_stock / create_support_ticket などを用意しました。関数は実データ(モックDB)を返すので、回答が"それっぽい嘘"にならず根拠に基づくのが重要点です。
Runner(実行ループ)
「ユーザー発話 → モデル思考 → ツール呼び出し → 結果を読む → 必要なら別エージェントへ handoff」という一連のループを回してくれる実行役。
OpenAI Realtime API(音声)
テキストとは別系統で、音声対音声をWebRTCで実現します。「文字起こし→考える→読み上げ」の3段パイプラインではなく、直接しゃべって直接返るので自然で速い。ブラウザは短命の一時キーでOpenAIに直接つながり(本物のAPIキーはブラウザに渡さない)、音声中でも同じバックエンドツールを呼べます。
5. アーキテクチャについて
今回作成したデモエージェントの問い合わせが、どう流れて答えになるかを順に追います。
記事の核心:「背骨」としての SessionContext
オーケストレーション設計で**一番大事なのが、この SessionContext(セッション・コンテキスト)です。何をしているかを一言で言うと、「会話全体で共有される1つのメモ帳」**です。
実装はとてもシンプルで、ただの dataclass(データの入れ物)です。 中身はこんなフィールドです。
-
customer_id(誰か)、language(何語か)、channel/modality(テキストか音声か) -
handoff_trail(どのエージェントが順番に対応したかの履歴) -
escalations(有人対応チケットの記録)
ポイントは「誰がこれを持ち回るか」です。Runner(実行ループ)が、この同じ1つのオブジェクトをすべてのエージェントとツールに引数として渡し続けます。各ツールは RunContextWrapper 経由でこのメモ帳を読み書きできます。だから――
- オーケストレーターが検出した言語を書き込めば、引き継ぎ先の専門エージェントも同じ言語で応答できる(毎回聞き直さない)
- ハンドオフのたびに
note_handoff()で足跡を1行残すので、「triage → 注文管理 → 商品サポート」という経路が後から見える - 有人対応に渡すときは、ただ"転送"するのではなく、このメモ帳ごと渡すので会話の文脈が人間にも引き継がれる
ここが「ハンドオフ=制御の受け渡し」と「コンテキスト=状態の受け渡し」を分けて考える設計の肝です。制御はエージェント間を飛び回っても、状態(メモ帳)は1つのまま壊れずに残る。これがあるから、話題が別エージェントに移っても会話が"ぶつ切り"になりません。
具体的な流れの例
- 「注文 ORD-50001 はどこ?」→ 司令塔が 📦 注文管理 へ handoff
- 注文管理が
get_order_statusツールを実行 → DBから実ステータス+到着予定を取得して回答 - 続けて「在庫ある?」→ 🔎 商品サポート へ handoff(文脈は維持)、倉庫横断で在庫確認
- 「返金して、人と話したい」→ 🙋 有人エスカレーション へ。要約付きチケットを作成して人間のキューへ
GitHubに公開したテスト版エージェントではモックDBを参照して返答します。
6. うまくいかないときどうするか(誤ルーティング・循環ハンドオフ)
「うまくいく理由」だけでは信用できません。オーケストレーションには固有の失敗モードがあり、そこへの備えこそが実用性を分けます。
ケース1:オーケストレーターが誤ルーティングした
例:在庫の質問なのに注文管理へ送ってしまう。対策は2つ。
再ハンドオフできる道を残す:各専門家は「自分の担当外」と気づいたら再ハンドオフできるようにしておきます。今回の実装ではどちらも可能にしています――(a) オーケストレーターへ戻る、(b) 他の専門エージェントへ直接渡すの両方です。ただし基本はオーケストレーターへ戻すのが安全です。司令塔に「振り直し」を一任すれば、ルーティング判断が1か所に集約され、専門家同士の直接受け渡しで起きがちな"たらい回し"を避けられます(専門家間の直行は、明らかに隣の担当だと分かる時の近道として使う)。いずれにせよ、一本道の「行き止まり」をなくすことがポイントです。
ケース2:ハンドオフが循環する
A→B→A→B…と互いに押し付け合って無限ループに陥るリスク。対策は次の3つです。
-
ターン上限(max turns)を設ける:
Runnerには一連の処理の上限があり、暴走を打ち切れる - handoff_trail を監視する:同じ遷移が繰り返されていたらループと検知して、オーケストレーターへ戻すか人間にエスカレートする
- 迷ったら人間へ:どのエージェントも最終手段として有人エスカレーションへ走れるようにしておく
ケース3:ツールが失敗した・データがない
存在しない注文番号など。ツールは推測で埋めずに「見つからない」と明示的に返し、モデルには「勝手に推測しない」よう指示しておく。根拠のない回答(ハルシネーション)を防ぐためです。
こうした「うまくいかない時の振る舞い」を設計に組み込めるのも、役割を分けているからこそ考えやすいという利点です。
7. 多言語・マルチチャネルへの"伸びしろ"
- 多言語: 検出した言語をコンテキストに入れ、各エージェントの指示へ動的に注入。同じ構造のまま、英語・スペイン語・日本語などに応答できる。
- マルチチャネル/マルチモーダル: 同じ頭脳をテキストにも音声にも。音声では「短く・箇条書きを避け・IDはゆっくり読む」など、チャネルに応じた話し方へ切替。
「1つの設計を、言語・チャネル・モダリティへ横展開できる」のがオーケストレーションの旨味です。
8. まとめ
| ポイント | 内容 |
|---|---|
| なぜ流行るか | 巨大プロンプトの"壊れやすさ"を解き、部品ごとに改善・テスト・差し替えができるから |
| トークン/性能 | 「設計次第で」得する。鍵は短いプロンプト・狭いツール・モデルの階層化 |
| SDK の役割 |
Agent/ハンドオフ/functionツール/Runner/Realtime API を提供し、実装に集中できる |
| 背骨 | SessionContext。制御(ハンドオフ)と状態(コンテキスト)を分離し、文脈を失わない |
| 失敗への備え | 誤ルーティング・循環・ハルシネーションそれぞれに逃げ道を設計する |
| ツールと実データ | ツールが実データを返すので根拠に基づく回答。本番連携は局所的な差し替えで済む |