複数のエージェントで協力する
監督役のエージェントにタスクを分割させ、コンテキストが独立した三つの作業役のエージェントに並行して任せ、結果をまとめます。単一のエージェントと比べてトークンは 3 分の 1 少なくなり、監督役のコンテキストはわずか 472 トークンでした。マルチエージェントの本当の価値と代償を説明します。
- 約 40 分
- 難易度:中級
- 検証:2026-09-14 deepseek-flash
コードと実行結果は実際に動かしたときのまま載せているため、コメントと出力は中国語です。
一人では終わらない仕事は、何人かで分担できます。エージェントも同じです。「監督役」に大きなタスクをいくつかの小さなタスクに分けさせ、何人かの「作業役」エージェントに割り振り、それぞれが終えたら結果をまとめる。これをマルチエージェント(multi-agent)システムと呼びます。
とても自然で魅力的に聞こえ、多くのフレームワークがこのために設計されています。しかし、タスクをエージェントの集団に任せる前に、はっきりさせておくべきことがあります。マルチエージェントは実際に何を解決するのか。代償は何か。この課では第 4 課のあのタスクで比較をしてみます。
よくある組織のしかた
- 監督と作業役:監督がタスクの分割、割り振り、取りまとめを担当し、作業役はそれぞれ一つのサブタスクをこなします。サブタスクどうしが独立しているときに最も向いています。
- パイプライン:一つ目のエージェントの出力が二つ目の入力になります。たとえば「リサーチャーが資料を調べる → ライターが初稿を書く → レビュアーがチェックする」。実はワークフローに近く、各ステップを一つのエージェントがこなしているだけです。
- ディベート:いくつかのエージェントが同じ問題にそれぞれ答えを出し、互いに疑問を投げかけ、最後に投票するか審判が決めます。
この課では一つ目を作ります。マルチエージェントの核心的な利点を最もよく示せるからです。
実験:監督と三つの作業役
タスクは第 4 課と同じです。「httpx の Client と AsyncClient は、タイムアウト、プロキシ、HTTP/2 の三つで設定方法が同じかどうかを比べてください。表にまとめ、どのマスにもドキュメントの出典を明記してください。」
このタスクはもともと分けられます。タイムアウト、プロキシ、HTTP/2 の三つは互いに関係がないので、別々に調べられます。
監督の仕事は三つのステップです。
def supervisor():
# 1. 主管拆任务:只输出 JSON,不调用工具
plan, usage = model([{"role": "user", "content":
TASK + '\n\n把这个任务拆成互相独立、可以同时进行的子任务,每个子任务只查一件事。'
'输出 json:{"subtasks": ["子任务1", ...]}'}], None)
subtasks = json.loads(plan.content[plan.content.find("{"):plan.content.rfind("}") + 1])["subtasks"]
# 2. 工人并行执行,每个工人都从空白的上下文开始,只看得到自己的子任务
def work(subtask):
return run_agent(model, subtask + "\n只回答这一件事,写清楚结论和文档出处(文件名和行号),不超过 150 字。",
max_steps=8, verbose=False)
with ThreadPoolExecutor(len(subtasks)) as pool:
results = list(pool.map(work, subtasks))
# 3. 主管汇总:只看工人交回的简短结论,看不到工人查过的原文
reports = "\n\n".join(f"子任务:{s}\n结论:{a}" for s, (a, _) in zip(subtasks, results))
final, usage = model([{"role": "user", "content": TASK + "\n\n下面是各子任务的调查结论,据此完成任务:\n\n" + reports}], None)
……
作業役は第 2 課で書いた run_agent そのもので、何も変えていません。各作業役は空のメッセージのリストから始まり、自分のサブタスクしか知りません。「150 字以内」を求めているのは、監督に返す内容を十分に簡潔にするためです。
比較対象は、単一のエージェントがタスク全体をそのままこなす場合です。完全なコードは code/05-agents/multi_agent.py にあります。
結果
単一のエージェント:
===== 单个智能体
[第 1 步] grep_docs({"keyword": "AsyncClient"}) → ……
[第 1 步] grep_docs({"keyword": "timeout"}) → ……
[第 1 步] grep_docs({"keyword": "proxy"}) → ……
[第 1 步] grep_docs({"keyword": "http2"}) → ……
[第 2 步] read_doc({"path": "advanced/timeouts.md", "start": 1, "end": 70}) → ……
[第 2 步] read_doc({"path": "advanced/proxies.md", "start": 1, "end": 50}) → ……
[第 2 步] read_doc({"path": "http2.md", "start": 20, "end": 75}) → ……
[第 3 步] read_doc({"path": "advanced/timeouts.md", "start": 70, "end": 120}) → ……
[第 3 步] grep_docs({"keyword": "AsyncClient(proxy"}) → 没有找到 AsyncClient(proxy
[第 3 步] grep_docs({"keyword": "AsyncClient(timeout"}) → 没有找到 AsyncClient(timeout
[第 3 步] read_doc({"path": "advanced/clients.md", "start": 1, "end": 40}) → ……
[第 4 步] read_doc({"path": "async.md", "start": 1, "end": 50}) → ……
[第 4 步] read_doc({"path": "api.md", "start": 30, "end": 50}) → ……
共 5 次模型调用,21047 词元,9 秒
監督と三つの作業役:
===== 主管 + 工人
主管拆出了 3 个子任务:
- 调查 httpx.Client 和 AsyncClient 在超时(timeout)配置上的方式是否一样,并记录文档文件名和行号
- 调查 httpx.Client 和 AsyncClient 在代理(proxy)配置上的方式是否一样,并记录文档文件名和行号
- 调查 httpx.Client 和 AsyncClient 在 HTTP/2 配置上的方式是否一样,并记录文档文件名和行号
工人「调查 httpx.Client 和 As…」:3 次调用,3943 词元,交回 222 字
工人「调查 httpx.Client 和 As…」:3 次调用,4506 词元,交回 208 字
工人「调查 httpx.Client 和 As…」:3 次调用,4094 词元,交回 253 字
主管汇总时的输入:472 词元
共 11 次模型调用,13720 词元,8 秒
二つの最終回答の結論はほぼ同じです。HTTP/2 にはドキュメントにはっきりした根拠があり(http2.md の 50 行目「HTTP/2 support is available on both Client and AsyncClient」)、タイムアウトとプロキシの例は Client だけで書かれているので、AsyncClient で同じ引数を使えるというのは推測です。
結果を読む
トークンが 3 分の 1 少なくなった。マルチエージェントはモデルを 11 回呼び出し、単一エージェントの 5 回の倍以上ですが、トークンの合計はかえって少なくなりました。13720 対 21047 です。
理由はコンテキストの増え方にあります。単一のエージェントは第 4 ステップまで来たとき、メッセージのリストにタイムアウト、プロキシ、HTTP/2 の三つのテーマすべての検索結果とドキュメントの内容が入っていて、各ステップでその大量のものを持ってモデルを呼び出します。各作業役は自分の一つのテーマの内容しか持っていないので、コンテキストはずっと小さいままです。小さなコンテキスト三つの合計は、膨らみ続ける大きなコンテキスト一つより安いのです。
監督のコンテキストがきれい。取りまとめのとき、監督の入力はわずか 472 トークンで、それぞれ 200 字余りの結論が三つあるだけです。作業役が読んだドキュメントの原文は見えません。これがマルチエージェントの最も核心的な利点、コンテキストの分離です。各エージェントは自分が見る必要のあるものだけを見て、監督は細部に埋もれません。もっと長く複雑なタスクでは、この利点はさらにはっきりします。
時間はほぼ同じ。三つの作業役が並行して実行するので、呼び出しの合計回数は多くても、合計の所要時間(8 秒)は単一のエージェント(9 秒)とほぼ同じです。順番に実行すれば、ずっと遅くなります。
単一のエージェントの回答はまた英語で始まった。最終回答の最初の一文は「I have enough evidence. Let me summarize.」で、その後に中国語が続きます。コンテキストが英語のドキュメントでいっぱいになると、モデルの言語はずれやすく、第 4 課でも一度起きました。マルチエージェントの監督のコンテキストにはほぼ中国語の結論しかないので、この問題はありません。
代償
- 細部が失われる。作業役は 150 字の結論しか返さず、監督は原文を受け取れません。ある作業役の結論があいまいに書かれていたり、重要な出典が抜けていたりしても、監督は気づけず、まして直すことはできません。作業役に返させる形式(結論、根拠、出典、不確かな点)を明確にするほど、この問題は小さくなります。
- 分割そのものが間違うかもしれない。今回のタスクは分割しやすく、三つのテーマは互いに関係がありませんでした。サブタスクどうしに依存関係があれば、たとえば「まずエラーを出す関数を見つけ、それからその呼び出し元を見る」なら、並行して分割することはできません。
- 呼び出し回数が増え、システムが複雑になる。エージェントが一つ増えるたびに、書くべきプロンプトが一つ、見るべき実行軌跡が一本増えます。問題が起きたら、まず分割の問題か、ある作業役の問題か、取りまとめの問題かを判断しなければなりません。
いつ使うか
- サブタスクどうしが独立していて、しかもそれぞれが大量の資料を読む必要がある。コンテキストの分離でお金を節約でき、各エージェントもより集中できます。
- 長いタスクの本筋をすっきり保ちたい。Claude Code のようなコーディングエージェントは、「コードベースで何かを検索する」ようなサブタスクをサブエージェントに任せます。サブエージェントは数十のファイルを調べ尽くし、見つけた結果だけを返すので、メインの会話が検索の途中経過でいっぱいになることはありません。
- サブタスクごとに違うツールや違うプロンプトが必要。各作業役に必要なツールだけを持たせるほうが、一つのエージェントに大量のツールを持たせるより間違いにくくなります(ツールが多すぎることの問題は第 3 課で扱いました)。
ほとんどのタスクは、一つのエージェントで足ります。まず一つで始め、コンテキストが膨らみすぎるとわかったとき、あるいはタスクがもともと並行して分割できるときに、分割を検討しましょう。
練習問題
- 作業役の「150 字以内」を「50 字以内」に変えて実行し直してください。最終回答の出典はまだ完全ですか。
- 作業役に決まった形式で結論を返させてください。
結論:…… 根拠の原文:…… 出典:…… 不確かな点:……。取りまとめた回答の質は変わりましたか。 - サブタスクどうしに依存関係のある質問を設計して(たとえば「httpx で ReadTimeout を投げる関数を見つけ、それがどの公開 API から呼ばれるかを説明して」)、監督に分割させ、正しく分割できるか見てください。
確認テスト
1. マルチエージェントは単一のエージェントより呼び出し回数が多いのに、トークンの合計がかえって少なくなるのはなぜですか?
単一のエージェントのコンテキストには、すべてのサブテーマの検索結果とドキュメントがたまり続け、後のすべてのステップでその内容を持ってモデルを呼び出します。マルチエージェントでは、各作業役は自分のサブタスクの内容しか持たないのでコンテキストがずっと小さく、監督も短い結論しか見ません。小さなコンテキストいくつかの合計は、膨らみ続ける大きなコンテキスト一つより安いのです。
2. マルチエージェントでの「コンテキストの分離」とは何ですか?利点と欠点は?
各エージェントが自分に必要な情報だけを見ることです。作業役は自分のサブタスクと調べた資料だけを、監督は作業役が返した結論だけを見ます。利点は、どのコンテキストも小さく集中していてお金が節約でき、監督が細部に埋もれないことです。欠点は、引き継ぎのときに情報が失われ、作業役が返さなかった細部を監督が確認できないことです。
3. どんなタスクが、複数の作業役に分けて並行してこなすのに向いていませんか?
サブタスクどうしに依存関係のあるタスクです。後のステップが前のステップで見つけた結果に基づくなら、並行してはできず、無理に分けると後の作業役に前の情報が欠けてしまいます。また、タスク自体が簡単で、一つのエージェントが数ステップで終えられるなら、分割は呼び出し回数と複雑さを増やすだけです。
質問と議論
このレッスンでつまずいたところは、ここで質問してください。他の人の質問に答えるのも歓迎です。
質問で 3 ポイント、回答で 6 ポイント。審査を通過すると公開されます。
議論を読み込んでいます…