ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

LangGraph工具调用实战:让大模型真正执行任务的智能体闭环

LangGraph工具调用实战:让大模型真正执行任务的智能体闭环 工具调用是 LangGraph 智能体开发里最关键的闭环。原因很直接大模型只能基于训练数据生成文本无法访问实时数据也无法执行真实动作而工具调用让模型在回答过程中“请求调用”一个外部函数函数执行完再把结果回传给模型模型基于真实结果继续生成。没有这一步智能体就只是聊天机器人有了这一步智能体才真正具备执行能力。这篇内容对应厦门大学林子雨老师《AI编程与智能体开发》课程的 9.5 节主题LangGraph 的工具调用。整篇文章围绕一条主线展开先讲工具调用要解决什么问题再给出一套可本地安装的运行环境接着用完整代码演示第一个带工具调用的智能体然后拆解 LangGraph 从模型到工具再到模型的执行流程最后把智能体包装成 HTTP 服务并跑批量任务。文章末尾会给出常见报错和排查方法。如果你正在学智能体开发或者准备把大模型接到自己的业务工具里这篇可以按顺序读也可以直接跳到第 4 节照着复制代码运行。整个示例对本地显卡没有硬性要求使用云端模型 API 即可跑通如果后续要换成本地模型第 3 节也给了 Ollama 的接入方式。1. LangGraph 工具调用核心能力速览先把最关键的信息列出来方便你判断这个技术栈适不适合当前项目。能力项说明框架来源LangGraphLangChain 官方推出的智能体编排框架当前版本已相当稳定课程对应厦门大学林子雨《AI编程与智能体开发》第 9 章 9.5 节核心功能基于 StateGraph 构建智能体模型调用外部工具条件路由控制循环支持子图、持久化、并行分支工具调用方式bind_tools绑定模型ToolNode执行工具tools_condition决定是否继续调用快速建智能体create_react_agent一行式封装 ReAct 循环模型兼容OpenAI、Anthropic、Google Gemini以及 DeepSeek、通义千问、Ollama 等兼容接口是否支持 CPU支持。工具调用逻辑本身与模型推理解耦CPU 环境可以跑通只是本地模型推理速度偏慢是否支持 API可以。智能体编译完成后可用 FastAPI 包成 HTTP 服务是否支持批量任务可以。用循环或线程池并行调用注意模型服务限流和失败重试显存要求使用云端模型 API 时本地无需 GPU使用 Ollama 本地模型时取决于模型参数量和量化精度实际占用需按本机测试适合场景AI 编程教学、智能体原型开发、RAG 工具增强、企业内部系统自动化、API 服务集成单看这张表LangGraph 工具调用的定位很明确它不是模型也不是对话界面而是负责“调度”的中间层。模型负责理解和生成工具负责执行和返回事实LangGraph 负责把这两者反复连接起来直到任务完成。2. 适用场景与使用边界2.1 适合谁用LangGraph 工具调用适合三类人。第一类是正在学 AI 编程和智能体开发的学生或开发者。比 LangChain 的链式调用更进一步LangGraph 用图结构组织智能体逻辑工具调用是理解StateGraph、条件路由、循环控制的最佳入口。第二类是要把大模型接到业务系统的后端工程师。模型不能直接查数据库、调订单接口、读本地文件但工具可以。给模型暴露一个“查库存”的工具它就具备了回答库存问题的能力。第三类是做原型验证的产品和算法工程师。用create_react_agent几分钟就能搭出带多个工具的实验环境先验证效果再决定是否上复杂工作流。2.2 能解决什么问题模型无法访问实时数据时工具负责查天气、查时间、查数据库。模型无法执行确定性计算时工具负责做数值计算、字符串处理、文件读写。模型无法调用外部系统时工具负责调 REST API、发通知、写日志。模型输出不稳定时工具的结果可以作为事实回传让模型基于真实输出继续作答。2.3 不适合什么场景工具调用不适合超大并发低延迟场景。每一次工具调用都会增加至少一轮模型推理链路越长延迟越高高并发场景需要做缓存、限流和异步任务队列而不是直接在同步接口里裸跑。工具调用也不适合对正确率要求 100% 的场景。模型可能选错工具、填错参数、编造工具结果必须在工具层做校验、在智能体层做失败重试。2.4 合规与安全边界工具一旦暴露给模型就相当于把一个可执行入口交给了模型。必须明确以下几点涉及用户隐私信息、企业数据的工具要确认调用方有合法授权。涉及人脸、声音、版权素材、公司内部文档的工具必须在授权范围内使用。写操作工具删除、修改、转账、发消息要单独加权限控制建议默认只读。敏感数据优先使用本地模型或私有化部署避免通过第三方 API 传输。生产环境不要用eval执行模型提供的表达式防止提示注入带来安全隐患。3. 本地部署环境准备LangGraph 工具调用的环境准备分三部分Python 环境、核心依赖、模型访问通道。3.1 检查 Python 版本建议使用 Python 3.9 及以上版本。终端执行python --version如果版本过低先升级 Python再创建虚拟环境。python -m venv .venv # Windows .venv\Scripts\activate # macOS / Linux source .venv/bin/activate3.2 安装 LangGraph 依赖pip install -U langgraph langchain-core langchain-openai python-dotenv这里拆开解释一下langgraph图编排框架负责状态管理、节点调度、条件路由。langchain-core提供tool装饰器、消息对象等基础类型。langchain-openaiOpenAI 兼容接口的模型封装。python-dotenv从.env文件读取 API Key。3.3 配置模型访问工具调用要求模型本身支持 function calling。OpenAI、DeepSeek、通义千问等模型都支持 OpenAI 兼容接口创建.env文件# .env OPENAI_API_KEY你的APIKey OPENAI_BASE_URLhttps://api.openai.com/v1如果使用第三方兼容服务把OPENAI_BASE_URL换成对应服务地址即可。代码里用load_dotenv()读取。3.4 使用本地模型可选如果不想依赖云端 API可以用 Ollama 加载本地模型。先安装 Ollama然后拉取一个支持工具调用的模型pip install -U langchain-ollama ollama pull qwen2.5:7b代码里把模型换成from langchain_ollama import ChatOllama llm ChatOllama( modelqwen2.5:7b, temperature0, )本地模型对显卡的要求取决于模型参数量和量化精度7B 左右的量化模型在消费级显卡上可以运行但响应速度、上下文长度和工具调用稳定性和云端模型有明显差距实际表现需要在本机实测确认。4. 第一个工具调用示例定义工具、绑定模型、构建图环境准备好之后直接看一个最小可运行示例。场景是让智能体查询城市天气工具返回模拟数据。import os from dotenv import load_dotenv from langchain_core.messages import HumanMessage from langchain_core.tools import tool from langchain_openai import ChatOpenAI from langgraph.graph import END, START, StateGraph from langgraph.graph.message import add_messages from langgraph.prebuilt import ToolNode, tools_condition from typing import Annotated, TypedDict load_dotenv() tool def get_weather(city: str) - str: 查询指定城市当前的天气情况。 return f{city}晴气温 22℃东南风 3 级 class State(TypedDict): messages: Annotated[list, add_messages] tools [get_weather] llm ChatOpenAI( modelgpt-4o-mini, temperature0, api_keyos.getenv(OPENAI_API_KEY), base_urlos.getenv(OPENAI_BASE_URL), ) # 关键步骤把工具绑定到模型模型才具备“请求调用工具”的能力 llm_with_tools llm.bind_tools(tools) def assistant(state: State): return {messages: [llm_with_tools.invoke(state[messages])]} graph StateGraph(State) # 两个核心节点assistant 负责决策tools 负责执行 graph.add_node(assistant, assistant) graph.add_node(tools, ToolNode(tools)) graph.add_edge(START, assistant) # 条件路由assistant 输出里有 tool_calls 就去 tools否则直接结束 graph.add_conditional_edges(assistant, tools_condition) # 工具执行完把 ToolMessage 回传给 assistant 再决策 graph.add_edge(tools, assistant) agent graph.compile()运行result agent.invoke({ messages: [HumanMessage(content厦门今天天气怎么样)] }) for msg in result[messages]: print(msg.type) print(msg.content)预期输出大致是human 厦门今天天气怎么样 ai tool 厦门晴气温 22℃东南风 3 级 ai 厦门今天天气是晴天气温 22 摄氏度东南风 3 级。注意执行顺序模型先生成一个带tool_calls的 AIMessage然后ToolNode执行get_weather生成 ToolMessage再交给模型生成最终回答。这就是一次完整的工具调用闭环。如果只打印result[messages][-1].content看到的是最终回答要验证工具调用是否生效最好是像上面一样把所有消息都打印出来。5. 执行流程拆解模型、ToolNode 与条件路由第 4 节的代码只有十几行实际包含 LangGraph 工具调用的所有核心机制。理解执行流程以后再复杂的智能体都是在这条主线上加节点、加边。5.1 完整链路整个执行过程如下用户输入以 HumanMessage 写入 state 的messages列表。START边把状态交给assistant节点。assistant调用绑定了工具的模型生成 AIMessage 或带tool_calls的 AIMessage。tools_condition检查 AIMessage 是否包含tool_calls。如果有tool_calls路由到tools节点ToolNode根据函数名和参数执行对应的工具函数。工具执行结果包装成 ToolMessage 追加到messages。tools - assistant边让模型再次看到完整消息列表包括工具结果。模型如果还需要其他工具信息重复第 4 到第 7 步如果不再需要tools_condition返回END图执行结束。这里的循环是关键。LangGraph 不是“一问一答”就结束而是让模型可以连续请求多个工具直到它认为信息足够。比如模型可以先查天气再查航班再用两个结果拼出完整回答。5.2 ToolNode 做了什么ToolNode是 LangGraph 预置的“工具执行器”。它接收 AIMessage 里的tool_calls列表逐个执行对应工具再把结果转为 ToolMessage 返回。如果多个工具被同时请求ToolNode默认并行执行这些工具。这带来一个好处批量查询类任务可以一次性完成不需要模型多次循环。5.3 tools_condition 的条件路由tools_condition是一个预置的判断函数作用相当于如果 AIMessage 里有tool_calls返回tools节点名。如果没有tool_calls返回END。在add_conditional_edges(assistant, tools_condition)这一行里LangGraph 会根据tools_condition的返回值动态决定下一步去哪个节点。这也是 LangGraph 条件路由最典型的使用方式。5.4 如何验证流程是否正常调试时重点看result[messages]里的消息顺序for i, msg in enumerate(result[messages]): print(f[{i}] {msg.type}: {msg.content}) if getattr(msg, tool_calls, None): print(f tool_calls - {msg.tool_calls})如果看到ai - tool - ai的顺序说明工具调用链路是通的如果ai直接给出最终回答而没有tool消息说明模型没有触发工具调用要检查工具描述是否清晰、模型是否支持 function calling。6. 用 create_react_agent 快速构建多工具智能体手动写StateGraph的好处是逻辑透明适合学习和精细控制。实际开发里如果只需要一个标准的循环智能体用预置的create_react_agent更快。它内部封装了assistant tools tools_condition的 ReAct 循环。下面演示一个带三个工具的智能体查天气、查时间、做计算。from datetime import datetime from langchain_core.tools import tool from langchain_openai import ChatOpenAI from langgraph.prebuilt import create_react_agent tool def get_weather(city: str) - str: 查询指定城市当前的天气情况。 return f{city}晴气温 22℃东南风 3 级 tool def get_current_time() - str: 获取当前系统时间。 return datetime.now().strftime(%Y-%m-%d %H:%M:%S) tool def calculate(expression: str) - str: 计算基础数学表达式支持加、减、乘、除和括号例如 (123456)*2。 # 教学演示用。生产环境禁止直接 eval应使用 ast 解析或专用表达式库。 try: return str(eval(expression, {__builtins__: {}})) except Exception as exc: return f计算失败{exc} tools [get_weather, get_current_time, calculate] llm ChatOpenAI( modelgpt-4o-mini, temperature0, api_key你的APIKey, base_urlhttps://api.openai.com/v1, ) agent create_react_agent( modelllm, toolstools, prompt你是一个可靠的助手需要查询实时信息时先调用工具再基于工具结果回答。, )测试一次多工具协作result agent.invoke({ messages: [{role: user, content: 现在几点另外帮我算一下 (123456)*2 等于多少。}] }) print(result[messages][-1].content)模型很可能会先把两个问题拆成两个工具调用ToolNode并行执行最后模型汇总结果输出。整个过程中开发者不需要手动写循环判断create_react_agent已经把“决策、执行、回传、再决策”封装好了。6.1 如何查看工具调用明细create_react_agent只返回最终消息列表调试时同样可以遍历for msg in result[messages]: print(msg.type, getattr(msg, name, ), msg.content) if getattr(msg, tool_calls, None): print( tool_calls:, msg.tool_calls)7. 封装 HTTP 接口与批量任务智能体跑通以后下一步通常是接入业务系统。这里给出两种常见用法封装 FastAPI 接口、批量处理任务列表。7.1 用 FastAPI 暴露接口# app.py from fastapi import FastAPI from pydantic import BaseModel from langchain_core.messages import HumanMessage from your_agent_module import get_agent # 这里替换成你编译好的 agent app FastAPI() class ChatRequest(BaseModel): message: str app.post(/chat) def chat(req: ChatRequest): result get_agent().invoke({ messages: [HumanMessage(contentreq.message)] }) return { answer: result[messages][-1].content, message_count: len(result[messages]), }启动服务pip install -U fastapi uvicorn uvicorn app:app --host 0.0.0.0 --port 8000用 curl 测试curl -X POST http://127.0.0.1:8000/chat \ -H Content-Type: application/json \ -d {message: 厦门今天天气怎么样}接口返回 JSON字段包含answer和message_count。message_count可以用来判断是否发生了工具调用大于 2 说明中间走了工具节点。接口部署时要注意两点一是把uvicorn的--port换成业务可用端口避免冲突二是如果服务要暴露到内网以外必须在网关上做鉴权和限流避免被刷。7.2 批量任务处理批量任务最简单的做法是循环调用tasks [厦门天气, 上海天气, 广州天气, 现在几点] for task in tasks: result agent.invoke({ messages: [{role: user, content: task}] }) print(task, -, result[messages][-1].content)如果任务量大且模型服务支持并发可以用线程池from concurrent.futures import ThreadPoolExecutor def run_one(text: str) - str: result agent.invoke({ messages: [{role: user, content: text}] }) return result[messages][-1].content with ThreadPoolExecutor(max_workers4) as pool: outputs list(pool.map(run_one, tasks))并发数不要盲目开大。云端模型服务通常有 RPM每分钟请求数和 TPM每分钟 Token 数限制开太多线程会触发限流导致大量 429 报错。推荐的做法是批量任务统一走队列控制并发数。每个任务记录日志包含输入、输出、耗时、是否发生工具调用。失败任务重试 2 到 3 次重试间隔递增。对结果做人工抽检避免模型编造工具输出。8. 资源占用与性能观察工具调用对资源的影响主要取决于模型在哪运行。8.1 云端模型 API使用 OpenAI、DeepSeek、通义千问等云端 API 时本地不承担模型推理压力agent.invoke的延迟主要来自网络往返和模型生成时间。观察重点不是显存而是接口响应耗时、Token 消耗、限流状态。8.2 本地模型使用 Ollama 等本地模型时CPU、内存、显存都会参与推理。观察显存占用用nvidia-smi -l 1也可以观察进程状态ollama ps这个命令会显示当前加载的模型、大小和显存占用。本地模型的显存占用和模型参数量、量化精度、上下文长度直接相关同一模型在不同机器上表现差异很大需要按自己的实际环境测试不建议直接照搬网上参数。8.3 影响性能的四个因素第一是工具数量。模型每次都要把工具名称和描述拼进 prompt工具越多prompt 越长首字延迟越高。只暴露当前任务需要的工具即可。第二是工具描述质量。描述写得模糊模型可能反复请求同一个工具或者选错工具导致循环次数增加。第三是循环轮数。每多一轮工具调用就多一次模型请求。对需要多工具协作的任务尽量让模型一次请求多个工具ToolNode会并行执行减少往返。第四是上下文长度。messages会累积所有历史消息和工具结果长对话场景要引入消息裁剪或摘要节点否则上下文会持续膨胀。9. 常见问题与排查方法把开发中容易遇到的现象、原因、排查方式和解决方案整理成一张表。问题现象可能原因排查方式解决方案模型不触发工具调用直接回答模型不支持 function calling或工具描述不清晰打印 AIMessage 看有没有tool_calls换成支持工具调用的模型重写工具描述说清楚用途和参数报错tool_calls不被识别模型返回了特殊格式的工具调用查看原始响应日志用官方兼容接口的模型升级langchain-core执行结果没有走工具节点tools_condition路由配置错误检查add_conditional_edges的返回值确认tools_condition返回的工具节点名与实际节点名一致出现无限循环报 RecursionLimit工具结果没有让模型停止或工具逻辑有误打印每一轮消息检查工具返回内容设置recursion_limit修正工具内部错误在工具中对异常返回明确错误信息依赖安装失败Python 版本过低或 pip 包冲突查看 pip 报错信息升级 Python 到 3.9在虚拟环境重建依赖API 调用报 401/403API Key 或 Base URL 配置错误检查.env和代码读取逻辑重新配置环境变量确认服务商地址正确本地模型工具调用不稳定小模型 function calling 能力弱用小样本任务逐个验证换参数更大的模型简化工具数量降低 temperature批量任务中途卡住并发过高触发限流或某个任务异常查看服务端日志和任务日志降低并发数加超时和失败重试对单个任务设置超时上限接口启动后页面打不开端口被占用或服务未启动检查终端日志和端口监听情况更换端口例如--port 8001或重启服务输出结果里混入编造信息模型忽略了工具结果或工具本身返回错误打印消息序列确认模型是否基于 ToolMessage 回答在 prompt 中强调必须基于工具结果回答工具函数内部做数据校验排查思路遵循一条主线先看消息序列判断是否发生了工具调用再看工具调用参数判断模型是否选对了参数最后看工具返回结果判断数据是否正常。把这三层日志打出来绝大多数问题都能定位。10. 最佳实践与合规建议10.1 工程化建议第一次先跑最小示例只暴露一个工具确认链路通了再加功能。保留一套最小可运行配置出现问题可以快速回退对照。工具描述写清楚三件事工具用途、参数含义、返回值格式。工具内部做异常兜底宁可返回错误字符串也不要让整个图崩溃。模型相关参数单独放配置文件不要硬编码在代码里。批量任务加日志、加超时、加重试避免任务静默失败。接口服务限制访问范围生产环境必须加鉴权。涉及删除、修改、转账等写操作的工具默认拒绝或人工确认后再执行。10.2 安全建议工具调用本质上是把代码执行入口暴露给模型安全边界必须清晰不要给模型暴露无参数约束的任意代码执行工具。文件读写工具要限制可访问目录。网络请求工具要限制可访问域名防止 SSRF 类风险。用户输入可能通过提示注入影响模型调用危险工具在 prompt 和工具层双重约束。涉及人脸、声音、版权素材、个人隐私数据时必须确认合法授权敏感数据优先本地模型处理。10.3 学习建议学 LangGraph 工具调用时不要只复制示例代码。建议按下面顺序做三组实验先修改工具描述观察对模型决策的影响再加第二个工具观察多工具并行执行最后把工具改成真实查询数据库或 HTTP 请求观察实际业务场景下的稳定性和延迟。11. 总结与下一步LangGraph 的工具调用是智能体开发中最值得先投入时间掌握的一节。它解决的不是“模型能不能回答”而是“模型能不能执行”。bind_tools让模型具备请求工具的接口ToolNode负责可靠地执行工具tools_condition控制模型何时继续调用、何时收尾。掌握这条闭环之后再去看条件路由、子图、并行分支、持久化这些 LangGraph 高级特性思路会顺畅很多。最容易踩的坑有三个一是用了不支持 function calling 的模型导致模型从不触发工具二是工具描述写得太含糊模型选错参数三是循环链路异常没有打印消息序列定位不到问题。建议第一次跑通后把模型换成 Ollama 本地模型再跑一遍对比两种场景下工具调用的稳定性和延迟这也是后续做成本选型最直接的参考。下一步可以扩展的方向一是接入create_react_agent之外的持久化checkpointer让智能体具备多轮记忆二是用子图把复杂任务拆成多个可复用模块三是加上流式输出让长工具链路的用户体验更好四是把工具调用做成企业内部的可插拔工具平台统一管理工具注册、鉴权和日志。先跑通这一节的最小闭环后面每一步都有明确抓手。
返回列表