本文详细解析了LangGraph框架中Tool-Calling的实现原理,从基础概念到源码剖析,让AI Agent具备调用外部工具的能力。文章介绍了@tool装饰器、bind_tools、ToolNode等核心组件,展示了消息流转过程,并提供了从环境准备到测试的完整实现案例。同时深入探讨了工具调用错误处理、多工具协作、工具调用链等进阶技巧,对比分析了LangGraph与OpenAI Function Calling的差异,为开发者构建具备"动手能力"的Agent提供了全面指南。
一、什么是 Tool-Calling?
在 AI Agent 的世界里,LLM(大语言模型)虽然"聪明",但有个致命缺陷:它只能思考,不能行动。
比如你问它"今天北京天气怎么样",它只能根据训练数据猜测,无法实时获取天气信息。
Tool-Calling(工具调用)就是解决这个问题的关键技术:让 LLM 能够调用外部工具(API、数据库、代码执行器等),从而获得"动手能力"。
1.1 Tool-Calling 的工作流程
整个流程可以总结为:
- LLM 思考:分析用户意图,决定是否需要调用工具
- 工具调用:按照 LLM 的指令,执行对应的工具函数
- 结果整合:LLM 根据工具返回的结果,生成最终回复
1.2 为什么 Tool-Calling 很重要?
| 场景 | 无工具调用 | 有工具调用 |
|---|---|---|
| 查询实时数据 | ❌ 只能瞎猜 | ✅ 调用 API 获取 |
| 执行计算 | ❌ 容易出错 | ✅ 调用计算器 |
| 操作数据库 | ❌ 无能为力 | ✅ 执行 SQL |
| 发送邮件 | ❌ 无能为力 | ✅ 调用邮件 API |
| 生成图表 | ❌ 无能为力 | ✅ 调用可视化工具 |
Tool-Calling 让 LLM 从"能说会道的嘴"变成了"能干活的手"。
二、LangGraph 的 Tool-Calling 架构
LangGraph 实现 Tool-Calling 的架构可以分为三层:
2.1 核心组件
| 组件 | 作用 |
|---|---|
| @tool 装饰器 | 将普通 Python 函数转换为 LLM 可识别的工具 |
| bind_tools | 将工具列表绑定到 LLM,让 LLM 知道有哪些工具可用 |
| ToolNode | LangGraph 内置的工具执行节点,自动处理工具调用 |
| AIMessage.tool_calls | LLM 返回的工具调用指令(包含工具名和参数) |
| ToolMessage | 工具执行结果的标准消息格式 |
2.2 消息流转
LangGraph 使用消息(Message)来传递 Tool-Calling 的信息:
# 1. 用户消息HumanMessage(content="北京今天天气怎么样?")# 2. LLM 返回的工具调用指令AIMessage(content="",tool_calls=[{"id":"call_123","name":"get_weather","args":{"city":"北京"}}])# 3. 工具执行结果ToolMessage(content="北京今天晴,25°C",tool_call_id="call_123")# 4. LLM 最终回复AIMessage(content="北京今天天气晴朗,气温25°C,非常适合出门活动!")三、从零实现 Tool-Calling
下面通过一个完整的例子,演示如何在 LangGraph 中实现 Tool-Calling。
3.1 环境准备
pip install langgraph langchain langchain-openai3.2 定义工具
from langchain_core.toolsimporttool @tool def get_weather(city: str)->str:"""获取指定城市的天气信息。 Args: city: 城市名称,如"北京"、"上海""""# 模拟天气数据(实际项目中调用真实 API)weather_data={"北京":"晴,25°C,东北风3级","上海":"多云,28°C,东南风2级","广州":"雷阵雨,32°C,南风4级"}returnweather_data.get(city, f"暂无{city}的天气数据")@tool def calculate(expression: str)->str:"""计算数学表达式。 Args: expression: 数学表达式,如"1 + 2 * 3"""" try: result=eval(expression)returnf"计算结果:{expression} = {result}"except Exception as e: returnf"计算出错:{e}"@tool def search_knowledge(query: str)->str:"""搜索知识库获取相关信息。 Args: query: 搜索关键词"""# 模拟知识库搜索knowledge={"langgraph":"LangGraph 是 LangChain 生态中用于构建复杂 Agent 工作流的框架,支持状态管理、条件分支和循环。","tool-calling":"Tool-Calling 是让 LLM 能够调用外部工具的技术,使 Agent 具备执行实际操作的能力。"}forkey, valueinknowledge.items():ifkeyinquery.lower():returnvalue returnf"未找到与'{query}'相关的信息"# 工具列表tools=[get_weather, calculate, search_knowledge]关键点解析:
@tool装饰器将普通函数转换为 LangGraph 可识别的工具- docstring 非常重要:LLM 通过 docstring 理解工具的功能和参数含义
- 参数类型注解(如
city: str)会被转换为 JSON Schema,供 LLM 理解参数格式
3.3 绑定工具到 LLM
from langchain_openaiimportChatOpenAI# 初始化 LLMllm=ChatOpenAI(model="gpt-4o-mini",temperature=0)# 将工具绑定到 LLMllm_with_tools=llm.bind_tools(tools)bind_tools 做了什么?
bind_tools会将工具的元信息(名称、描述、参数 Schema)注入到 LLM 的请求中:
{"tools":[{"type":"function","function":{"name":"get_weather","description":"获取指定城市的天气信息。","parameters":{"type":"object","properties":{"city":{"type":"string","description":"城市名称,如"北京"、"上海""}},"required":["city"]}}}]}这样 LLM 就知道有哪些工具可用,以及如何调用它们。
3.4 构建 Agent 节点
from typingimportAnnotated, TypedDict from langgraph.graph.messageimportadd_messages# 定义状态class AgentState(TypedDict): messages: Annotated[list, add_messages]# Agent 节点:调用 LLM 进行推理def agent_node(state: AgentState):"""Agent 节点:调用 LLM 进行推理""" messages=state["messages"]response=llm_with_tools.invoke(messages)return{"messages":[response]}这里的add_messages是 LangGraph 提供的消息累加器,会自动将新消息追加到历史消息列表中。
3.5 使用 ToolNode 执行工具
from langgraph.prebuiltimportToolNode# 创建工具执行节点tool_node=ToolNode(tools)ToolNode 的工作原理:
- 接收包含
AIMessage的状态 - 解析
AIMessage.tool_calls中的工具调用指令 - 执行对应的工具函数
- 将结果封装为
ToolMessage返回
3.6 定义路由逻辑
from langgraph.graphimportEND def should_continue(state: AgentState):"""判断是否需要继续执行工具""" messages=state["messages"]last_message=messages[-1]# 如果 LLM 返回了工具调用指令,则继续执行工具ifhasattr(last_message,"tool_calls")and last_message.tool_calls:return"tools"# 否则结束returnEND核心逻辑:
- 检查最后一条消息是否包含
tool_calls - 如果有,说明 LLM 想要调用工具,路由到
tools节点 - 如果没有,说明 LLM 已经给出最终回复,结束流程
3.7 组装 StateGraph
from langgraph.graphimportStateGraph# 创建状态图workflow=StateGraph(AgentState)# 添加节点workflow.add_node("agent", agent_node)workflow.add_node("tools", tool_node)# 设置入口点workflow.set_entry_point("agent")# 添加条件边:Agent 节点根据是否有工具调用决定下一步workflow.add_conditional_edges("agent", should_continue,{"tools":"tools",# 有工具调用 -> 执行工具END: END# 无工具调用 -> 结束})# 添加普通边:工具执行完后,回到 Agent 继续处理workflow.add_edge("tools","agent")# 编译app=workflow.compile()关键设计:形成循环
注意这里tools -> agent形成了一个循环!这是 LangGraph Tool-Calling 的精髓:
agent ──(有tool_calls)──>tools ──>agent ──(无tool_calls)──>END │ │ └───────────────────────────────────┘ 循环直到完成这种设计允许:
- 多次工具调用:LLM 可以连续调用多个工具
- 工具链:一个工具的结果可以作为另一个工具的输入
- 自我修正:工具执行失败后,LLM 可以重新调整参数再试
3.8 测试
from langchain_core.messagesimportHumanMessage def chat(user_input: str):"""与 Agent 对话""" result=app.invoke({"messages":[HumanMessage(content=user_input)]})returnresult["messages"][-1].content# 测试if__name__=="__main__":# 测试 1:调用天气工具print("="*50)print("测试1:查询天气")print("="*50)response=chat("北京今天天气怎么样?")print(f"回复: {response}")# 测试 2:调用计算工具print("\n"+"="*50)print("测试2:数学计算")print("="*50)response=chat("帮我算一下 (23 + 45) * 2 等于多少")print(f"回复: {response}")# 测试 3:调用知识库搜索print("\n"+"="*50)print("测试3:知识库搜索")print("="*50)response=chat("什么是 LangGraph?")print(f"回复: {response}")# 测试 4:不需要工具调用print("\n"+"="*50)print("测试4:普通对话(无工具调用)")print("="*50)response=chat("你好,自我介绍一下")print(f"回复: {response}")输出示例:
四、深入原理:Tool-Calling 的底层实现
4.1 @tool 装饰器的秘密
@tool装饰器做了以下事情:
from langchain_core.toolsimporttool @tool def my_func(arg1: str, arg2: int)->str:"""这是函数描述。 Args: arg1: 参数1描述 arg2: 参数2描述"""return"result"# 查看工具元信息print(my_func.name)# "my_func"print(my_func.description)# "这是函数描述。"print(my_func.args_schema)# Pydantic 模型,包含参数定义内部转换过程:
- 解析函数签名,提取参数名和类型注解
- 解析 docstring,提取函数描述和参数描述
- 生成 Pydantic 模型作为
args_schema - 创建
StructuredTool对象,包装原函数
4.2 LLM 如何决定调用哪个工具?
当 LLM 收到绑定了工具的请求时,它会:
- 理解用户意图:分析用户输入,判断需要什么信息/操作
- 匹配工具:根据工具的
name和description,选择合适的工具 - 构造参数:根据
args_schema,从用户输入中提取参数值 - 返回调用指令:生成
tool_calls结构
# LLM 的返回示例AIMessage(content="",# 注意:调用工具时 content 通常为空tool_calls=[{"id":"call_abc123","name":"get_weather","args":{"city":"北京"}}])4.3 ToolNode 源码解析
ToolNode 的核心逻辑(简化版):
class ToolNode: def __init__(self, tools: list):# 建立工具名到工具对象的映射self.tools_by_name={tool.name: toolfortoolintools}def __call__(self, state: dict): messages=state["messages"]last_message=messages[-1]outputs=[]fortool_callinlast_message.tool_calls:# 1. 找到对应的工具tool=self.tools_by_name[tool_call["name"]]# 2. 执行工具result=tool.invoke(tool_call["args"])# 3. 封装结果为 ToolMessageoutputs.append(ToolMessage(content=str(result),tool_call_id=tool_call["id"]))return{"messages":outputs}关键点:
tool_call_id用于将工具结果与调用指令关联- 支持并行执行多个工具调用
- 结果以
ToolMessage形式返回,LLM 可以继续处理
4.4 消息流转全过程
五、进阶技巧
5.1 处理工具调用错误
工具执行可能会失败,我们需要优雅地处理错误:
from langchain_core.toolsimporttool @tool def risky_operation(param: str)->str:"""执行可能失败的操作。"""ifparam=="error":raise ValueError("参数不合法!")returnf"操作成功: {param}"# 方法1:在工具内部捕获异常@tool def safe_operation(param: str)->str:"""执行操作(带错误处理)。""" try:ifparam=="error":raise ValueError("参数不合法!")returnf"操作成功: {param}"except Exception as e: returnf"操作失败: {str(e)}"# 方法2:使用 ToolNode 的 handle_tool_error 参数tool_node=ToolNode(tools,handle_tool_error=True# 自动捕获错误,将错误信息作为 ToolMessage 返回)5.2 多工具协作
LLM 可以在一次响应中调用多个工具:
# 用户: "北京和上海今天哪个城市更热?"# LLM 返回多个工具调用AIMessage(content="",tool_calls=[{"id":"call_1","name":"get_weather","args":{"city":"北京"}},{"id":"call_2","name":"get_weather","args":{"city":"上海"}}])# ToolNode 会并行执行两个调用,返回两个 ToolMessage5.3 工具调用链
一个工具的输出可以作为决策依据,触发另一个工具:
# 用户: "如果北京今天下雨,帮我发一封提醒邮件"# 循环 1Agent:"我需要先查天气"→ 调用 get_weather("北京")Tools: 返回"北京今天晴..."# 循环 2Agent:"天气是晴天,不需要发邮件"→ 直接回复# 如果返回的是下雨# 循环 2Agent:"天气是下雨,需要发邮件"→ 调用 send_email(...)Tools: 返回"邮件发送成功"# 循环 3Agent:"邮件已发送"→ 回复用户5.4 结构化工具定义
除了@tool装饰器,还可以使用StructuredTool进行更灵活的定义:
from langchain_core.toolsimportStructuredTool from pydanticimportBaseModel, Field# 使用 Pydantic 模型定义参数class WeatherInput(BaseModel): city: str=Field(description="城市名称")date: str=Field(default="today",description="日期,默认今天")def get_weather_impl(city: str, date: str="today")->str: returnf"{city}在{date}的天气是晴天"# 创建结构化工具weather_tool=StructuredTool.from_function(func=get_weather_impl,name="get_weather",description="获取指定城市和日期的天气",args_schema=WeatherInput)六、LangGraph Tool-Calling vs OpenAI Function Calling
| 特性 | OpenAI Function Calling | LangGraph Tool-Calling |
|---|---|---|
| 工具定义 | JSON Schema | @tool 装饰器 / StructuredTool |
| 多轮调用 | 需手动实现循环 | 原生支持(StateGraph 循环) |
| 状态管理 | 需手动维护 | 自动管理(State) |
| 错误处理 | 需手动处理 | 内置 handle_tool_error |
| 工具链 | 需手动编排 | 图结构自动编排 |
| 可观测性 | 需额外工具 | 内置 trace(与 LangSmith 集成) |
LangGraph 的优势在于:把 Tool-Calling 纳入了图结构的工作流中,自动处理循环、状态传递和错误恢复。
七、实战建议
7.1 工具设计原则
- 单一职责:每个工具只做一件事,功能聚焦
- 清晰描述:docstring 要准确描述功能,LLM 靠这个理解工具
- 参数简洁:参数越少越好,减少 LLM 出错概率
- 错误友好:返回清晰的错误信息,帮助 LLM 理解和调整
7.2 常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| LLM 不调用工具 | 工具描述不够清晰 | 优化 docstring |
| LLM 调用错误的工具 | 多个工具功能重叠 | 明确区分工具职责 |
| 参数提取错误 | 参数描述不明确 | 添加示例和约束 |
| 工具循环调用 | 缺少终止条件 | 检查 should_continue 逻辑 |
八、总结
本文深入讲解了 LangGraph 中 Tool-Calling 的使用和实现原理:
核心组件:
@tool装饰器:定义工具bind_tools:绑定工具到 LLMToolNode:执行工具调用AIMessage.tool_calls:LLM 的调用指令ToolMessage:工具执行结果
工作流程:
关键设计:
- 消息驱动的工具调用
- 图结构支持循环调用
- 状态自动管理
掌握了 Tool-Calling,你的 Agent 就具备了真正的"动手能力"。
如何学习大模型 AI ?
由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。
但是具体到个人,只能说是:
“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。
这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。
我在一线科技企业深耕十二载,见证过太多因技术卡位而跃迁的案例。那些率先拥抱 AI 的同事,早已在效率与薪资上形成代际优势,我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在大模型的学习中的很多困惑。我们整理出这套AI 大模型突围资料包:
- ✅ 从零到一的 AI 学习路径图
- ✅ 大模型调优实战手册(附医疗/金融等大厂真实案例)
- ✅ 百度/阿里专家闭门录播课
- ✅ 大模型当下最新行业报告
- ✅ 真实大厂面试真题
- ✅ 2025 最新岗位需求图谱
所有资料 ⚡️ ,朋友们如果有需要《AI大模型入门+进阶学习资源包》,下方扫码获取~
① 全套AI大模型应用开发视频教程
(包含提示工程、RAG、LangChain、Agent、模型微调与部署、DeepSeek等技术点)
② 大模型系统化学习路线
作为学习AI大模型技术的新手,方向至关重要。 正确的学习路线可以为你节省时间,少走弯路;方向不对,努力白费。这里我给大家准备了一份最科学最系统的学习成长路线图和学习规划,带你从零基础入门到精通!
③ 大模型学习书籍&文档
学习AI大模型离不开书籍文档,我精选了一系列大模型技术的书籍和学习文档(电子版),它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。
④ AI大模型最新行业报告
2025最新行业报告,针对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。
⑤ 大模型项目实战&配套源码
学以致用,在项目实战中检验和巩固你所学到的知识,同时为你找工作就业和职业发展打下坚实的基础。
⑥ 大模型大厂面试真题
面试不仅是技术的较量,更需要充分的准备。在你已经掌握了大模型技术之后,就需要开始准备面试,我精心整理了一份大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。
以上资料如何领取?
为什么大家都在学大模型?
最近科技巨头英特尔宣布裁员2万人,传统岗位不断缩减,但AI相关技术岗疯狂扩招,有3-5年经验,大厂薪资就能给到50K*20薪!
不出1年,“有AI项目经验”将成为投递简历的门槛。
风口之下,与其像“温水煮青蛙”一样坐等被行业淘汰,不如先人一步,掌握AI大模型原理+应用技术+项目实操经验,“顺风”翻盘!
这些资料真的有用吗?
这份资料由我和鲁为民博士(北京清华大学学士和美国加州理工学院博士)共同整理,现任上海殷泊信息科技CEO,其创立的MoPaaS云平台获Forrester全球’强劲表现者’认证,服务航天科工、国家电网等1000+企业,以第一作者在IEEE Transactions发表论文50+篇,获NASA JPL火星探测系统强化学习专利等35项中美专利。本套AI大模型课程由清华大学-加州理工双料博士、吴文俊人工智能奖得主鲁为民教授领衔研发。
资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的技术人员,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。