ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

LangChain导入报错:create_tool_calling_agent版本错配修复指南

LangChain导入报错:create_tool_calling_agent版本错配修复指南

1. 报错现场还原:这个 ImportError 到底卡在哪一步

前几天有位做 Agent 开发的同行拿着一条报错来问我,说项目一跑起来就崩在导入语句上:ImportError: cannot import name 'create_tool_calling_agent' from 'langchain.agents'。代码是照着最新教程一行一行敲的,环境也是刚配的,结果连 import 都过不去。这个报错我在技术群里见过很多回,基本可以断定是 LangChain 版本错配的经典翻车现场。如果你正在用 LangChain 做工具调用式 Agent,或者刚在网上找了一个带create_tool_calling_agent的示例,那么这篇内容就是给你准备的。下面我会从报错的真实含义讲起,带你把版本、环境和导入路径挨个查一遍,最后给出三条能落地的修复路线,顺便把修完之后大概率会撞上的几个连环坑一起讲清楚。

1.1 完整的报错信息怎么看

先还原一下现场。你在终端里运行脚本,拿到的报错通常是这么一大段:

Traceback (most recent call last): File "main.py", line 5, in <module> from langchain.agents import create_tool_calling_agent ImportError: cannot import name 'create_tool_calling_agent' from 'langchain.agents' (/usr/local/lib/python3.10/site-packages/langchain/agents/__init__.py)

ImportError和ModuleNotFoundError有本质区别。ModuleNotFoundError是说模块整体不存在,比如from langchain import agents时找不到langchain;而ImportError的报错信息里明确写明了来源文件——langchain/agents/__init__.py,意思是这个模块被 Python 找到了,但模块里根本拿不出create_tool_calling_agent这个名字。换句话说,报错不是"你的代码把路径写错了",而是"你装的那个版本的 langchain 里面压根没有这个函数"。

__init__.py是 Python 包的对外名片,from langchain.agents import xxx本质上就是从__init__.py里查名字。报错路径里出现了它,就说明导入链路本身是通的,问题出在"版本太老,名片上没印这个函数"。

1.2 这个函数是什么时候加进 LangChain 的

create_tool_calling_agent是 LangChain 在 0.2.x 中期(我印象里是 0.2.5 前后)加入的导出函数,专门用来构建"模型原生支持工具调用"的 Agent。它的设计目标是让大模型通过函数调用机制直接决定调用哪个工具、传什么参数,而不是靠提示词让模型输出一大段文本再自己解析。

如果你装的是 0.1.x 这个分支,langchain.agents里只有initialize_agent、create_react_agent这类老接口,自然找不到create_tool_calling_agent。问题在于,很多 2024 年下半年之后写的教程和示例代码默认大家用的是最新版,不会在开头特意标注"需要 langchain>=0.2.5"。于是就会出现标题里这种场面:代码正确、意图正确,就是环境版本对不上。

1.3 最容易踩中这个坑的三类人

根据我看过的提问记录,撞上这个报错的基本是三种情况。

第一种是跟着新教程做 Agent 项目的新手,所在环境里 langchain 是某个旧项目或者虚拟环境预装好的,版本还停留在 0.1.x。第二种是在线 Notebook 或共享环境的用户,环境里预装的 langchain 版本不是最新,教程却要求 0.2.x。第三种是公司内网用镜像源安装依赖的开发者,镜像里的 langchain 包没有及时同步,装出来的是旧版,pip install langchain不会报错,因为旧版本确实存在,只是没有你要的函数。

先判断自己属于哪一类,对后面怎么修很关键:前两类直接升级就行,第三类还得考虑镜像源缓存的问题。

2. 根源排查:版本号、导入路径和环境三连问

知道大概方向之后,别急着pip install -U一把梭,先花两分钟把三个问题查清楚,能省掉后面很多反复。很多人在这一步就会发现,报错根本不是你以为的那个原因。

2.1 第一步:确认你实际装的是哪个版本

在项目目录下跑这三条命令,把输出对照一下:

python -m pip show langchain python -c "import langchain; print(langchain.__version__)" pip list | grep -i langchain

pip show给的是元数据里的版本号,python -c给出的是解释器实际加载的版本号,pip list则把所有相关包都列出来。正常情况下这三个结果应该一致。只要看到版本号是0.1.x,原因就锁定了一大半:版本太旧,函数还没出生。

这里我特意用了python -m pip show而不是直接写pip show,是有讲究的。python -m pip保证了 pip 和 python 指向同一个安装目录,不会出现"终端里的 pip 是 A 环境的,python 却是 B 环境的"这种精神分裂情况。如果你环境里同时有 conda 和系统 Python,这个细节尤其重要。

2.2 第二步:确认 Python、pip 和 Notebook 内核是不是同一个环境

如果上面查出来版本是 0.2.x 甚至 0.3.x,但还是报 ImportError,那基本可以断定是"环境不一致"。最常见的情况是:你在终端用pip install把包装到了一个环境,但运行代码的解释器是从另一个环境启动的。

which python which pip python -c "import sys; print(sys.executable)"

三条命令的输出路径应该指向同一个解释器。如果你用的是 Jupyter Notebook,还得额外确认内核用的是哪个解释器:

python -m ipykernel list

如果列表里显示的内核跟你在终端装包用的解释器不是同一个,Notebook 里 import 不到新装的包就非常正常了。这时候要么在内核对应的环境里重新安装,要么在 Notebook 里用%pip install让它自动对准当前内核的环境。

为了让你查得更顺手,我把这个阶段常用的判别逻辑整理成了一张表:

现状最可能原因该做的验证
版本号显示 0.1.x包太旧直接走升级流程
pip show 是最新版,import 仍报错多环境混用对比 which python 与 pip 路径
版本号 0.2.x 以上,报错仍在文件遮蔽或缓存检查项目目录、清缓存
换个目录运行就正常本地文件遮蔽检查当前目录下是否有同名模块

2.3 第三步:排出本地文件遮蔽和缓存干扰

环境没问题、版本也对,结果还是在报错,那就要怀疑 Python 把别的"同名文件"当成 langchain 导入了。最典型的元凶是项目目录下恰好有一个langchain.py或者langchain目录。Python 的导入顺序里,当前目录的优先级很高,你写from langchain.agents import ...时,解释器可能优先读到了自己的那个同名文件,自然找不到函数。

排查方法很简单:

ls -la | grep -i langchain find . -name "langchain.py" -type f echo $PYTHONPATH

如果确实存在同名文件,把它改名或者移出项目目录即可。另外,升级完版本之后如果还是老报错,试试清掉字节码缓存:

find . -name "__pycache__" -type d -exec rm -rf {} +

这一招尤其适合那种"明明升级了,代码还是走旧逻辑"的灵异现象。旧的.pyc缓存有时会让人误以为版本没变。

3. 修复实操:升级依赖,以及两条备用路线

排查完成,原因基本水落石出,接下来就是动手修复。我给三条路线:直接升级、换兼容 API、写兼容层。绝大多数人走第一条就够了。

3.1 推荐方案:升级 LangChain 全家桶

既然create_tool_calling_agent在 0.2.x 才加入,最简单的方案就是升级到最新稳定版。需要注意的是,LangChain 在 0.2.x 之后做了一次包拆分,光升级langchain本体不够,模型封装、社区工具都在单独的包里,最好一起更新:

python -m pip install -U langchain langchain-openai langchain-community

这里解释一下为什么要装三个包。0.2 版本之后,LangChain 把核心依赖做了解耦:langchain-core是底层抽象,langchain是链和 Agent 编排层,langchain-openai、langchain-anthropic这类是各家模型的适配层,langchain-community则是社区维护的各种工具集成。create_tool_calling_agent本身在langchain包里,但你要让它跑起来,还得有真正的模型对象、工具实例,所以干脆全家桶一起升。

如果在公司内网或镜像源环境下,升级时可能因为缓存拿到旧包,建议加参数强制拉取最新:

python -m pip install --no-cache-dir -U langchain langchain-openai langchain-community

装完验证一下,能正常 import 就说明第一步过了:

python -c "from langchain.agents import create_tool_calling_agent; print('import ok')"

3.2 修复后可以跑的完整示例

升级完成之后,一个最小的工具调用 Agent 长这样。我把当时给那位同行演示的代码贴在这里,你直接把your_tool换成自己的工具函数就能跑通:

from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate # 1. 模型:必须是支持 tool calling 的模型 llm = ChatOpenAI(model="gpt-4o", temperature=0) # 2. 工具:你自己定义的工具列表,比如 @tool 装饰的函数 tools = [your_tool] # 3. 提示词:注意必须有 agent_scratchpad 占位符 prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个乐于助人的助手,需要调用工具时请直接调用。"), ("human", "{input}"), ("placeholder", "{agent_scratchpad}"), ]) # 4. 组装 Agent agent = create_tool_calling_agent(llm, tools, prompt) executor = AgentExecutor(agent=agent, tools=tools, verbose=True) # 5. 调用 result = executor.invoke({"input": "帮我查询一下天气"}) print(result)

这段代码里最值得注意的地方在第 3 步:{agent_scratchpad}是 Agent 的"草稿纸",用于存放多轮工具调用之间的历史思考过程。没有它,Agent 框架没法把"上一次工具调用的结果"回写给模型。这个占位符必须是空的,不要在invoke的输入参数里手动传agent_scratchpad,否则会跟框架内部注入冲突。这块细节是修完版本问题后最常见的第二个坑,我放在第四章专门讲。

3.3 备用路线 A:不想升级就改用 create_react_agent

如果因为某些原因不能升级——比如公司内部锁定了旧版本依赖、或者你的模型根本不支持原生工具调用——那就换一个老版本里就有的接口:create_react_agent。它走的是 ReAct 路线,让模型通过"思考-行动-观察"的文本循环来调用工具,不需要模型具备原生 function calling 能力,所以在老版本和新版本的兼容性上都更好。

from langchain.agents import create_react_agent, AgentExecutor from langchain import hub # ReAct 需要一套推理提示词模板 prompt = hub.pull("hwchase17/react") agent = create_react_agent(llm, tools, prompt) executor = AgentExecutor(agent=agent, tools=tools, verbose=True) result = executor.invoke({"input": "帮我查询一下天气"})

create_react_agent的提示词模板通常要包含{tools}、{tool_names}、{agent_scratchpad}这些变量,直接拉取社区维护好的模板最省事。它跟create_tool_calling_agent相比,对模型要求更低,但对 prompt 质量更敏感,因为模型需要自己从文本里理解工具列表和格式约束。如果你用的是不支持 tool calling 的模型,这条路是唯一的选择。

3.4 备用路线 B:写兼容层,让代码两边都能跑

还有一种场景比较特殊:你写的不是一次性脚本,而是一个要被很多人用的库,必须同时兼容不同用户的 langchain 版本。此时可以在导入处做一次 try/except 降级:

try: from langchain.agents import create_tool_calling_agent AGENT_IMPL = "tool_calling" except ImportError: from langchain.agents import create_react_agent as create_tool_calling_agent AGENT_IMPL = "react"

但要提醒一句:这个兼容层只能帮你把"导入"这关过了,后面组装 Agent 时两个接口对 prompt 的要求、对模型能力的要求都不一样,你还是要根据AGENT_IMPL分支写不同的组装逻辑。所以它适合作为库的兜底,不适合作为写业务代码的偷懒手段。若是你自己的项目,升级永远比兼容简单。

4. 修完这个错之后,紧接着会来的三个连环坑

版本问题修好,导入语句顺利通过,你以为就结束了?以我帮人排查的经验,后面还有三个坑在排队。这几个坑几乎每个用create_tool_calling_agent的新手都会踩一遍,提前了解能省掉大把查资料时间。

4.1 Prompt 里缺 agent_scratchpad 占位符

很多人在升级完依赖、照抄了教程的 import 语句后,写出来的 prompt 是这样的:

prompt = ChatPromptTemplate.from_messages([ ("system", "你是一个助手"), ("human", "{input}"), ])

然后运行时报错,内容多半是关于agent_scratchpad的KeyError或者提示 "must contain agent_scratchpad"。原因我在 3.2 里提过:create_tool_calling_agent返回的是一个 Runnable 链,框架在内部会读取agent_scratchpad并把历史工具调用的中间结果填进去。prompt 模板里没有这个占位符,格式化阶段就会立刻失败。

正确做法是加一行("placeholder", "{agent_scratchpad}")。为什么用placeholder而不是human或system?因为placeholder类型允许在运行期动态插入任意消息列表,而human类型的消息会被强制当成普通用户输入,无法承载多轮工具调用记录。这个细节不直观,但踩过一次就记住了。

4.2 模型本身不支持 Tool Calling

第二个坑来得更隐蔽。create_tool_calling_agent的名字已经说明了它对模型的要求:模型必须原生支持 tool calling(也叫 function calling)。如果你在代码里换了一个不具备该能力的模型,导入阶段不会报错,运行阶段会抛出类似ValueError的错误,提示当前模型无法使用 tool calling。

这不是代码问题,是模型能力边界问题。常见支持 tool calling 的模型包括 GPT 系列、Claude 系列,以及不少国内外大模型厂商的 API;某些只做文本补全的模型就真的不行。判断方法很简单:看模型厂商文档里有没有标明 function calling 支持,或者直接在代码里传{"input": "你好"}让它随便跑一次,看看框架报错信息里有没有 "tool calling not supported" 之类的关键词。如果模型不支持,就老老实实回到 3.3 的create_react_agent路线。

4.3 AgentExecutor 会用,但已经开始有被替代的苗头

最后一个坑不是报错,而是方向问题。LangChain 近两年在力推 LangGraph 的新一代 Agent 写法,create_tool_calling_agent+AgentExecutor这套组合虽然还能用,但在新版本里已经能感觉到被边缘化的趋势。新项目直接去了解 LangGraph 的create_agent会省掉以后的迁移成本,而老项目如果跑得稳定,暂时也不用动,等彻底弃用再说。

我的建议是:如果是为了快速验证想法、跑通 Demo,create_tool_calling_agent依然是最短路径;如果是正经做一个长期维护的线上项目,花点时间看 LangGraph 的 Agent 写法更值得。这个取舍我在 5.4 里再具体讲。

5. 从这次报错反推:几条版本管理上的实在经验

报错本身三分钟就能修好,但"为什么会报错"才是真正值得复盘的地方。整理几条我踩过坑之后沉淀下来的经验,希望对你有用。

5.1 虚拟环境是真正的护身符

我见过太多人把包直接装进系统 Python,结果系统环境里几十个项目互相打架,今天升级 A 项目搞挂了 B 项目。Python 的依赖是全局共享的,没有虚拟环境隔离,版本管理就是一句空话。用 venv 或者 conda 为每个项目建独立环境,是避免类似 ImportError 的最根本手段:

python -m venv .venv source .venv/bin/activate python -m pip install langchain langchain-openai

虚拟环境的好处不仅在于隔离,还在于可以随时一键销毁重建。遇到依赖乱成一锅粥的情况,删掉重装比手工梳理快得多。

5.2 项目里锁版本,不要只写包名

很多项目的依赖清单只写包名不写版本,比如只写一个langchain,这就把"哪天装出来是什么版本"交给运气了。今天的langchain和半年前的langchain完全是两个东西,API 变化堪称天翻地覆。正确做法是在依赖文件里写上版本范围:

langchain>=0.2.5,<0.4 langchain-openai>=0.1.0 langchain-community>=0.2.0

上界<0.4是为了防止未来大版本接口变化导致意料之外的破坏。锁版本的同时,README 里最好再写一行"用什么命令安装",连--no-cache-dir这种镜像源踩坑点也标上,能帮后来的人节约大量时间。

5.3 看教程先看发布时间和版本要求

互联网上的 LangChain 教程数量极其庞大,但大多数都没有标注版本。很多教程发布于 0.1 时代,代码用的initialize_agent、LLMChain,现在拿出来跑自然一堆报错。反过来,2024 年下半年的新教程用create_tool_calling_agent,你拿旧环境跑,就是本文标题这个报错。

我的习惯是:找到一个示例后,先看文章发布日期,再看它 import 了哪些模块,然后对照自己环境里pip show langchain的版本号,心里大概有个数再动手。官方文档的版本切换器也是一个好工具,看 API 文档时可以切成和项目一致的版本,避免被最新文档误导。

5.4 一套可以复制到任何环境的排查清单

最后把这篇文章里的排查流程压缩成一张清单,下次再遇到任何 "cannot import name xxx from yyy" 的报错,都可以照这个顺序走:

  1. 用python -m pip show 包名确认实际版本,和教程要求的版本做对比;
  2. 用which python和which pip确认环境一致,Notebook 还要查内核;
  3. 用ls检查项目目录有没有同名文件遮蔽;
  4. 清__pycache__缓存,加--no-cache-dir重装;
  5. 把报错的完整信息(包括来源文件路径)贴到搜索框,重点看路径指向哪个包的哪个文件。

就我个人经验来说,最后这一条往往是最快的。完整报错信息里那个括号里的路径,比如(/usr/local/lib/python3.10/site-packages/langchain/agents/__init__.py),已经告诉你问题出在哪个真实文件上,顺着它查下去,比直接搜报错摘要有效得多。

好了,这次的分享就到这里。如果你按上面的步骤修好了自己的环境,或者踩到了别的、我没列出来的坑,欢迎在评论区留下你的现场记录,我们下次接着聊。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进