LangGraph 工具完整介绍(分基础概念、两种工具模式、完整流程、实战代码)
LangGraph 工具完整介绍(分基础概念、两种工具模式、完整流程、实战代码)
一、什么是 LangGraph 工具
LangGraph 工具 =可供大模型自动调用的外部能力,用于弥补 LLM 短板:
- 实时计算、联网搜索、数据库查询、API 调用、文件读写、RAG 检索等
- 核心两类使用场景:
- 前置规则工具:代码路由提前判断(你之前的
classify_node判断数学问题,直接走计算器分支) - LLM 自动工具调用(ReAct 循环):模型自主判断要不要调用工具,多轮循环直到给出答案
- 前置规则工具:代码路由提前判断(你之前的
配套核心依赖
bash
运行
pip install langgraph langchain langchain-openai python-dotenv二、工具的两种定义方式
方式 1:@tool 装饰器(最常用,你计算器用的这种)
python
运行
from langchain.tools import tool @tool def calculator_tool(expression: str) -> str: """ 计算基础数学表达式 Args: expression: 四则运算字符串,如 "10 + 20 * 3" """ try: allowed_names = {"abs": abs, "round": round, "sum": sum, "int": int, "float": float} res = eval(expression, {"__builtins__": {}}, allowed_names) return str(res) except Exception as e: return f"计算错误: {str(e)}"关键特性:
- 自动解析文档字符串给 LLM 看用途、参数格式
- 自动生成 JSON Schema,大模型能看懂传参规则
- 直接放入工具列表
tools = [calculator_tool]即可绑定模型
方式 2:自定义 BaseTool(复杂工具,多参数 / 异步)
适合需要复杂初始化、鉴权、异步请求的场景,日常简单函数优先用@tool。
三、LLM 绑定工具:bind_tools ()
模型必须绑定工具列表,才具备生成工具调用指令的能力:
python
运行
from langchain_openai import ChatOpenAI import os from dotenv import load_dotenv load_dotenv() llm = ChatOpenAI( model=os.getenv("OPENAI_MODEL"), api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_API_BASE"), temperature=0 # 工具调用必须0,稳定无随机输出 ) # 绑定计算器工具 tools = [calculator_tool] llm_with_tools = llm.bind_tools(tools)作用:
- LLM 收到用户提问后,判断需要计算时,输出带
tool_calls的AIMessage tool_calls包含:工具名、参数、唯一调用 ID,给工具节点执行
四、LangGraph 内置工具执行节点:ToolNode
官方预制工具执行器,不用手动写循环分发逻辑,一键执行所有模型发起的工具调用
python
运行
from langgraph.prebuilt import ToolNode # 传入工具列表,生成工具执行节点 tool_node = ToolNode(tools)内部自动逻辑(无需手动实现):
- 读取 state 中最后一条 AI 消息的
tool_calls - 循环匹配对应工具、执行函数
- 生成
ToolMessage携带返回结果,放回 messages 列表,维持对话上下文匹配
五、标准工具调用工作流(ReAct 循环架构)
流程图
START → LLM节点(生成回答/工具调用) → 条件路由判断
- 有
tool_calls→ 进入ToolNode执行工具 → 回到 LLM 整合结果 - 无
tool_calls→END结束输出答案
完整标准组件
- State(状态):存储对话消息,通用内置
MessagesState - LLM Node:调用绑定工具的模型,生成 AIMessage
- ToolNode:执行所有工具调用
- 路由函数 should_continue:条件分支判断是否要执行工具
- add_conditional_edges:实现循环逻辑
完整可运行标准工具调用代码
python
运行
from typing import Literal from langgraph.graph import StateGraph, START, END from langgraph.prebuilt import ToolNode, MessagesState from langchain_openai import ChatOpenAI from langchain.tools import tool import os from dotenv import load_dotenv load_dotenv() # 1. 定义工具 @tool def calculator_tool(expression: str) -> str: """计算基础数学表达式,支持四则运算、abs、round""" try: allowed_names = {"abs": abs, "round": round, "sum": sum, "int": int, "float": float} res = eval(expression, {"__builtins__": {}}, allowed_names) return str(res) except Exception as e: return f"计算错误: {str(e)}" tools = [calculator_tool] tool_node = ToolNode(tools) # 2. 初始化绑定工具的LLM llm = ChatOpenAI( model=os.getenv("OPENAI_MODEL"), api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_API_BASE"), temperature=0 ) llm_with_tools = llm.bind_tools(tools) # 3. LLM节点函数 def llm_agent(state: MessagesState): messages = state["messages"] response = llm_with_tools.invoke(messages) return {"messages": [response]} # 4. 路由函数:判断是否需要调用工具 def should_continue(state: MessagesState) -> Literal["tools", END]: last_msg = state["messages"][-1] # 如果存在工具调用,去执行工具;否则直接结束 if last_msg.tool_calls: return "tools" return END # 5. 构建图 builder = StateGraph(MessagesState) # 注册节点 builder.add_node("agent", llm_agent) builder.add_node("tools", tool_node) # 入口 builder.add_edge(START, "agent") # 条件分支:agent输出后分流 builder.add_conditional_edges("agent", should_continue) # 工具执行完回到大模型重新整合答案 builder.add_edge("tools", "agent") graph = builder.compile() # 测试调用 res = graph.invoke({"messages": [("human", "125乘以36再减去200等于多少")]}) print(res["messages"][-1].content)六、你之前业务场景:前置关键词路由 + 工具分流(非自动 ReAct)
区别于模型自动调用工具,代码提前判断分支,就是你写的classify_node+add_conditional_edges模式:
- 关键词匹配判断是否数学问题
- 条件边分流:数学走计算器节点,普通问题走通用问答节点
python
运行
from typing import TypedDict from langgraph.graph import StateGraph, START, END # 自定义状态 class State(TypedDict): query: str is_math: bool # 工具函数(计算器) @tool def calculator_tool(expression: str) -> str: try: allowed_names = {"abs": abs, "round": round, "sum": sum, "int": int, "float": float} res = eval(expression, {"__builtins__": {}}, allowed_names) return str(res) except Exception as e: return f"计算错误: {str(e)}" # 分类节点:关键词判断 def classify_node(state: State): query_lower = state["query"].lower() math_keywords = ["+", "-", "*", "/", "plus", "minus", "times", "divided", "calculate", "sum"] is_math = any(kw in query_lower for kw in math_keywords) return {"is_math": is_math} # 路由函数:根据标记分流 def route_query(state: State) -> Literal["calc_node", "qa_node"]: if state["is_math"]: return "calc_node" return "qa_node" # 业务节点 def calc_node(state: State): # 提取表达式调用计算器 expr = state["query"].replace("calculate ", "").replace("sum of ", "") result = calculator_tool.invoke(expr) return {"answer": f"计算结果:{result}"} def qa_node(state: State): return {"answer": f"普通问答回答:{state['query']}"} # 搭建流程图 builder = StateGraph(State) builder.add_node("analyze", classify_node) builder.add_node("calc_node", calc_node) builder.add_node("qa_node", qa_node) builder.add_edge(START, "analyze") # 条件分支分流 builder.add_conditional_edges( source="analyze", path_fn=route_query, path_map={ "calc_node": "calc_node", "qa_node": "qa_node" } ) builder.add_edge("calc_node", END) builder.add_edge("qa_node", END) graph = builder.compile() # 测试 print(graph.invoke({"query": "calculate sum of 10 and 90"}))七、两种工具使用模式对比
表格
| 模式 | 适用场景 | 核心特点 |
|---|---|---|
| LLM 自动工具调用(ToolNode+ReAct 循环) | 不确定是否需要工具、多工具混合(搜索 / 计算 / 数据库) | 模型自主决策,支持多轮循环,通用 Agent 标准方案 |
| 前置代码路由工具(关键词 / 规则分流) | 固定关键词可预判场景(数学、翻译、天气) | 速度更快,无需大模型决策分支,简单业务优先 |
八、关键知识点与踩坑点
1. bind_tools 必须搭配 temperature=0
温度大于 0 会随机丢失工具调用逻辑,数学 / 工具场景强制 0。
2. ToolNode 依赖 MessagesState
内置工具节点需要 state 包含messages列表,存储 HumanMessage/AIMessage/ToolMessage。
3. 循环逻辑必须手动加边builder.add_edge("tools", "agent")
工具执行结果必须回传给大模型,让模型整合工具输出给出自然语言答案,否则流程直接中断。
4. @tool 函数参数必须标注类型
expression: str,LangChain 自动解析 Schema 给 LLM,无类型标注会导致模型传参失败。
5. eval 安全要点(计算器工具)
必须清空__builtins__+ 自定义白名单,防止代码注入攻击,线上环境不可省略。
九、拓展:多工具共存
可同时放入计算器、搜索、天气等多个工具,LLM 会自动选择匹配的工具调用:
python
运行
@tool def get_weather(city: str): """查询城市天气""" return f"{city} 今日26℃,晴天" tools = [calculator_tool, get_weather] llm_with_tools = llm.bind_tools(tools) tool_node = ToolNode(tools)