第3週では、ローカルLLMを使ってループ型エージェントを実装し、AIが「終わるまで考え続ける」仕組みを作りました。
これにより、AIは単発の処理ではなく、状況に応じて判断と実行を繰り返せるようになりました。
しかし実際のシステムでは、1つのAIだけですべてを判断・実行するのではなく、
役割ごとに複数のAIが分担して動く構成が一般的です。
本記事では、「司令塔」と「実行役」に分けたマルチエージェント構成を実装し、より実践的なAIエージェントの形を学びます。
AIエージェントとは
AIエージェントとは、単体で判断するだけでなく、複数の役割に分かれて連携しながら目的を達成する仕組みです。
第4週では「司令塔」と「実行役」に分けたマルチエージェント構成を実装します。
学習ロードマップ
| 週 | 内容 |
|---|---|
| 第1週 | チャット + JSON |
| 第2週 | ツール呼び出し |
| 第3週 | ループ |
| 第4週 | マルチエージェント |
作業環境について
本記事は、第1週の内容を前提として進めています。
そのため、以下の環境がすでに整っている状態を想定しています。
- ローカルLLM実行環境:Ollama
- 使用モデル:qwen3
- Python仮想環境(venv)
- ollama Pythonライブラリのインストール済み
作業環境の準備ができていない場合
第1週の記事で、環境構築からJSON出力までを解説しています。
未実施の場合は、先に第1週から進めることをおすすめします。
AIエージェント入門|ローカルLLMで学ぶチャットとJSON出力【第1週】
第4週のゴール
- AIの役割を分けて設計できる
- 複数のAIを連携させる
- より実用的な構成を理解する
なぜマルチエージェントが必要なのか?
1つのAIでは何に限界があるのか
これまでの構成では、すべてを1つのAIが担当していました。
AI → 判断 → 実行 → 繰り返し一見シンプルですが、実際の処理が増えてくると、ここに問題が出てきます。
限界①:判断が複雑になりすぎる
例えば、処理が増えると、1つのAIが、以下の「どの処理を選ぶか」の判断を毎回考える必要がでます。
- タスクを追加する
- タスクを削除する
- 優先度を変更する
- データを確認する
その結果、判断ロジックがどんどん複雑になります
限界②:役割が混ざる
1つのAIが、以下の様にすべて担当する必要があります。
- 何をするか決める
- 実行方法を考える
- 結果を評価する
その結果、「何をしているAIなのか」が曖昧になります
限界③:拡張しにくい
例えば、以下の様に機能を追加した場合、すべて1つのAIに影響する。
- 新しいツールを追加
- 新しいルールを追加
その結果、コードの修正がどんどん難しくなります
限界④:コンテキスト長の制限
LLMの制約に、コンテキスト長(コンテキストウィンドウ)の上限があります。
コンテキスト長(単位:トークン)とは、LLMが一度に扱える情報量の限界のことです。
コンテキスト長は、LLMのモデル・バージョンやシリーズによって異なります。
例えば、Qwen3-4Bでは32Kトークン・Qwen3-8Bでは128Kトークンになります。
参考:Qwen3 公式ブログ
ループ型エージェントでは、次のように会話や処理履歴を蓄積していきます。
過去の指示
↓
過去の実行結果
↓
新しい入力
↓
AIが判断しかし、履歴が増え情報量が増え、コンテキスト長の上限を超えるとLLMは処理ができなくなります。
これにより、以下の問題が生じてきます。
- 古い情報が切り捨てられる
- 文脈が欠ける
- 判断の精度が下がる
マルチエージェントでどう解決されるか
これらの問題を解決するのが、AIを役割ごとに分ける方法です。
司令塔AI(Planner) → 何をするか決める
実行AI(Worker) → 実際に処理する① 判断がシンプルになる
司令塔AIは、「何をするか」だけ考えればいい
② 役割が明確になる
司令塔 → 判断専門
実行役 → 実行専門
これにより、責任がはっきり分けることができます。
③ 拡張しやすくなる
以下の場合でも、
- 実行AIを増やす
- 新しいツールを追加
司令塔はそのまま使うことができます。
1つのAI
→ 1人で全部やる
マルチエージェント
→ チームで分担する④扱う情報を制限できる
それぞれのLLMが扱う情報量を限定することができます。
司令塔AI → 判断に必要な情報だけ持つ
実行AI → 個別の処理だけ担当これにより、以下のメリットが生じます。
- コンテキストが短くなる
- 情報の整理がしやすくなる
- 精度が安定する
マルチエージェント動作の図解

実装してみる
参考:
ollama-python/examples at main · ollama/ollama-python · GitHub
ollama/docs/api.md at main · ollama/ollama · GitHub
Multi-agent – Docs by LangChain
完成コード(簡易版マルチエージェント)
このコードは、司令塔A(planne)と実行AI(Worker)は存在するが、worker は自律的な意思決定を行わずに planner の指示を Python 関数として実行する疑似的なマルチエージェント構成です。
【第3週】のループ型エージェントに近いコードです。
Multi-loop-agent.py
from ollama import chat
def add_todo(task: str) -> str:
return f"タスクを追加しました: {task}"
TOOLS = [
{
"type": "function",
"function": {
"name": "add_todo",
"description": "ToDo追加",
"parameters": {
"type": "object",
"properties": {
"task": {"type": "string"}
},
"required": ["task"]
}
}
}
]
# 司令塔AI
def planner(messages):
return chat(
model="qwen3",
messages=messages,
tools=TOOLS,
)
# 実行AI(Worker)
def worker(tool_call):
name = tool_call["function"]["name"]
args = tool_call["function"]["arguments"]
if name == "add_todo":
return add_todo(args["task"])
messages = [
{"role": "system", "content": "あなたは司令塔AIです。"},
{"role": "user", "content": "牛乳を買うタスクを追加して"}
]
response = planner(messages)
message = response["message"]
tool_calls = message.get("tool_calls", [])
if tool_calls:
for call in tool_calls:
result = worker(call)
messages.append(message)
messages.append({
"role": "tool",
"content": result
})
final = chat(
model="qwen3",
messages=messages
)
print(final["message"]["content"])
else:
print(message.get("content", ""))AIマルチエージェント処理フローまとめ
AIによるプランニング (planner)
- ユーザーの依頼(「牛乳を買うタスクを追加して」)を受け取り、AIが「どの道具(関数)を使うべきか」を判断します。
AIの回答(指示書)を受け取る
- AIの返答から「メッセージ本体(
message)」と「道具の使用指示(tool_calls)」を取り出します。
道具を使う必要があるか判定 (if tool_calls)
- AIが「道具を使いたい」と言った場合はステップ4へ進みます。
- 「道具は不要(ただの雑談など)」と判断した場合はステップ8へ飛びます。
実際の関数を実行 (worker)
for文を使って、AIからの指示(call)を一つずつ取り出し、プログラム内の関数(add_todo)を実際に動かして結果(result)を得ます。
AIの「考え」を履歴に記録 (messages.append(message))
- 「AIがこの関数を使おうとした」という過程を、会話履歴に追加します。
関数の「実行結果」を履歴に記録 (messages.append(...))
- 実際に道具を動かして得られた結果(「追加しました」という報告など)を、
tool役として履歴に追加します。
最終回答の作成と表示 (final = chat)
- 「ユーザーの依頼」「AIの判断」「実際の実行結果」が揃った履歴をAIに送り直し、ユーザーへの丁寧な完了報告を作成して表示します。
(道具不要な場合)回答をそのまま表示 (else)
- 道具を使わなかった場合は、AIが返したメッセージ(「こんにちは」など)をそのまま画面に表示します。
注意:ollama起動タイムアウト
ollama起動後、5分間アクセスがないとプロセスが終了します。
ollama起動タイムアウトを防止するには、以下のコマンドで環境変数を変更する必要があります。
export OLLAMA_KEEP_ALIVE="-1"完成コード(自律型マルチエージェント)
このコードは、実行担当AI(Worker)が、自律的に動く本格的マルチエージェントのコードです。
Multi-loop-agent.pyからの変更点
- WorkerのLLM化:独自の
system_promptを持つ AI に変更 - エージェント間の対話:Planner はWorker に対して自然言語で指示
- 自律性: Worker は渡された指示を自分で解釈し、必要に応じてツール(
add_todo)を使用
Multi-selfdisciplin-agent.py
from ollama import chat
import json
# --- ツール(道具)の定義 ---
def add_todo(task: str) -> str:
# 実際はデータベース保存などの処理
return f"【システム】ToDoリストに「{task}」を登録しました。"
TOOLS = [
{
"type": "function",
"function": {
"name": "add_todo",
"description": "ToDoリストにタスクを追加する",
"parameters": {
"type": "object",
"properties": {
"task": {"type": "string", "description": "追加するタスクの内容"}
},
"required": ["task"]
}
}
}
]
# --- Workerエージェント(実行担当) ---
def todo_worker_agent(instruction):
print(f"\n[Worker]: 指示を受けました -> '{instruction}'")
messages = [
{"role": "system", "content": "あなたはToDo管理の専門家です。指示に従い、ツールを適切に使用してタスクを登録してください。"},
{"role": "user", "content": instruction}
]
# Workerが自分でツールを使うかどうか判断する
response = chat(model="qwen3", messages=messages, tools=TOOLS)
message = response["message"]
if message.get("tool_calls"):
for tool in message["tool_calls"]:
if tool["function"]["name"] == "add_todo":
args = tool["function"]["arguments"]
result = add_todo(args["task"])
return f"Workerの報告: {result}"
return "Workerの報告: タスクを追加できませんでした。"
# --- Plannerエージェント(司令塔) ---
def planner_agent(user_input):
print(f"\n[Planner]: ユーザーの要望を解析中...")
# Plannerは「誰に何を頼むか」を考えるのが仕事
system_prompt = (
"あなたは優秀な司令塔です。ユーザーの要望を聞き、"
"ToDo関連であれば 'todo_worker' に具体的に指示を出してください。"
"回答は必ず『依頼先: [エージェント名], 内容: [指示内容]』の形式で答えてください。"
)
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_input}
]
response = chat(model="qwen3", messages=messages)
plan = response["message"]["content"]
print(f"[Planner]: 実行プラン -> {plan}")
return plan
# --- メイン処理(エージェント同士の連携) ---
def main():
user_query = "牛乳を買うタスクを追加して"
# 1. Plannerが計画を立てる
plan_output = planner_agent(user_query)
# 2. Plannerの回答からWorkerへの指示を抽出(簡易的な判定)
if "todo_worker" in plan_output:
# 本来はここもAIがパースするのが理想的
instruction = plan_output.split("内容:")[1] if "内容:" in plan_output else user_query
# 3. Workerに仕事を依頼する
final_report = todo_worker_agent(instruction)
# 4. 最終結果を表示
print(f"\n[最終結果]: {final_report}")
if __name__ == "__main__":
main()Multi-selfdisciplin-agent.pyの実行
Git Bashで、保存したフォルダに移動してから実行します。
./.venv/Scripts/python.exe Multi-selfdisciplin-agent.py正常に動けば、AIからの返答がターミナルに表示されます。
[Planner]: ユーザーの要望を解析中...
[Planner]: 実行プラン -> 依頼先: todo_worker, 内容: 牛乳を買うタスクをToDoリストに追加してください
[Worker]: 指示を受けました -> ' 牛乳を買うタスクをToDoリストに追加してください'
[最終結果]: Workerの報告: 【システム】ToDoリストに「牛乳を買う」を登録しました。マルチエージェントの処理フロー
1. 受付:ユーザーの依頼
- ユーザー: 「牛乳を買うタスクを追加して」
user_queryとしてPlannerエージェント(司令塔)への入力となる
2. 判断:Planner(司令塔)の動き
Planner は自分の知識(システムプロンプト)の指示に従って、誰に仕事を振るべきか考えます。
system_prompt = (
"あなたは優秀な司令塔です。ユーザーの要望を聞き、"
"ToDo関連であれば 'todo_worker' に具体的に指示を出してください。"
"回答は必ず『依頼先: [エージェント名], 内容: [指示内容]』の形式で答えてください。"
)- Planner: 「ToDoの追加だと判断し、Todo担当(Worker)への指示書を作成する。」
- アウトプット: 「依頼先: todo_worker, 内容: 牛乳の購入タスクを登録せよ」という指示書を作成します。
依頼:Worker(実務担当)へ作業指示
プログラム上で、Planner が作った指示書が Worker(実行担当) に渡されます。
- 重要: Planner は自分でツール(add_todo)を使いません。あくまで「指示」を出すだけです。
4. 実行:Worker(実務担当)の動き
指示を受け取った Worker は、自分の役割(role)に従って処理を決めます。
{"role": "system", "content": "あなたはToDo管理の専門家です。指示に従い、ツールを適切に使用してタスクを登録してください。"}- Workerの判断: 「Planner からの指示が「牛乳の購入タスクを登録せよ」であるため、 add_todoツール を使う。」
- アクション: ここで Python の関数(
add_todo)が実行されます。
5.完了報告
- add_todo関数の処理結果: 「Workerの報告」
- Worker からの完了報告: 「[最終結果]:ToDoリストに「牛乳を買う」を登録しました。」
第4週のまとめ
この週では、AIエージェントを「役割ごとに分けて動かす」仕組みを学びました。
1. AIは分けて設計できる
AIは1つにまとめる必要はなく、役割ごとに分けて設計することができます。
2. 役割ごとに動かす
マルチエージェントでは、
- 司令塔(Planner) → 何をするか決める
- 実行役(Worker) → 実際に処理する
というように、判断と実行を分けて動かします。
3. これが実用的な構成
役割を分けることで、
- 処理がシンプルになる
- 拡張しやすくなる
- 安定した動作になる
システムとして使える形になります。
シリーズまとめ
- 第1週:
AIを呼ぶ(使えるようにする) - 第2週:
行動させる(処理を実行する) - 第3週:
考え続ける(ループする) - 第4週:
分担させる(役割を分ける)
【第4週】では、Planner と Worker に役割を分けたマルチエージェント構成を作り、AIを“チーム”として動かす考え方を学びました。
次の【第5週】からは、追加記事として、AIアプリと外部システムをつなぐための標準である MCP(Model Context Protocol) を学びます。
AIエージェント入門|ローカルLLMでMCP接続を学ぶ【第5週】

