各位读者朋友好,今天我们围绕 LangGraph 的工具调用(Tool Calling)做一期完整实战拆解。这个话题来自智能体开发中非常核心的一环:当大模型只负责“思考”时,谁来负责“行动”?答案就是工具调用。无论你是在学习 AI 编程,还是准备把智能体落地到实际业务系统,LangGraph 的工具调用机制都是绕不开的关键技能。
本文会从基础概念讲起,再逐步深入到代码实现、条件路由、多工具协作、以及生产环境常见的坑点。内容偏实操,建议你打开 Python 环境跟着敲一遍,效果会比只看不练好很多。
1. 什么是 LangGraph,为什么需要工具调用
1.1 从普通对话到大模型智能体
我们先从一个大背景说起。过去我们用大模型,大多数场景是“你问我答”:用户输入 Prompt,模型返回一段文本。这种模式适合写作辅助、代码生成、内容总结,但它有一个明显的问题:模型只会“说”,不会“做”。
什么是“做”?比如查天气、查数据库、调后端 API、操作文件、发请求、执行一段计算逻辑。这些动作大模型本身无法直接完成,因为它没有执行环境,也没有实时数据的访问权限。
智能体(Agent)的出现,就是为了解决这个问题。智能体让大模型变成一个“决策大脑”,它通过工具调用把具体的操作委托给函数、API 或者外部服务去执行,然后把执行结果拿回来继续推理。这样,大模型就从“聊天机器人”升级成了“能干活的工作流引擎”。
1.2 LangGraph 的定位
LangGraph 是 LangChain 生态中面向智能体编排的框架,它把整个智能体流程建模成一张图(Graph),图中的节点是“要执行的逻辑”,边是“状态流转的方向”。
相比早期的 LangChain Agent 执行器,LangGraph 更强调可控性和可视化。你可以非常清楚地看到:模型在哪个节点被调用、工具执行结果如何回流、走哪条分支、是否需要循环重试。这对于生产级智能体的调试和维护非常重要。
打个比方:LangChain 的传统 Agent 像“自动挡”,开起来方便但不好控制;LangGraph 更像“手动挡”,每个换挡逻辑都透明可控。对于需要精细编排的智能体项目,LangGraph 是更稳妥的选择。
1.3 工具调用的核心价值
工具调用(Tool Calling)指的是让大模型在生成回复的过程中,输出一个“调用某个工具”的指令,由程序侧解析这个指令并执行对应的函数,再把函数返回值作为上下文交还给模型继续生成。
这样做有三大好处:
第一,能力扩展。模型可以访问实时数据、私有业务接口、数据库等原本接触不到的信息源。
第二,结果可控。工具的执行逻辑是开发者编写的普通函数,会有明确的输入输出规范和异常处理,不会像模型生成内容那样不可控。
第三,可审计。每一次工具调用都有明确的请求参数和执行结果,方便日志追踪、权限控制和人工审核。
2. 环境准备与版本说明
在开始写代码之前,我们先准备好运行环境。本节内容基于常见的 Python 开发环境,版本信息方面需要结合你本机的实际环境进行调整,但整体思路不变。
2.1 安装 Python 环境
LangGraph 是基于 Python 的框架,建议使用 Python 3.9 及以上版本。如果你还没有安装 Python,可以直接去 Python 官网下载安装包,安装时记得勾选“Add Python to PATH”。
安装完成后,在命令行中确认版本:
python --version预期输出类似:
Python 3.11.92.2 创建虚拟环境
为了隔离依赖,建议为每个项目创建独立的虚拟环境。这里以 Python 内置的 venv 为例:
mkdir langgraph-tool-calling cd langgraph-tool-calling python -m venv venvWindows 系统激活虚拟环境:
venv\Scripts\activatemacOS 或 Linux 系统激活虚拟环境:
source venv/bin/activate激活成功后,命令行前面会显示(venv)字样。
2.3 安装 LangGraph 与相关依赖
本文示例需要安装以下核心库:
- langgraph:智能体编排框架
- langchain-core:提供 Tool、Message 等基础数据结构
- langchain-openai:OpenAI 兼容接口的 LangChain 适配层
安装命令:
pip install langgraph langchain-core langchain-openai如果你的网络环境不允许直接访问 OpenAI,可以使用兼容 OpenAI 接口的国内模型服务,只要配置对应的 base_url 和 api_key 即可。本文示例代码会以环境变量的方式读取配置,便于切换。
2.4 获取 API Key
工具调用依赖大模型的 function calling 能力。你需要准备一个支持工具调用的大模型 API Key。
如果你是使用 OpenAI,在终端设置环境变量:
export OPENAI_API_KEY="你的API Key"Windows PowerShell 设置方式:
$env:OPENAI_API_KEY="你的API Key"如果你使用的是国内大模型平台的 OpenAI 兼容接口,需要额外设置 base_url:
export OPENAI_API_KEY="你的API Key" export OPENAI_BASE_URL="模型服务商提供的接口地址"这里提醒一下:国内的模型服务商接口地址和模型名称各有差异,请以官方文档为准。本文的代码逻辑是通用的,只需要调整模型名称和 base_url 就能适配。
3. LangGraph 工具调用的核心原理
3.1 从“模型直接回答”到“模型发起工具调用”
在没有工具调用的情况下,一次完整的模型请求是:
用户输入 → 模型生成文本 → 返回给用户
引入工具调用之后,链路变成:
用户输入 → 模型判断是否需要工具 → 如果需要,则输出结构化工具调用指令 → 程序执行对应函数 → 将结果返回给模型 → 模型继续生成最终回复
这里有个关键点:模型本身并不直接执行函数。它生成的是一个“调用请求”,例如“我要调用 get_weather 这个工具,参数是 city=厦门”。真正执行的是我们的 Python 代码。LangGraph 负责把模型输出的调用请求解析出来,路由到对应的工具函数,再把工具结果封装成消息重新发给模型。
3.2 LangGraph 的核心组件
LangGraph 把智能体流程组织成一张有状态图。以下三个核心概念必须理解。
State(状态):智能体运行过程中的共享数据结构,相当于“全局变量池”。每一步节点的输出都会更新 State,图的状态在节点之间传递。
Node(节点):一个普通 Python 函数,接收当前 State,处理后返回一个字典,字典内容会合并到新的 State 中。
Edge(边):定义节点之间的流转关系。普通边表示固定流转,条件边则根据 State 动态决定下一个节点。
工具调用场景下,我们通常设计成这样的结构:
- 初始化 State
- 调用 Agent 节点,也就是大模型节点
- 大模型输出后判断是否有工具调用请求
- 如果有,进入 Tool 执行节点
- 执行完成,把结果写回 State,回到 Agent 节点再次推理
- 如果没有工具调用,直接输出最终答案,流程结束
这就是一个典型的 ReAct 循环:思考(Reasoning)→ 行动(Acting)→ 观察(Observation)→ 再思考,直到得到最终结论。
3.3 function calling 与 Tool Calling 的关系
在 LangChain 体系中,function calling 通常指大模型侧的原生能力,也就是模型在训练时学会了输出结构化工具调用指令。Tool Calling 则是 LangChain/LangGraph 对这些指令的统一封装层。你定义好一个 Tool,LangGraph 会自动生成模型需要的函数描述格式,并处理结果的解析。
这意味着我们只需要用@tool装饰器定义一个普通 Python 函数,LangGraph 会帮我们完成和模型之间的协议对接。
4. 完整实战:构建一个带工具调用的智能体
下面进入本文的核心环节。我们将从零开始构建一个能调用外部工具的智能体,并且逐步加入状态管理、条件路由、多工具等能力。
4.1 实战场景定义
为了让例子贴近实际开发,我设计一个日常场景:一个“生活助手智能体”,支持查询当前时间和查询城市天气。这两个信息对模型来说不是训练数据能实时覆盖的,必须通过工具函数获取。
4.2 创建项目结构
项目目录结构如下:
langgraph-tool-calling/ ├── venv/ ├── agent.py └── requirements.txt其中agent.py是我们的主程序文件,requirements.txt记录依赖。
创建requirements.txt:
langgraph>=0.2.0 langchain-core>=0.3.0 langchain-openai>=0.2.04.3 编写核心代码
我们分步骤来写,不直接堆一大段代码。
第一步,导入依赖并定义工具函数。
# 文件路径:agent.py from datetime import datetime from langchain_core.tools import tool使用@tool装饰器定义工具。每个工具函数都需要有清晰的 docstring,因为 LangGraph 会把函数名、docstring、参数签名发送给大模型,作为模型决定是否调用该工具的依据。
@tool def get_current_time() -> str: """获取当前服务器时间。当用户询问“现在几点”“当前时间”时调用。""" now = datetime.now() return now.strftime("%Y-%m-%d %H:%M:%S") @tool def get_weather(city: str) -> str: """查询指定城市的天气情况。当用户询问天气时调用。 参数说明: city: 城市名称,例如“厦门”“北京”“上海”。 """ # 这里为了演示,返回模拟数据。 # 实际项目中可以接入真实天气 API,例如和风天气、高德天气等。 weather_data = { "厦门": "晴,28度,东南风2级", "北京": "多云,22度,北风3级", "上海": "小雨,25度,东风2级", } return weather_data.get(city, f"暂无{city}的天气数据,请确认城市名称是否正确。")这里要解释一下为什么工具函数要写 docstring。大模型并不理解 Python 注释,它看到的是由 LangChain 生成的 JSON Schema 函数描述。@tool装饰器会自动解析函数名、参数类型、docstring 并转换成 OpenAI 兼容的工具描述格式。如果 docstring 不清晰,模型很可能在“该不该调用工具”的判断上出错。
第二步,创建工具列表并绑定到模型。
from langchain_openai import ChatOpenAI tools = [get_current_time, get_weather] # 使用环境变量中的 OPENAI_API_KEY 和 OPENAI_BASE_URL llm = ChatOpenAI( model="gpt-4o-mini", temperature=0 ) # 把工具绑定到大模型,模型就能感知到这些工具的存在 llm_with_tools = llm.bind_tools(tools)需要说明的是,model="gpt-4o-mini"只是示例,你需要根据实际使用的模型服务商调整模型名称。bind_tools是 LangChain 的方法,它会将工具列表转换为大模型接口要求的参数格式。
第三步,定义图的 State。
from typing import Annotated, TypedDict from langgraph.graph.message import add_messages class AgentState(TypedDict): messages: Annotated[list, add_messages]这里用到了 TypedDict 定义状态结构。messages字段存储对话消息列表,add_messages是 LangGraph 提供的消息合并处理器,它会把新节点返回的消息追加到原有消息列表中,而不是直接覆盖。
第四步,定义图节点。
我们设计两个核心节点:
agent节点:调用大模型,生成回复或工具调用指令。tools节点:执行模型请求的工具调用。
from langgraph.prebuilt import ToolExecutor, ToolNode from langgraph.graph import StateGraph, END tool_executor = ToolExecutor(tools) def agent_node(state: AgentState): """调用大模型核心节点""" messages = state["messages"] response = llm_with_tools.invoke(messages) return {"messages": [response]} def tools_node(state: AgentState): """执行大模型发起的工具调用""" messages = state["messages"] last_message = messages[-1] # 遍历模型输出的所有工具调用请求 tool_outputs = [] for tool_call in last_message.tool_calls: action = ToolExecutor(tool_executor).execute(tool_call) tool_outputs.append(action)等一下,上面的tools_node写法有点绕。实际上 LangGraph 提供了现成的ToolNode,我们直接使用即可,这样更简洁严谨:
from langgraph.prebuilt import ToolNode tools_node = ToolNode(tools)ToolNode会自动读取上一条 AI 消息中的tool_calls字段,找到对应的工具函数并执行,然后把结果包装成ToolMessage返回。它的内部逻辑正是我们手动写的那种遍历执行逻辑,但封装得更好,异常处理也更完善。
第五步,构建图结构。
from langgraph.graph import StateGraph, START, END def should_continue(state: AgentState): """条件路由函数:判断是否继续执行工具调用""" messages = state["messages"] last_message = messages[-1] if last_message.tool_calls: return "tools" return "end" graph = StateGraph(AgentState) # 添加节点 graph.add_node("agent", agent_node) graph.add_node("tools", tools_node) # 添加入口边:START -> agent graph.add_edge(START, "agent") # 添加条件边:agent 节点根据状态决定下一步走向 graph.add_conditional_edges( "agent", should_continue, { "tools": "tools", "end": END } ) # 工具执行结束后,回到 agent 节点继续推理 graph.add_edge("tools", "agent") # 编译图 app = graph.compile()第六步,编写 main 函数运行整个流程。
def main(): questions = [ "现在几点钟了?", "厦门今天的天气怎么样?", "帮我查一下深圳的天气", ] for question in questions: print("=" * 50) print(f"用户问题:{question}") print("-" * 50) result = app.invoke( {"messages": [("human", question)]} ) final_message = result["messages"][-1] print(f"智能体回答:{final_message.content}\n") if __name__ == "__main__": main()注意这里传入的初始 State 结构是{"messages": [("human", question)]},LangGraph 会自动将("human", question)转换为HumanMessage对象并存入状态。
4.4 运行与验证
在命令行执行:
python agent.py预期输出会分为三种情况:
第一种,不需要调用工具。比如询问“你好”,模型直接回复,不会经过 tools 节点。
第二种,需要调用工具。比如询问“现在几点钟了”,agent 节点会判断需要调用 get_current_time,然后 tools 节点执行该函数,把执行结果交还给 agent,agent 基于工具返回的数据生成最终回答。
第三种,多个工具连续调用。如果用户提问涉及多个工具,LangGraph 会重复执行 agent → tools → agent 的循环,直到 agent 不再发起新的工具调用为止。
4.5 结果说明
从上面的例子可以看到,LangGraph 工具调用的完整流程非常清晰:
- 用户消息进入图。
- agent 节点调用大模型,模型返回带
tool_calls的消息。 - 条件函数检测到需要调用工具,路由到 tools 节点。
- tools 节点执行函数,生成 ToolMessage。
- ToolMessage 进入 agent 节点,模型结合工具结果生成最终回答。
- 最终回答输出给用户。
这就是一个最基本的 ReAct 智能体循环。理解了这个循环,LangGraph 的绝大多数功能都能顺着这个思路展开。
5. 进阶实战:多工具协作与条件路由
5.1 定义更复杂的工具
实际开发中,工具往往不只是返回固定字典,而是需要访问数据库、调用第三方 API、处理文件等。我们扩展一个例子:增加一个“计算器”工具,和一个“获取用户订单状态”的工具,演示一下带有参数校验和多工具组合的场景。
@tool def calculate(expression: str) -> str: """计算数学表达式的值。当用户需要计算时调用。 参数说明: expression: 数学表达式字符串,例如 "1 + 2 * 3"。 """ try: result = eval(expression) return f"计算结果:{result}" except Exception as e: return f"计算失败:{str(e)},请检查表达式格式。" @tool def get_order_status(order_id: str) -> str: """查询订单的物流状态。当用户查询订单时调用。 参数说明: order_id: 订单号,例如 "ORD123456"。 """ # 模拟数据,实际项目可接入订单系统数据库 order_db = { "ORD123456": "已发货,预计明天送达", "ORD789012": "正在拣货中", } return order_db.get(order_id, f"未找到订单 {order_id},请确认订单号是否正确。")真实场景的注意事项:eval函数存在安全风险,直接在生产环境中执行不可信输入可能导致代码注入。上面的calculate工具仅用于教学演示,生产环境应该使用安全的表达式解析库,例如asteval或者自己写词法/语法解析器。
5.2 注册多个工具
把新工具加入工具列表:
tools = [get_current_time, get_weather, calculate, get_order_status]后续的llm.bind_tools(tools)和ToolNode(tools)代码无需修改,框架自动处理多工具的绑定和执行。这也是 LangGraph 设计得比较好的地方:工具的增删不会影响图结构骨架。
5.3 并行工具调用的行为
如果用户提问“现在几点,顺便查一下厦门的天气”,模型可能会在一次响应中输出两个工具调用请求。ToolNode会依次执行这两个调用,然后把所有ToolMessage一起返回给 agent 节点。
这种设计减少了 agent 循环的次数,降低了延迟。但要注意:如果两个工具之间有依赖关系(后一个工具需要前一个工具的输出作为参数),那就不能并行调用,需要拆分成多个 agent 循环步骤,或者手动设计专门的编排节点。
5.4 条件路由的进阶写法
前面should_continue返回的是字符串。LangGraph 的条件边本质是一个从 State 到“下一节点名”的映射。我们可以设计更丰富的路由规则,比如根据工具调用结果决定下一步进入人工审核节点还是直接结束。
def should_continue(state: AgentState): messages = state["messages"] last_message = messages[-1] if not last_message.tool_calls: return "end" # 示例:如果某个工具调用返回错误信息,则进入人工处理节点 tool_calls = last_message.tool_calls for call in tool_calls: if call["name"] == "get_order_status" and "未找到" in call.get("args", {}).get("order_id", ""): return "human_review" return "tools"这个示例说明,路由判断完全可以基于业务逻辑来定制。图结构不只是“模型决定一切”,开发者可以在关键节点上插入自己的规则,这在大模型智能体落地时非常重要,因为有些业务逻辑不能全部交给模型判断。
6. LangGraph 与 LangChain 的关系梳理
很多初学智能体开发的朋友容易混淆 LangGraph 和 LangChain,这里做一个简单梳理。
LangChain 是更底层的工具集,它提供了模型封装(ChatOpenAI)、提示词模板(PromptTemplate)、文本分割器、向量存储封装、Agent 的早期实现等。你可以用 LangChain 拼装一条“链”(Chain),但链是线性或简单的串行结构,难以表达复杂的条件和循环。
LangGraph 则是在 LangChain 基础之上构建的图编排框架。它使用图的数据结构来表达节点之间的依赖关系,支持条件路由、循环、子图、并行分支等复杂流程。
两者不是替代关系,而是互补关系。LangGraph 中的节点内部通常还是调用 LangChain 的组件。比如我们上文中的ChatOpenAI就是 LangChain 的模型封装,@tool装饰器来自 LangChain Core。
用一句话概括:LangChain 提供“积木”,LangGraph 提供“搭建图纸和施工流程”。对于复杂智能体项目,LangGraph 是更合适的选择;对于简单链式调用,LangChain 反而更轻量。
7. 常见问题与排查思路
工具调用的开发过程中,报错和异常行为集中在几个方面。下面整理高频问题。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 模型从不调用工具 | 工具描述不清晰,或模型不支持 function calling | 检查工具 docstring 是否明确;确认模型服务商是否支持工具调用;尝试更换模型 |
| 工具执行报错导致流程中断 | 工具函数内部未捕获异常 | 在工具函数内部捕获所有异常,返回可读错误信息;避免直接抛出异常 |
| 模型虚构工具返回结果 | 工具结果没有被正确传回模型 | 检查 State 中 messages 是否使用了 add_messages 处理器;确认 ToolNode 是否返回 ToolMessage |
| 循环调用工具停不下来 | 条件路由函数逻辑有误 | 打印每次 last_message.tool_calls 内容,检查路由分支是否符合预期 |
| 提示缺少 api_key | 环境变量未设置 | 检查 OPENAI_API_KEY 或对应服务商的环境变量是否正确设置 |
| 模型返回格式解析失败 | 使用的大模型工具调用接口与 LangChain 版本不兼容 | 检查 langchain-openai 版本,模型服务商是否使用标准 OpenAI 格式 |
7.1 如何调试 LangGraph 流程
调试智能体流程时,建议先打印每一步的 State 变化。可以编写一个辅助函数:
def debug_invoke(question: str): """带调试信息的运行函数""" print(f"\n用户问题:{question}") print("-" * 40) # 逐步遍历每个节点的事件 config = {"recursion_limit": 10} for event in app.stream( {"messages": [("human", question)]}, config=config, stream_mode="updates" ): print(f"图事件: {event}") print("=" * 40)app.stream会逐步产出每个节点的运行结果,stream_mode="updates"模式输出的是每个节点的状态更新。通过这个输出,你能清楚地看到 agent 节点产生了什么消息、tools 节点调用了哪些工具、路由是如何决策的。
7.2 recursion_limit 错误
LangGraph 默认限制图的最大递归次数(通常为 25)。如果你的智能体在 agent 和 tools 之间循环过多,会触发类似RecursionError: Maximum recursion limit reached的错误。
解决方法有两种:
第一种,检查为什么循环次数过多。通常是工具返回结果没有解决模型的问题,导致模型反复尝试调用同一个工具。这时应该检查工具返回的信息质量。
第二种,调整递归上限:
result = app.invoke( {"messages": [("human", question)]}, config={"recursion_limit": 50} )注意:调大上限只是临时缓解,根本解决办法是优化工具调用逻辑。
7.3 工具参数格式错误
如果模型传给工具的参数类型不对,比如定义了整数参数却传入字符串,工具执行时会报错。建议在工具函数内部做一层容错转换:
@tool def get_user_age(user_id: str) -> str: """根据用户ID查询年龄。""" try: age = int(user_id) + 10 # 模拟查询结果 return f"用户年龄:{age}" except (ValueError, TypeError): return f"用户ID格式不正确:{user_id}"更稳妥的方式是使用 Pydantic 模型定义工具输入结构,LangChain 的@tool装饰器支持通过args_schema参数指定输入校验模型。不过对于大多数内部工具,简单的 try-except 已经能满足需求。
8. 最佳实践与工程建议
8.1 工具命名与描述规范
工具的命名和描述直接影响模型调用工具的准确率。这里给出三条建议:
第一条,工具名使用动词开头的蛇形命名,例如get_weather、send_email、create_order,避免使用含糊的名称如action1、util。
第二条,docstring 要写清楚“什么场景下使用”和“关键参数的含义”。不要只写“获取天气”,而是要写“当用户询问某个城市的天气、温度、降水情况时调用;城市参数使用中文名称”。
第三条,如果多个工具功能类似,避免让模型混淆。例如有get_weather和get_air_quality两个工具,描述中要明确区分两者的边界。
8.2 错误处理与容错设计
工具函数是智能体感知外部世界的重要通道。外部世界充满不确定性:网络超时、数据库连接失败、参数异常、权限不足等。
建议每个工具函数都遵循以下模式:
- 捕获所有预期异常。
- 返回可读的错误信息,而不是抛出异常中断流程。
- 对于无法恢复的错误,可以在返回消息中明确提示“需要人工介入”。
这样做的好处是:即使某个工具失败,智能体依然可以基于错误信息给用户一个合理的回复,而不是整个流程崩溃。
8.3 日志与可观测性
生产环境的智能体必须有完整的日志记录。建议为每个工具调用记录以下信息:
- 模型生成的工具调用原始信息(工具名、参数)。
- 工具执行耗时。
- 工具执行结果或错误信息。
- 整个图的运行轨迹(哪个节点、哪个分支、递归次数)。
LangGraph 本身支持在节点函数中打日志,也可以接入 LangSmith 这类可观测性平台。对于个人项目,最简单的做法是在节点函数里打印或写入日志文件。
8.4 权限与安全边界
工具调用赋予了大模型“行动”的能力,也就意味着它可能在没有任何人类监督的情况下执行代码、调用 API、修改数据。这在生产环境中是必须严肃对待的问题。
我的建议是遵循最小权限原则:每个工具只暴露完成该任务所需的最小能力。例如,一个订票智能体,模型应该只能调用“查询航班”和“创建订单”接口,而不应该接触“取消订单”或“修改用户信息”等高危操作。
同时,涉及写操作(创建、修改、删除)的工具,应该在执行前增加确认机制,或者使用人工审核节点进行把关。
8.5 工具状态与幂等性
在设计工具时,尽量保证工具的幂等性。所谓幂等,就是同一个请求执行一次和执行多次,产生的结果是相同的。这个特性在智能体的重试场景下非常重要,因为模型可能会因为网络超时而重发同一个工具调用,如果不做幂等处理,就可能产生重复订单、重复发送消息等问题。
8.6 重视模型选择与成本控制
工具调用能力在不同模型上有明显差异。较新的模型在遵循工具调用指令、参数解析、多工具并行方面通常表现更好。如果你的智能体频繁出现“不调用工具”或“参数传错”的问题,可以优先考虑升级模型。
另外,每次 agent 和 tools 之间的循环都会消耗大模型的 Token。合理控制循环次数、精简工具描述、避免让模型反复调用同一工具,都是成本优化的方向。如果你使用的是按量计费的大模型 API,建议在代码中限制单次对话的最大工具调用次数:
MAX_TOOL_CALLS = 5 def track_tool_calls(state: AgentState): """统计工具调用次数,超过上限则停止""" tool_call_count = 0 for message in state["messages"]: if hasattr(message, "tool_calls") and message.tool_calls: tool_call_count += len(message.tool_calls) return tool_call_count当然,更优雅的做法是在 State 中增加一个计数器字段,在工具节点中递增,并把它纳入路由判断条件。
9. 总结与后续学习建议
本文围绕 LangGraph 的工具调用机制,一步步搭建了一个可运行的智能体应用,覆盖了工具定义、模型绑定、图构建、条件路由、多工具协作、调试排错和生产建议等关键环节。你现在应该能回答这几个问题了:
- LangGraph 为什么用图来组织智能体流程?
- 工具调用和普通函数调用的区别在哪里?
- 一个带工具调用的智能体是如何在 agent 节点和 tools 节点之间循环的?
- 条件路由函数如何处理“有工具调用”和“无工具调用”两种分支?
- 多工具并行调用时,LangGraph 是怎么处理返回结果的?
如果这篇文章的代码你已经全部跑通,下一步可以尝试以下扩展方向:
第一,加入记忆持久化。LangGraph 支持基于 Checkpoint 的状态持久化,这样跨会话的对话历史和工具调用状态都能保存下来。
第二,尝试子图(Subgraph)。当智能体流程复杂到一定程度,可以把一组节点封装成子图,作为主图的一个节点使用。
第三,研究并行分支。LangGraph 支持在一次执行中并行走多个节点,适合处理多个工具调用相互独立的场景。
第四,接入真实业务工具。把天气 API、订单数据库、内部系统接口接入工具函数,让智能体真正开始解决实际问题。
最后想提醒的是:大模型智能体的开发,本质上是一个系统工程。模型能力只是一部分,更重要的是工具设计的质量、状态管理的清晰度、错误处理的完善度。希望这篇文章能帮你把 LangGraph 工具调用这块基础打牢,以后无论多复杂的智能体系统,你都能从图和节点两个层次快速理解其运转逻辑。如果你在实操中遇到问题,欢迎对照第七节的排查表格逐项检查,通常能快速定位到原因。祝编码顺利。