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)

実行結果:

根据天气专家的查询,杭州今天晴天,气温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",  # エージェントに名前を付ける
)

方法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

マルチエージェントのアーキテクチャパターン

パターン構造適用シナリオ
コーディネーターパターン1つの親エージェント → 複数の子エージェントツールタスクの種類が明確で分類可能
リレーパターンエージェントAの出力 → エージェントBの入力パイプライン処理(生成→レビュー→推敲)
ディベートパターン複数のエージェントが並行して出力 → 集約して意思決定多角的な分析が必要な問題

マルチエージェントシステムは複雑さと Token 消費を増加させます。「マルチエージェント」のためにマルチエージェントを使うべきではありません。まずは単一のエージェント + 適切に設計された system_prompt と Middleware で問題を解決してください。本当に領域の分離や独立したコンテキストが必要な場合にのみ、マルチエージェントを導入してください。

その他の拡張