AIエージェント・MCP

LangGraphのTool Callingとは|仕組みとToolNodeでの実装・create_agentとの使い分け

LangGraphのTool Callingとは|仕組みとToolNodeでの実装・create_agentとの使い分け

Tool Callingでいちばん誤解されているのは、「LLMがツールを実行してくれる機能」だと思われている点です。実際にはLLMはツールを実行しません。返すのは「このツールを、この引数で呼んでほしい」というtool_callsという要求だけで、実行するのはアプリケーション側のコードです。LangGraphはこの往復をグラフの辺として組み立てるためのフレームワークで、実行役を担うのがToolNode、往復を続けるか終えるかを判定するのがtools_conditionにあたります。この記事では、その仕組みを動くコードで示したうえで、2025年10月のv1.0でcreate_react_agentが非推奨になった件と、自前グラフとcreate_agentのどちらを選ぶかの判断基準までを扱います。

まとめ

  • Tool CallingはLLMがツールを実行する機能ではない。LLMが返すのはtool_calls(ツール名・引数・IDの組)だけで、実行と結果の差し戻しはアプリ側の責務。
  • Function Calling・Tool Use・Tool Callingは同じ仕組みの別名。OpenAIが「Function Calling」で広めた呼称が、複数ツールを前提とする「Tool Calling」に統一されつつある。
  • LangGraphでの最小構成は4点セット。@toolでツールを定義し、bind_tools()でモデルに渡し、ToolNodeで実行し、tools_conditionでループを閉じる。
  • langgraph.prebuilt.create_react_agentはv1.0で非推奨。移行先はlangchain.agents.create_agent。ただしlanggraph.prebuiltモジュール自体は現役で、ToolNodeとtools_conditionはそのまま使える。
  • ToolNodeのhandle_tool_errorsは既定で「引数が不正な呼び出し」を捕まえるが、ツール関数の中で起きた例外まで自動で握りつぶすわけではない。外部API連携では例外処理を自分で書く。

以下、モデルが実際に何を返すのかというところから順に見ていきます。

Tool Callingの仕組み:モデルはツールを実行しない

LLMは学習済みの重みからテキストを生成するだけの存在なので、今日の為替レートを引いたり、社内DBを検索したり、メールを送ったりはできません。Tool Callingは、この制約を「モデルに実行させる」のではなく「モデルに指示を出させる」ことで回避する仕組みです。LangChainの公式ドキュメントも、モデル自身はツールを実行せず、いつ呼ぶかを決めるだけだと明記しています。

モデルが返すのはtool_callsという実行要求

あらかじめツールの名前・説明・引数スキーマをモデルに渡しておくと、モデルは「このユーザー入力にはこのツールが要る」と判断したときに、通常のテキストではなく構造化された呼び出し要求を返します。LangChainではこれがAIMessage.tool_callsに入り、中身は次の3フィールドです。

フィールド 型 中身
name str 呼び出すツール名
args dict ツールに渡す引数
id str | None この呼び出しの識別子
type Literal[“tool_call”] 判別用(省略可)

このidが結果の差し戻しに効いてきます。ツールを実行したら、結果はToolMessageとして会話履歴に積み直しますが、その際tool_call_idに同じIDを入れて「どの要求への答えか」を対応付ける必要があります(tool_call_idはToolMessageの必須フィールドです)。複数のツールが同時に呼ばれたときに結果が取り違えられないのは、このIDのおかげです。ToolMessageにはstatusフィールドもあり、既定は"success"、失敗を伝えるときは"error"を入れます。

つまり1ターンの実体は、モデル呼び出し → tool_calls受領 → アプリ側で実行 → ToolMessageで差し戻し → 再びモデル呼び出し、という往復です。この往復を手で書くと煩雑になるため、LangGraphがグラフとして構造化します。

Function Calling・Tool Useとの呼称の違い

「LangGraph function calling」で検索してたどり着いた方向けに整理すると、Function CallingとTool Callingは対立する別技術ではありません。同じ仕組みに付けられた別名です。OpenAIが2023年にfunctionsパラメータで導入した際の名称が「Function Calling」で、その後、単一関数だけでなくコード実行や検索といった多様な「ツール」を扱う前提に広がり、API上もtools/tool_callsという語に置き換わりました。AnthropicはこれをTool Useと呼びます。LangChainはbind_tools()という統一インターフェースを提供し、どのプロバイダのモデルでも同じ書き方で扱えるようにしています。

OpenAI APIそのものでの実装(toolsパラメータの書式やレスポンス構造)を追いたい場合は、Function Callingとは|仕組みとOpenAI APIでの実装をコードで解説で個別に扱っています。本記事はその上に載るLangGraph側のグラフ構築に絞ります。

LangGraphでのTool Calling実装:@tool・bind_tools・ToolNode

ここからは実際に動くコードです。前提は Python 3.10以上(langgraph 1.2系・langchain 1.3系ともにrequires_python >=3.10)です。バージョンは明示的に固定してください。

pip install "langgraph>=1.2" "langchain>=1.3" langchain-openai

Python 3.9以下の環境では、pipが要件を満たす最新版を選べずlanggraph 0.6系・langchain 0.3系まで落ちます。この0.x系には後述のcreate_agentが存在せず、本記事のコードは動きません。まずpython -Vで3.10以上であることを確認してください。

ツールを定義する(@toolと型ヒント)

@toolデコレータを付けた関数が、そのままモデルに渡せるツールになります。ここで手を抜けないのが型ヒントとdocstringです。型ヒントが引数スキーマに変換され、docstringがツールの説明としてモデルに送られます。モデルはこの説明だけを手がかりに「どのツールを呼ぶか」を決めるため、docstringが曖昧だとツール選択そのものが外れます。

from langchain.tools import tool

@tool
def get_weather(city: str) -> str:
    """指定した都市の現在の天気を返す。cityは日本語の都市名で渡す。"""
    return f"{city}の天気は晴れ、気温は22度です。"

@tool
def get_exchange_rate(base: str, quote: str) -> str:
    """2通貨間の為替レートを返す。baseとquoteはISO 4217の3文字コード。"""
    return f"1 {base} = 155.2 {quote}"

「天気を調べる」のような漠然としたdocstringではなく、引数の形式まで書き切ります。上のget_exchange_rateで「ISO 4217の3文字コード」と指定しているのは、そう書かないとモデルが「日本円」「ドル」といった日本語を渡してくるためです。

モデルにツールを渡す(bind_tools)

bind_tools()でツール一覧をモデルに結び付けます。返ってくるのはツール付きの新しいモデルオブジェクトで、元のモデルは変わりません。

from langchain.chat_models import init_chat_model

tools = [get_weather, get_exchange_rate]
model = init_chat_model("openai:gpt-5.6").bind_tools(tools)

response = model.invoke("東京の天気は?")
print(response.tool_calls)
# [{'name': 'get_weather', 'args': {'city': '東京'}, 'id': 'call_abc123', 'type': 'tool_call'}]
print(response.content)
# '' (テキスト本文は空。モデルは要求を返しただけで実行はしていない)

この出力が、前章で説明した「モデルは実行しない」の実物です。contentは空で、tool_callsだけが返ります。ここで処理を止めれば、天気は永遠に取得されません。

StateGraphにToolNodeとtools_conditionを組み込む

実行役と分岐役をグラフに載せて、往復を閉じます。ToolNodeがtool_callsを受け取ってツールを実行し、結果をToolMessageにして状態へ書き戻します。tools_conditionは直前のAIMessageにtool_callsがあれば"tools"を、なければ"__end__"を返す条件関数で、これがループの出口になります。

from langgraph.graph import StateGraph, START, END, MessagesState
from langgraph.prebuilt import ToolNode, tools_condition

def call_model(state: MessagesState):
    return {"messages": [model.invoke(state["messages"])]}

builder = StateGraph(MessagesState)
builder.add_node("llm", call_model)
builder.add_node("tools", ToolNode(tools))

builder.add_edge(START, "llm")
builder.add_conditional_edges("llm", tools_condition)
builder.add_edge("tools", "llm")

graph = builder.compile()

result = graph.invoke({"messages": [{"role": "user", "content": "東京の天気と、1ドル何円か教えて"}]})
print(result["messages"][-1].content)

builder.add_edge("tools", "llm")が要です。ツール実行後に必ずモデルへ戻すことで、モデルはToolMessageを読んで「もう1本ツールが要る」か「答えを書ける」かを再判断します。この辺を張り忘れると、ツールを実行したところでグラフが終わり、ユーザーには何も返りません。tools_conditionはadd_conditional_edgesの第2引数に直接渡すだけで、行き先の辞書を省略できます。ただしこれが成立するのはツールノードの名前が"tools"のときだけです。tools_conditionの戻り値は"tools"か"__end__"という固定の文字列で、その値がそのままノード名として解決されるためです。別名を付けた場合は対応表を明示します。

builder.add_node("my_tools", ToolNode(tools))
builder.add_conditional_edges("llm", tools_condition, {"tools": "my_tools", "__end__": END})

create_react_agentは非推奨:langchain.agents.create_agentへ

ここが2026年時点でいちばん事故りやすい箇所です。日本語の解説記事の多くは、いまだにfrom langgraph.prebuilt import create_react_agentを載せています。2025年10月22日のLangChain 1.0/LangGraph 1.0で、この関数は非推奨になりました。公式リファレンスは「This function is deprecated in favor of create_agent from the langchain package」と明記し、from langchain.agents import create_agentへのimport変更を指示しています。

移行前後のコード

# 旧(非推奨)
from langgraph.prebuilt import create_react_agent
agent = create_react_agent(model, tools)

# 新
from langchain.agents import create_agent
agent = create_agent("openai:gpt-5.6", tools)

result = agent.invoke({"messages": [{"role": "user", "content": "東京の天気は?"}]})

単なる改名ではありません。create_agentはミドルウェア機構を持ち、before_model/wrap_tool_call/after_modelといったフックでエージェントの各段に処理を差し込めます。人間の承認を挟む、履歴を要約する、PIIをマスクする、といった要求は、以前ならグラフを自前で組み直す必要がありましたが、いまはミドルウェアで足ります。LangChain v1.0の概要と新機能:v0.xからの刷新点・新要素を含むアップデート全体像で刷新点の全体像を扱っています。

langgraph.prebuiltは現役(ToolNodeとtools_conditionは残る)

「create_react_agentが非推奨」を「langgraph.prebuiltが廃止」と読み違えないでください。非推奨になったのはcreate_react_agentという関数ひとつです。langgraph.prebuiltモジュール自体は現役で、ToolNode・tools_condition・InjectedState・InjectedStore・ValidationNode・ToolRuntime・ToolCallTransformerを引き続き提供します。前章のStateGraph実装はそのまま有効です。

依存の向き自体も、この1.0で読み替えが必要になりました。かつて「LangGraphはLangChainの上に構築された拡張」と説明されていましたが、現在は逆で、LangChainのcreate_agentがLangGraphランタイム上で動きます。両者の関係はLangChainとLangGraphの違い|v1.0で逆転した関係と使い分けで整理しています。

ToolNodeのエラー処理と並列実行

ここは本番投入時に効いてくる挙動で、公式リファレンスを読まないと気付きにくい部分です。

handle_tool_errorsの既定は「引数不正」を拾うもの

ToolNodeのシグネチャは以下です。

ToolNode(
    tools,
    *,
    name: str = "tools",
    tags: list[str] | None = None,
    handle_tool_errors=...,   # 既定は内部の _default_handle_tool_errors
    messages_key: str = "messages",
    wrap_tool_call=None,
    awrap_tool_call=None,
)

handle_tool_errorsの既定が拾うのは、モデルが不正な引数を渡したこと(tool invocation errors)に起因する失敗です。エラー内容がToolMessageとしてモデルへ返され、モデルは引数を直して呼び直せます。ここを「ツール内で起きたあらゆる例外を自動で握ってくれる」と読むと危険です。実装のdocstringは、ツール実行時のエラーは無視して再送出する(Ignores tool execution errors — they will be re-raised)と明言しています。外部APIのタイムアウトや認証失敗といった実行時の例外は、ツール関数の側で処理するのが前提です。

import httpx
from langchain.tools import tool

API_ENDPOINT = "https://api.example.com/quote"

@tool
def get_stock_price(symbol: str) -> str:
    """銘柄コードから現在株価を返す。symbolはティッカーシンボル。"""
    try:
        resp = httpx.get(API_ENDPOINT, params={"symbol": symbol}, timeout=5.0)
        resp.raise_for_status()
    except httpx.HTTPError as e:
        return f"株価を取得できませんでした({type(e).__name__})。銘柄コードを確認してください。"
    return f"{symbol}: {resp.json()['price']}円"

例外を文字列で返しているのは、モデルに読ませて次の判断をさせるためです。例外を送出したままにすると、グラフ全体が落ちてユーザーには何も返りません。挙動を明示的に変えたいときは、handle_tool_errorsにTrue(全捕捉)、False(無効化)、文字列(その文字列を固定のエラーメッセージとして返す)、例外クラスやそのタプル(捕捉する例外を限定)、コールバック関数(エラーから返却メッセージを組み立てる)のいずれかを渡します。

複数のtool_callsは並列で実行される

1つのAIMessageにtool_callsが複数入っていた場合、ToolNodeはそれらを並列に実行します。同期実行(invoke)ではThreadPoolExecutor、非同期実行(ainvoke)ではasyncioで走ります。冒頭のコード例で「東京の天気と、1ドル何円か教えて」と聞いたとき、get_weatherとget_exchange_rateは逐次ではなく同時に走り、両方のToolMessageが揃ってからモデルに戻ります。

この設計は速い代わりに、ツール関数がスレッドセーフである必要があることを意味します。モジュールレベルのグローバル変数を書き換える、スレッドセーフでないDBクライアントを共有する、といった実装は競合します。外部APIのレート制限も、想定より短時間に呼び出しが集中する前提で設計してください。

自前グラフとcreate_agentのどちらを選ぶか

ここは立場を明確にします。大半のケースでは、StateGraphを自前で組む必要はありません。 前章までで示したStateGraph+ToolNode+tools_conditionの構成は、create_agentが内部でやっていることとほぼ同じです。「LangGraphを使っている感」を出すために自前でグラフを書くのは、保守すべきコードを増やしているだけです。

自前グラフに倒すべき条件は、次のいずれかに当てはまるときに限られます。

  • ツール実行の前後に、エージェントのループとは無関係なノード(バッチ処理、別モデルによる検証、外部ワークフローへの受け渡し)を挟む必要がある。
  • ツール結果によってエージェント自体を分岐させ、複数の異なるエージェントへルーティングする(マルチエージェント構成)。
  • 条件分岐がtools_conditionの「tool_callsがあるか否か」では表現できない(たとえば特定ツールの結果値でループを打ち切る)。

逆に、「ツールを呼んで答えを返す」だけのエージェントに自前グラフは不要です。人間の承認を挟みたい、履歴を要約したい、といった要求もミドルウェアで足りるため、それを理由にグラフを書き起こす判断は現在では古くなっています。検索と組み合わせて回答精度を上げたい場合の構成はAgentic RAGのアーキテクチャと構成要素を詳しく解説が近く、できあがったエージェントを運用環境に載せる段階ではLangGraph Platform(旧 LangGraph Cloud)とは?デプロイ手順・Studio・料金を実践解説【2026年版】を参照してください。

よくある質問

Tool CallingとFunction Callingは何が違いますか?

仕組みは同じで、呼称の違いです。OpenAIが2023年に導入したfunctionsパラメータの名称が「Function Calling」で、複数ツールを扱う前提へ拡張される過程でAPI上もtools/tool_callsに置き換わり、業界的にはTool Callingという呼び方が一般化しました。AnthropicはTool Useと呼びます。LangChainではbind_tools()に統一されているため、モデルを差し替えてもコードは変わりません。

create_react_agentはもう使えないのですか?

非推奨ですが、langgraph 1.2.9時点で削除はされておらず、まだ動きます。削除される版は公式に告知されていません(非推奨の分類は「v1.0以降」であることのみ示されています)。ただし公式はfrom langchain.agents import create_agentへの移行を指示しているため、新規実装で使う理由はなく、2.0での削除に備えて早めに移行しておくのが安全です。既存コードはcreate_agentへ置き換えたうえで、承認フローや履歴要約はミドルウェアに寄せるのが移行の筋です。

ToolNodeを使うとツールのエラーは自動で処理されますか?

されるのは、モデルが不正な引数で呼んだケースだけです。既定のhandle_tool_errorsはその失敗をToolMessageとしてモデルに差し戻し、呼び直させます。ツール関数の内部で起きたHTTPエラーやタイムアウトは対象外なので、ツール側で捕捉してエラー内容を文字列で返してください。

ツールが同時に複数呼ばれたときの順序は制御できますか?

ToolNodeは同一AIMessage内のtool_callsを並列に実行するため、実行順序は保証されません。順序に依存する処理(Aの結果を使ってBを呼ぶ)は、1つのツールにまとめるか、グラフのノードを分けて逐次のエッジで結びます。並列実行される以上、ツール関数はスレッドセーフに書く必要があります。

LangGraphのTool Callingに対応しているモデルは?

bind_tools()を実装しているチャットモデルであれば使えます。OpenAI、Anthropic、Google、Amazon Bedrock経由の各モデルなど主要プロバイダは対応しています。ツール呼び出しに未対応のモデルではエラーになるため、ローカルLLMを使う場合は、そのモデルがツール呼び出し用にファインチューニングされているかを事前に確認してください。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.07 テックブログ ファインズの不正アクセスと153万件の予約者情報|予約システムを使う店舗の報告と通知の手順
  2. 2026.10.15 テックブログ 銀のさら不正アクセス:最大650万件の対象確認と窓口、パスワード変更・退会の手順
  3. 2026.10.19 テックブログ コミックシーモア不正アクセスで会員情報流出:再開時期・パスワード再設定と運営側のログ点検
  4. 2026.10.16 テックブログ バーガーキング情報漏洩どうしたらいい?約1,118万件の対象確認・補償・退会とメールの真偽
  5. 2026.10.14 テックブログ ブックオフ不正アクセス:約638万人の対象確認と本物メールの見分け方

RELATED POSTS 関連記事

目次