はじめに
LangChainでエージェントを作るとき、これまでは AgentExecutor を使うのが定番でした。しかし LangChain 1.x 系をインストールした環境で from langchain.agents import AgentExecutor を実行すると、ImportError になります。対象読者は、LangChain でツール呼び出し型のエージェントを実装している開発者、および AgentExecutor ベースの既存コードを LangChain 1.x へ移行しようとしている開発者です。
この記事では、クラウド環境に実際に langchain 1.3.14 / langgraph 1.2.10 / langchain-classic 1.0.8 をインストールし、AgentExecutor(レガシー実装)と、後継の create_agent(LangGraph 実行エンジン採用)の両方を同一タスクで動かして比較した結果をまとめます。
比較表(同一タスクでの実測)
天気を返す 1 つのツール(get_weather)を呼び出すだけの単純なエージェントを、両方の実装で構築して比較しました。LLM 呼び出し部分は API キーを使わず、決まったレスポンスを返すスクリプト化モデル(後述)に差し替えています。
| 評価軸 | AgentExecutor(langchain-classic) |
create_agent(LangChain 1.x) |
|---|---|---|
| パッケージ |
langchain-classic 1.0.8 |
langchain 1.3.14(内部で langgraph 1.2.10 を利用) |
| インポート元 |
langchain.agents からは ImportError。langchain_classic.agents からのみ可能 |
langchain.agents から直接インポート可能 |
| プロンプト設計 | ReAct 形式のテキストプロンプトを PromptTemplate で手書きする必要がある |
不要(モデルのネイティブ tool calling を前提にした設計) |
| 実装コード行数(空行除く・同一タスク) | 20 行 | 8 行(60% 減) |
| 実行エンジン |
AgentExecutor 独自の実行ループ |
LangGraph の状態グラフ(Pregel ランタイム) |
| ツール呼び出しの仕組み | LLM の出力テキストを Thought: / Action: / Action Input: でパースする |
モデル API のネイティブ tool calling(bind_tools)を使う |
| メンテナンス状況 | 由来元の LangChain 0.3 系が公式に 2026 年 12 月まで MAINTENANCE mode と明言 | 現行の推奨 API |
出典: LangGraph v1 migration guide(create_react_agent プリビルトの非推奨化と create_agent への移行を案内)、Is AgentExecutor Deprecated in LangChain?(AgentExecutor を含む旧世代エージェント実装の概要)、Release policy - Docs by LangChain(LangChain 0.3 系が MAINTENANCE mode・2026 年 12 月までのサポートである旨を明記。langchain-classic はこの 0.3 系に由来する旧実装の切り出し先)
1. AgentExecutorが動かなくなった経緯を実機で確認する
まず、クラウド環境に主要パッケージをインストールしてバージョンを確認しました。
pip install -q langchain langgraph langchain-anthropic
langchain 1.3.14
langgraph 1.2.10
langchain-anthropic 1.5.3
この状態で AgentExecutor を素直にインポートすると、次のように失敗します。
from langchain.agents import AgentExecutor, create_react_agent
ImportError: cannot import name 'AgentExecutor' from 'langchain.agents'
Web上の移行ガイドには「create_react_agent プリビルトが非推奨になった」という記述はありますが、langchain.agents から AgentExecutor 自体が完全に取り除かれている点は、実際にインポートしてみるまで気づきにくい変化でした。原因を辿ると、AgentExecutor を含む旧世代のエージェント実装一式が langchain-classic という別パッケージに切り出されていました。
pip install -q langchain-classic
from langchain_classic.agents import AgentExecutor, create_react_agent
これは成功します。パッケージ構成としては、langchain 本体からレガシーコードを分離し、新規ユーザーが誤って古い実装を使わないようにする意図と読めます。
2. 同一タスクを両実装で動かす
天気を返すだけのツールを1つ用意し、同じ入力(「東京の天気は?」)に対して同じ出力(「東京は晴れです。」)を返すように、LLM 呼び出し部分を決め打ちの応答シーケンスに差し替えて実行しました。APIキーを使わずに実行フロー全体を検証するための構成です。
create_agent(新API)での実装
from langchain.agents import create_agent
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""指定した都市の天気を返す"""
return f"{city}は晴れです"
def build_new_agent(model):
return create_agent(model=model, tools=[get_weather])
ツールを渡す以外の設定はほぼありません。プロンプトの雛形はライブラリ側が持っており、モデルの bind_tools(ネイティブ tool calling)を前提にした設計になっています。この構成で agent.invoke({"messages": [{"role": "user", "content": "東京の天気は?"}]}) を実行すると、HumanMessage → AIMessage(tool_calls=...) → ToolMessage → AIMessage の順でメッセージ列が積み上がり、最終応答として「東京は晴れです。」を得られることを確認しました。
AgentExecutor(レガシー実装)での実装
from langchain_classic.agents import AgentExecutor, create_react_agent
from langchain_core.tools import tool
from langchain_core.prompts import PromptTemplate
@tool
def get_weather(city: str) -> str:
"""指定した都市の天気を返す"""
return f"{city}は晴れです"
REACT_PROMPT = PromptTemplate.from_template(
"Answer the following questions as best you can. You have access to the following tools:\n\n"
"{tools}\n\nUse the following format:\n\nQuestion: the input question\n"
"Thought: you should always think about what to do\n"
"Action: the action to take, should be one of [{tool_names}]\n"
"Action Input: the input to the action\nObservation: the result of the action\n"
"... (this Thought/Action/Action Input/Observation can repeat N times)\n"
"Thought: I now know the final answer\nFinal Answer: the final answer\n\n"
"Begin!\n\nQuestion: {input}\nThought:{agent_scratchpad}"
)
def build_legacy_agent(llm):
agent = create_react_agent(llm, [get_weather], REACT_PROMPT)
return AgentExecutor(agent=agent, tools=[get_weather], verbose=False)
こちらは ReAct 形式のプロンプトを自前で用意する必要があります。LLM は Action: get_weather\nAction Input: 東京 のようなテキストを出力し、AgentExecutor がそのテキストをパースしてツールを呼び出します。同じ「東京の天気は?」を入力すると、{'input': '東京の天気は?', 'output': '東京は晴れです。'} という辞書が返り、最終的な回答は create_agent と一致しました。
空行を除いたコード行数で比較すると、create_agent 実装が 8 行、AgentExecutor 実装が 20 行でした。差分の大部分は ReAct プロンプトの手書き部分です。
3. 実行エンジンの違い
両者の最も大きな違いは、内部の実行エンジンです。create_agent は LangGraph の状態グラフ(Pregel ランタイム)の上に構築されており、エージェントのループはノードとエッジで表現されます。
一方 AgentExecutor は、while ループの中で「LLM呼び出し → テキストパース → ツール実行」を繰り返す、より手続き的な実装です。LangGraph 化されたことで、状態の永続化(チェックポイント)やストリーミング、複数エージェントの合成がしやすくなる一方、bind_tools を実装していないモデル(テキスト出力のみのモデル)は create_agent にそのまま渡せないという制約も実測で確認しました。テスト用に bind_tools 未実装の FakeMessagesListChatModel を渡すと NotImplementedError になり、ツール呼び出し(Function Calling)に対応したモデルであることが create_agent の前提条件になっています。
4. 移行を検討する際の所見
今回の実測を通じて分かったのは、「AgentExecutor が非推奨になった」という情報だけでは移行作業の全体像がつかめない、という点です。実際には次の2段階の変化が同時に起きていました。
- パッケージの切り出し:
AgentExecutorを含む旧実装一式がlangchain-classicという独立パッケージへ移動し、langchain本体からはImportErrorになる - 実行モデルの変更: 新しい
create_agentは ReAct テキストパースをやめ、モデルのネイティブ tool calling 前提の設計に変わっている
既存コードを LangChain 1.x に移行する場合、pip install langchain-classic を追加してインポート元を書き換えるだけで動かし続けることは可能です。ただし langchain-classic は maintenance-only(重大な不具合修正のみ)と案内されているため、恒久的な対応としては create_agent への書き換えが必要になります。移行コストは、ReAct プロンプトを自前で書いていた分をそのまま削除できるため、今回検証した単純なツール呼び出しエージェントでは大きくありませんでした。ただしカスタムの Observation フォーマットや独自の停止条件を AgentExecutor に実装していた場合は、LangGraph のノード・エッジ設計に置き換える作業が発生します。
まとめ
LangChain 1.x では AgentExecutor が langchain.agents から取り除かれ、langchain-classic パッケージに切り出されています。新しい create_agent は LangGraph の実行エンジン上で動作し、モデルのネイティブ tool calling を前提とした設計です。同一タスクで比較した結果、ReAct プロンプトの手書きが不要になった分、コード行数はおよそ 6 割減りました。一方で bind_tools に対応していないモデルは create_agent に渡せないため、移行前にモデル側の tool calling 対応状況を確認しておく必要があります。
関連記事
- State of Agent Engineering 2026完全解説 — 本番導入57%・品質障壁32%の実態
- LangGraph 1.1入門 — version="v2"で実現する型安全エージェントストリーミング
- repomixほか詰め込みCLI4種を実測比較、トークン数は最大4.4倍差