LangGraph エージェントのツール呼び出しを確認する
LangGraphを利用してモデルがどのようにツールを呼び出しているのか確認します
記事作成日:2025-11-29, 著者: Hi6
Overview
LangGraph でモデルノードとツールノードを定義し、LLM がどのようにツールを呼び出しているかを確認したいと思います。プロンプトに「ゴジラ2体 + ケンタウルス3体 - ヘビ1匹の足の合計は?」を投げてみます。動物の足の本数を取得できるツールで LLM が足の本数を取得し、その結果をつっかって、ツールで用意した計算用ツールを使用するところを確認します。
モデルの準備
from langchain_ollama import ChatOllama
model = ChatOllama(
model="qwen3-vl:8b",
temperature=0.0
)
各ツールの定義
@toolデコレータを使って各ツールを定義します。除算の場合は 0 除算でのエラーも試してみたいと思います。
各ツールが何をするのかを@tool(description="何をするのかの指示")でも指定できますが。これを指定しないとドックストリングの内容をdescriptionとして受け取ります。またツール名の指定も必要ですか指定しない場合は関数名がツール名になります。
from langchain.tools import tool, ToolException
@tool
def multiply(a: int, b: int) -> int:
"""Multiply `a` and `b`.
Args:
a: First int
b: Second int
"""
return a * b
@tool
def add(a: int, b: int) -> int:
"""Adds `a` and `b`.
Args:
a: First int
b: Second int
"""
return a + b
@tool
def divide(a: int, b: int) -> int:
"""Divide `a` and `b`.
Args:
a: First int
b: Second int
"""
if b == 0:
raise ToolException("Cannot divide by zero")
return a / b
@tool
def Sub(a: int, b: int) -> int:
"""Subtraction `a` and `b`.
Args:
a: First int
b: Second int
"""
return a - b
@tool
def search_animals_legs(name: str) -> int:
"""動物やモンスターの足の本数の辞書.
Args:
name: 動物名やモンスター
"""
match name:
case "ゴジラ":
return 2
case "ヘビ":
return 1
case "ウルトラマン":
return 2
case "スライム":
return 0
case "ケンタウルス":
return 4
case _:
return 0
# ツールをリストでまとめておく
tools = [add, multiply, divide, Sub, search_animals_legs]
# LLMにツールをバインドしてツール呼び出しができるようにしておく
model_with_tools = model.bind_tools(tools)
# 各ツールをtool.nameで呼び出せるような辞書型の作成
# {Dict[str, RunnableTool]} を作成している、tool.nameが辞書のkey
tools_by_name = {tool.name: tool for tool in tools}
モデルノードの定義
どのツールを呼び出すか選択するモデルノードを定義します。0除算に関する一文はこれを入れておかないとLLMが気を聞かせて、0で割るを0でプラスに変えて処理してしまいエラーが確認できなかったため入れています。 @taskデコレータで@toolと同じように、ドックストリングでタスクの動作を指示します。
from langchain_core.messages import BaseMessage
from langgraph.func import task
from langchain.messages import SystemMessage
@task
def call_llm(messages: list[BaseMessage]):
"""LLM はツールを呼び出すかどうかを決定します"""
return model_with_tools.invoke(
[
SystemMessage(
content="あなたは、一連の入力に対して演算を実行するという任務を負った、役に立つアシスタントです。動物やモンスターの足の本数の確認は search_animals_legs を、計算には[add, multiply, divide, Sub]使用してください。0除算によるエラー処理はツールに実装済みです。"
)
]
+ messages
)
ツールノードの定義
実際にツールをAIが用意した引数で呼び出すノードをタスク定義します。
from langgraph.func import task
from langchain.messages import ToolCall
@task
def call_tool(tool_call: ToolCall):
# ToolCall:AI がツールを呼び出すリクエストに使用する型を表します。
# {"name": "add", "args": {"a": 1}, "id": "123"}
"""ツール呼び出しを実行します"""
tool = tools_by_name[tool_call["name"]]# tool名 がキーの辞書型。
return tool.invoke(tool_call) # ツールの実行
エントリーポイントのエージェント定義
LangGraphのエントリーポイントを@entrypoint()デコレータで指定しエージェントを定義します。
from langgraph.func import entrypoint
from langchain_core.messages import BaseMessage
@entrypoint()
def agent(messages: list[BaseMessage]):
# モデルノードを呼び出してツールの利用を判断させます。
model_response = call_llm(messages).result()
# LLM がツールの利用が必要だと判断した場合、
# model_response.tool_callsに必要なモデルが
# {'name': 'search_animals_legs', 'args': {'name': 'ゴジラ'}, 'id': '123', 'type': 'tool_call'}のようなToolCall型で返却されます。
while True:
if not model_response.tool_calls:
# ツールの実行が必要ない場合、ループを抜けてそのままレスポンスを返す
break
tool_result_futures = [
# LLMが要求した、必要なツールをToolCall型の引数で全て実行します
call_tool(tool_call) for tool_call in model_response.tool_calls
]
# ツール実行の結果をメッセージに追記
# fut.result() の使用は、非同期実行の終了を待っていることを示唆します
tool_results = [fut.result() for fut in tool_result_futures]
messages = add_messages(messages, [model_response, *tool_results])
# ツールの結果とメッセージを添えてLLMの実行
model_response = call_llm(messages).result()
# 最終メッセージを追記して返す
messages = add_messages(messages, model_response)
return messages
実行
messages = [HumanMessage(content=\
"(ゴジラ2体 + ケンタウルス3体) - ヘビ1匹の足の合計は?")]
# "(ゴジラ2体 + ケンタウルス3体) / スライム1匹の足の合計は?"を利用すれば0除算の例外発生を確認できるはずです。
for chunk in agent.stream(messages, stream_mode="updates"):
# ツール呼び出しの出力
if "call_tool" in chunk:
print(f"called tool: {chunk["call_tool"].name}, \
result : {chunk["call_tool"].content}")
# 最終AI出力
elif "agent" in chunk:
chunk["agent"][-1].pretty_print()
下記が私の環境での実行結果ですが、LLMがタスクの実行に必要だと判断したツールをそれぞれ実行して情報を取得しさらにそれを利用して計算ツールを利用して計算処理をしている様子が見て取れます。
Called tool: search_animals_legs, result : 2 Called tool: search_animals_legs, result : 4 Called tool: search_animals_legs, result : 1 Called tool: multiply, result : 4 Called tool: multiply, result : 12 Called tool: add, result : 16 Called tool: Sub, result : 15 Ai Message ==================================
(ゴジラ2体 + ケンタウルス3体) - ヘビ1匹の足の合計は 15 本です。
計算の詳細:
- ゴジラ1体 = 2本 → 2体 = 2 × 2 = 4本
- ケンタウルス1体 = 4本 → 3体 = 3 × 4 = 12本
- 合計 = 4 + 12 = 16本
- ヘビ1匹を引く = 16 - 1 = 15本
[!NOTE] 参照サイト