LangChain マルチエージェント
タスクが複雑すぎて単一のエージェントでは対応できない場合、それぞれ役割を持つ複数のエージェントを作成し、チームのように協力させることができます。
なぜマルチエージェントが必要か
単一エージェントの問題:
- system_prompt が長すぎるとモデルの注意力が散漫になる
- ツールが多すぎるとモデルがツールを選択する際のエラー確率が上がる
- 異なるタイプのタスクには異なる専門知識と行動スタイルが必要
マルチエージェントの解決策:各エージェントが1つの領域に特化し、協力して複雑なタスクを完了します。
方法1:子エージェントをツールとして使用
エージェントを CompiledStateGraph にコンパイルし、それをツールとして親エージェントに登録します:
例
from dotenv import load_dotenv
load_dotenv()
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 子エージェント1:天気専門家
@tool
def get_weather(city: str) -> str:
"""天気を調べる"""
data = {"杭州": "晴れ、25°C", "北京": "曇り、18°C"}
return data.get(city, f"{city}: データがありません")
weather_agent = create_agent(
model=model,
tools=[get_weather],
name="weather_expert", # 名前は識別とログに使用される
system_prompt="あなたは天気の専門家で、天気に関する質問に答えます。回答は簡潔にしてください。",
)
# 子エージェント2:計算専門家
@tool
def calculate(expression: str) -> str:
"""数式を計算する"""
result = eval(expression, {"__builtins__": {}}, {})
return f"{expression} = {result}"
math_agent = create_agent(
model=model,
tools=[calculate],
name="math_expert",
system_prompt="あなたは数学の専門家で、数学の計算を行います。回答は簡潔にしてください。",
)
# 親エージェント:コーディネーター
# 子エージェントをツールとして登録
@tool
def ask_weather_expert(question: str) -> str:
"""天気の専門家に天気関連の質問を相談します。
Args:
question: 天気に関する質問
"""
result = weather_agent.invoke(
{"messages": [HumanMessage(content=question)]}
)
return result["messages"][-1].content
@tool
def ask_math_expert(question: str) -> str:
"""数学の専門家に数学の計算問題を相談します。
Args:
question: 数学の計算問題
"""
result = math_agent.invoke(
{"messages": [HumanMessage(content=question)]}
)
return result["messages"][-1].content
coordinator = create_agent(
model=model,
tools=[ask_weather_expert, ask_math_expert],
system_prompt="""あなたは調整アシスタントです。ユーザーの質問に応じて適切な専門家を選択してください:
- 天気関連の問題 → ask_weather_expert を使用
- 数学の計算問題 → ask_math_expert を使用
- 複数の分野にまたがる場合は、各専門家に順番に相談してください""",
)
# 複合問題をテスト
result = coordinator.invoke({
"messages": [HumanMessage(
content="杭州の今日の天気はどうですか?温度が25度の場合、華氏に換算すると何度ですか?"
"(公式:華氏 = 摂氏 × 9/5 + 32)"
)]
})
print(result["messages"][-1].content)
load_dotenv()
from langchain.tools import tool
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
from langchain.messages import HumanMessage
model = init_chat_model("deepseek:deepseek-v4-flash", temperature=0)
# 子エージェント1:天気専門家
@tool
def get_weather(city: str) -> str:
"""天気を調べる"""
data = {"杭州": "晴れ、25°C", "北京": "曇り、18°C"}
return data.get(city, f"{city}: データがありません")
weather_agent = create_agent(
model=model,
tools=[get_weather],
name="weather_expert", # 名前は識別とログに使用される
system_prompt="あなたは天気の専門家で、天気に関する質問に答えます。回答は簡潔にしてください。",
)
# 子エージェント2:計算専門家
@tool
def calculate(expression: str) -> str:
"""数式を計算する"""
result = eval(expression, {"__builtins__": {}}, {})
return f"{expression} = {result}"
math_agent = create_agent(
model=model,
tools=[calculate],
name="math_expert",
system_prompt="あなたは数学の専門家で、数学の計算を行います。回答は簡潔にしてください。",
)
# 親エージェント:コーディネーター
# 子エージェントをツールとして登録
@tool
def ask_weather_expert(question: str) -> str:
"""天気の専門家に天気関連の質問を相談します。
Args:
question: 天気に関する質問
"""
result = weather_agent.invoke(
{"messages": [HumanMessage(content=question)]}
)
return result["messages"][-1].content
@tool
def ask_math_expert(question: str) -> str:
"""数学の専門家に数学の計算問題を相談します。
Args:
question: 数学の計算問題
"""
result = math_agent.invoke(
{"messages": [HumanMessage(content=question)]}
)
return result["messages"][-1].content
coordinator = create_agent(
model=model,
tools=[ask_weather_expert, ask_math_expert],
system_prompt="""あなたは調整アシスタントです。ユーザーの質問に応じて適切な専門家を選択してください:
- 天気関連の問題 → ask_weather_expert を使用
- 数学の計算問題 → ask_math_expert を使用
- 複数の分野にまたがる場合は、各専門家に順番に相談してください""",
)
# 複合問題をテスト
result = coordinator.invoke({
"messages": [HumanMessage(
content="杭州の今日の天気はどうですか?温度が25度の場合、華氏に換算すると何度ですか?"
"(公式:華氏 = 摂氏 × 9/5 + 32)"
)]
})
print(result["messages"][-1].content)
実行結果:
根据天气专家的查询,杭州今天晴天,气温25°C。 换算成华氏度:25 × 9/5 + 32 = 77°F。 所以杭州今天25°C,相当于77°F,天气晴好。
方法2:name パラメータでエージェントを区別
子エージェントをツールとして組み込むとき、name パラメータを設定するとメッセージの出所を追跡しやすくなります:
例
# name パラメータの役割:
# 1. コンパイル後のグラフでこの名前が使用される
# 2. サブグラフノードとして親グラフに埋め込まれるときにこの名前が使用される
# 3. すべての AI メッセージがこの名前でマークされる
agent = create_agent(
model=model,
tools=[...],
name="customer_service", # エージェントに名前を付ける
)
# 1. コンパイル後のグラフでこの名前が使用される
# 2. サブグラフノードとして親グラフに埋め込まれるときにこの名前が使用される
# 3. すべての AI メッセージがこの名前でマークされる
agent = create_agent(
model=model,
tools=[...],
name="customer_service", # エージェントに名前を付ける
)
方法3:Middleware でエージェントルーティングを実装
より複雑なマルチエージェントのシナリオでは、Middleware を使用して動的ルーティングを実装できます:
例
from langchain.agents.middleware import before_model
# 異なる専門家が使用するツールセットを定義
general_tools = [tool_a, tool_b]
admin_tools = [tool_c, tool_d]
@before_model
def route_by_user_role(state, runtime):
"""ユーザーの役割に応じて利用可能なツールを動的に切り替える"""
context = runtime.context
if context is None:
return None
user_role = context.get("user_role", "user")
# 役割によって表示されるツールが異なる
if user_role == "admin":
available_tools = general_tools + admin_tools
else:
available_tools = general_tools
# 注意:before_model は tools を直接変更できない。
# wrap_model_call または request.override と組み合わせて実装する必要がある
return None
# 異なる専門家が使用するツールセットを定義
general_tools = [tool_a, tool_b]
admin_tools = [tool_c, tool_d]
@before_model
def route_by_user_role(state, runtime):
"""ユーザーの役割に応じて利用可能なツールを動的に切り替える"""
context = runtime.context
if context is None:
return None
user_role = context.get("user_role", "user")
# 役割によって表示されるツールが異なる
if user_role == "admin":
available_tools = general_tools + admin_tools
else:
available_tools = general_tools
# 注意:before_model は tools を直接変更できない。
# wrap_model_call または request.override と組み合わせて実装する必要がある
return None
マルチエージェントのアーキテクチャパターン
| パターン | 構造 | 適用シナリオ |
|---|---|---|
| コーディネーターパターン | 1つの親エージェント → 複数の子エージェントツール | タスクの種類が明確で分類可能 |
| リレーパターン | エージェントAの出力 → エージェントBの入力 | パイプライン処理(生成→レビュー→推敲) |
| ディベートパターン | 複数のエージェントが並行して出力 → 集約して意思決定 | 多角的な分析が必要な問題 |
その他の拡張マルチエージェントシステムは複雑さと Token 消費を増加させます。「マルチエージェント」のためにマルチエージェントを使うべきではありません。まずは単一のエージェント + 適切に設計された system_prompt と Middleware で問題を解決してください。本当に領域の分離や独立したコンテキストが必要な場合にのみ、マルチエージェントを導入してください。