当前位置: 首页 > news >正文

Langgraph 5. 工具使用 Tool Use(Function Calling)

摘要:工具使用(Tool Use,常以 Function Calling 实现)让智能体突破纯文本生成,能够按需调用外部能力——查信息、算数据、调 API、执行代码等。本文说明该模式的动机、典型流程,以及如何用LangGraph状态图 + Agent 节点 + 条件边 + ToolNode实现一个可多步调用工具的研究助理,配套示例包含信息检索、股价查询与数学计算三类工具。

关键词:工具使用;Tool Use;Function Calling;LangGraph;ToolNode;bind_tools;状态图;条件边

配套代码目录:LangGraph 5. Tool Use 源代码


1 为什么需要「工具使用」?

前几章我们已经见过:链式(Chaining)按顺序执行步骤,路由(Routing)按条件选路径,并行(Parallelization)同时跑多条分支,反思(Reflection)通过「生成 → 评审 → 修订」提升质量。但所有这些都建立在「模型根据已有上下文生成文本」的基础上。大模型的知识来自训练数据,无法直接访问实时天气、数据库、计算器或外部 API;若没有与外部世界的接口,智能体就无法完成「查一下伦敦现在几度」「算 100 股 AAPL 市值」这类需要即时数据或确定计算的任务。

工具使用(Tool Use)就是在智能体中引入可调用的外部能力:把「查天气」「查股价」「算表达式」等封装成工具,用名称、描述和参数告诉模型;模型根据用户问题决定是否调用、调用哪个、传什么参数;框架负责执行工具并把结果塞回对话,模型再据此生成最终回复。这样,智能体就从「只会说」变成「能查、能算、能动手」,真正与外部系统协作。

💡理解要点:Tool Use =给模型一双“手”:模型负责「想」(何时用、用哪个、传啥),执行层负责「做」(真正跑函数/API),结果再回馈给模型做总结或下一步决策。


2 工具使用在解决什么问题?

想象这些场景:

  • 信息检索:用户问「法国首都是哪?」「伦敦天气怎么样?」——模型训练数据里虽有答案,但若希望统一走“查一查”的流程(例如对接真实搜索或知识库),就需要一个「查信息」工具,由模型决定查询词并解析结果。
  • 数据 + 计算:用户问「AAPL 现在多少钱?我持有 100 股,总市值多少?」——需要先查股价(外部数据),再算 价格 × 100(精确计算)。模型不擅长精确算术,且股价实时变化,必须通过「股价工具 + 计算工具」两步完成。
  • 执行代码:用户给一段 Python 问「这段代码输出啥?」——需要在沙箱里执行代码并把输出返回给模型,再由模型解释。这同样依赖「代码执行」这一外部能力。

工具使用模式把「该不该用外部能力」和「用哪个、传什么」交给模型,把「真正执行」交给框架与工具实现。这样既保留了自然语言交互的灵活性,又补上了实时性、精确性与可执行性的短板。

🔍实际例子:就像你问助理「明天北京飞上海的早班机有哪些?」——助理不会「硬想」,而是去查航班系统(工具),把结果拿回来再组织成你能听懂的回复。Tool Use 就是给 LLM 配好这样的「可查、可算、可执行」的接口。


3 工具使用的典型流程

工具使用通常包含以下步骤(可循环多轮):

  1. 工具定义(Tool Definition):将外部能力封装成「工具」,并给出模型能理解的名称、描述和参数(含类型与说明)。在 LangChain/LangGraph 中常用@tool装饰器,模型会看到函数的 docstring 与参数 schema。
  2. 模型决策(LLM Decision):模型收到用户请求与工具列表后,判断是否需要调用工具;若需要,则选择工具并从自然语言中抽取参数(如地点、股票代码、数学式子)。
  3. 工具调用生成(Function Call Generation):模型输出结构化请求(如 JSON),包含工具名与参数。支持 Function Calling 的接口会直接返回tool_calls列表,由框架解析。
  4. 工具执行(Tool Execution):框架根据tool_calls找到对应工具、传入参数并执行,得到返回值(字符串或可序列化结果)。
  5. 观察/结果(Observation):执行结果被包装成ToolMessage(或等价结构),追加到对话历史中,作为「工具返回」的上下文。
  6. 模型再处理(Optional):模型看到用户问题 + 自己之前的 tool_calls + 工具结果,决定是再调其他工具(多步)还是生成最终回复给用户。

在多步场景下,步骤 3~6 会重复,直到模型不再发起 tool_calls,而是直接输出面向用户的回答。

💡理解要点「想」和「做」分离——模型只负责「想」(选工具、填参数),执行与结果回传由框架和工具完成;LangGraph 的ToolNode就是「做」的那一环,条件边根据「有没有 tool_calls」决定是继续执行工具还是结束。


4 常见应用场景

场景典型工具收益
信息检索搜索 API、知识库查询、天气 API获取训练数据外或实时信息
数据库与 API查库存、订单状态、支付接口与业务系统联动,完成查询或操作
计算与数据分析计算器、公式求值、统计/表格精确计算、避免模型算术错误
发送通信发邮件、发消息、通知触发真实世界动作
执行代码沙箱代码执行、Notebook 内核运行用户代码并解释结果
控制设备/系统智能家居、IoT、工作流触发与物理世界或其他系统交互

工具使用是智能体从「纯对话」走向「可感知、可推理、可行动」的基础能力;与 Chaining、Routing、Reflection 等组合后,可以构建出非常强大的多步、多角色工作流。


5 LangGraph 中的工具使用:状态图 + Agent + 条件边 + ToolNode

LangGraph 用状态图描述「用户消息 → 模型(可能带 tool_calls)→ 按需执行工具 → 结果回传模型 → 直至无 tool_calls 结束」的流程:节点对应「模型推理」与「工具执行」,条件边根据最后一条消息是否包含tool_calls决定下一跳。

  • 状态(State):图中流转的共享数据。工具调用场景下通常包含消息列表messages(HumanMessage、AIMessage、ToolMessage),且使用add_messages归约:新消息追加而非覆盖,便于多轮对话与多步工具调用。
  • Agent 节点:将「系统提示 + 当前消息列表」送入已绑定工具的 LLMllm.bind_tools(tools))。LLM 返回的AIMessage可能带tool_calls,表示本轮要调用的工具及参数。
  • 条件边:若最后一条消息存在tool_calls,则进入工具节点;否则进入END,表示模型已给出最终回复。
  • 工具节点(ToolNode):LangGraph 预构建的ToolNode(tools)会读取状态中最后一条AIMessagetool_calls,依次执行对应工具,将每个结果封装为ToolMessage并追加到messages,然后通常回到 Agent 节点,让模型根据工具结果继续推理或生成回复。

这样就把「谁在什么时候调用模型、谁在什么时候执行工具、何时停止」都固化在图结构里,便于维护和扩展。

START

Agent: LLM + bind_tools

有 tool_calls?

ToolNode 执行工具

END


6 配套代码结构概览

本示例实现一个研究助理:具备信息检索(模拟)、股票价格查询(模拟)与数学表达式计算三类工具,可回答「法国首都是哪」「伦敦天气」「AAPL 股价及 100 股市值」等问题。运行方式见 demo_codes/README.md。

6.1 状态定义

图的状态仅包含消息列表,并使用add_messages归约,使每条新消息(AIMessage、ToolMessage)都追加到列表末尾(注意,不是覆盖,而是像List那样,新的消息会append在之前消息的后面):

# 摘自 demo_codes/tool_use_graph.pyfromtypingimportAnnotatedfromlanggraph.graph.messageimportadd_messagesclassToolUseState(TypedDict):messages:Annotated[list[BaseMessage],add_messages]

6.2 工具定义(tools.py)

使用 LangChain 的@tool装饰器,让普通函数变成模型可见、可调用的工具;docstring 与参数会作为「工具描述」传给模型:

# 摘自 demo_codes/tools.pyfromlangchain_core.toolsimporttool@tooldefsearch_information(query:str)->str:"""根据给定主题检索事实性信息。适用于诸如「法国首都是哪」「伦敦天气如何」等查询。"""# ... 模拟检索逻辑,返回字符串@tooldefget_stock_price(ticker:str)->float:"""查询指定股票代码的最新(模拟)价格。输入为股票代码,如 AAPL、GOOGL。"""# ... 模拟价格字典,无效代码抛 ValueError@tooldefcalculate_expression(expression:str)->str:"""计算一个数学表达式的值。仅支持基本运算:+、-、*、/、**、括号。"""# ... 安全 eval,仅允许数字与运算符

💡理解要点工具描述(docstring + 参数名/类型)是模型「选工具、填参数」的主要依据,写清楚用途和参数含义能显著提高调用准确率。

6.3 Agent 节点与 bind_tools

Agent 节点负责:在消息列表前注入系统提示(研究助理 + 工具使用规范),然后调用llm.bind_tools(tools)得到的可调用模型;返回的AIMessage可能带tool_calls,由条件边决定是否进入 ToolNode:

def_agent_node(state:ToolUseState)->dict:""" Agent 节点:将系统提示与历史消息送入已绑定工具的 LLM, 返回 AIMessage(可能包含 tool_calls)。 """ifSTEP_VERBOSE:logger.info("[Agent] 调用 LLM(已绑定工具)...")llm=_build_llm()tools=get_all_tools()llm_with_tools=llm.bind_tools(tools)messages=state["messages"]# 若首条不是系统消息,则前置系统提示ifnotmessagesornotisinstance(messages[0],SystemMessage):messages=[SystemMessage(content=SYSTEM_PROMPT)]+list(messages)response=llm_with_tools.invoke(messages)ifSTEP_VERBOSE:ifhasattr(response,"tool_calls")andresponse.tool_calls:fortcinresponse.tool_calls:name=tc.get("name","?")ifisinstance(tc,dict)elsegetattr(tc,"name","?")args=tc.get("args",{})ifisinstance(tc,dict)elsegetattr(tc,"args",{})logger.info("[Agent] 本轮输出: 工具调用 %s(%s)",name,args)else:content_preview=(getattr(response,"content",None)or"")[:150]iflen(str(getattr(response,"content","")or""))>150:content_preview+="..."logger.info("[Agent] 本轮输出: 最终回复(无 tool_calls): %s",content_preview)return{"messages":[response]}

🔍实际例子:用户问「AAPL 股价多少?100 股总市值多少?」时,模型可能先输出tool_calls: [get_stock_price("AAPL")];ToolNode 执行后把价格(如 178.15)以 ToolMessage 形式追加;模型再次被调用时看到「用户问题 + 自己之前的 tool_calls + 工具返回 178.15」,再决定调用calculate_expression("178.15 * 100"),最后用两次工具结果组织成一句回复。

6.4 条件边:是否有 tool_calls

路由函数检查最后一条消息是否为带tool_calls的 AIMessage:是的话,则进入tools节点,否则结束:

def_should_continue(state:ToolUseState)->Literal["tools","__end__"]:""" 条件边:若最后一条为 AIMessage 且含 tool_calls,则进入工具节点;否则结束。 """messages=state["messages"]ifnotmessages:ifSTEP_VERBOSE:logger.info("[路由] 无消息,结束。")return"__end__"last=messages[-1]ifhasattr(last,"tool_calls")andlast.tool_calls:ifSTEP_VERBOSE:logger.info("[路由] 进入工具节点(本轮回调 %s 个工具)",len(last.tool_calls))return"tools"ifSTEP_VERBOSE:logger.info("[路由] 结束,返回最终回复。")return"__end__"

6.5 图构建

将 Agent 节点、ToolNode、START、END 与条件边、工具→Agent 的边组装成图并编译:

# 摘自 demo_codes/tool_use_graph.pydefbuild_tool_use_graph():tools=get_all_tools()tool_node=ToolNode(tools,name="tools")workflow=StateGraph(ToolUseState)workflow.add_node("agent",_agent_node)workflow.add_node("tools",tool_node)workflow.add_edge(START,"agent")workflow.add_conditional_edges("agent",_should_continue,{"tools":"tools","__end__":END})workflow.add_edge("tools","agent")returnworkflow.compile()

完整实现(含系统提示、配置与入口)见源代码中 demo_codes/tool_use_graph.py、demo_codes/main.py、demo_codes/tools.py 与 demo_codes/prompt.py。

最后,我们举个例子,看看输出日志中整个数据流是怎么运转的。比如,用户问了一个问题:“苹果公司股票 AAPL 的当前(模拟)股价是多少?如果我持有 100 股,总市值大约多少?请先查股价再计算。”

日志显示:

2026-03-12 15:19:21 [INFO] tool_use: [Agent] 调用 LLM(已绑定工具)... 问题: 苹果公司股票 AAPL 的当前(模拟)股价是多少?如果我持有 100 股,总市值大约多少?请先查股价再计算。 2026-03-12 15:19:22 [INFO] tool_use: [Agent] 本轮输出: 工具调用 get_stock_price({'ticker': 'AAPL'}) 2026-03-12 15:19:22 [INFO] tool_use: [路由] 进入工具节点(本轮回调 1 个工具) 2026-03-12 15:19:22 [INFO] tool_use: [Tools] 执行工具: get_stock_price({'ticker': 'AAPL'}) 2026-03-12 15:19:22 [INFO] tool_use: [Tools] 工具 get_stock_price 返回: 178.15 2026-03-12 15:19:22 [INFO] tool_use: [Agent] 调用 LLM(已绑定工具)... 2026-03-12 15:19:23 [INFO] tool_use: [Agent] 本轮输出: 工具调用 calculate_expression({'expression': '178.15 * 100'}) 2026-03-12 15:19:23 [INFO] tool_use: [路由] 进入工具节点(本轮回调 1 个工具) 2026-03-12 15:19:23 [INFO] tool_use: [Tools] 执行工具: calculate_expression({'expression': '178.15 * 100'}) 2026-03-12 15:19:23 [INFO] tool_use: [Tools] 工具 calculate_expression 返回: 17815.0 2026-03-12 15:19:23 [INFO] tool_use: [Agent] 调用 LLM(已绑定工具)... 2026-03-12 15:19:24 [INFO] tool_use: [Agent] 本轮输出: 最终回复(无 tool_calls): 苹果公司(AAPL)当前(模拟)股价为 **178.15 美元**。 若您持有 100 股,则总市值约为 **17,815.00 美元**。 2026-03-12 15:19:24 [INFO] tool_use: [路由] 结束,返回最终回复。 回复: 苹果公司(AAPL)当前(模拟)股价为 **178.15 美元**。 若您持有 100 股,则总市值约为 **17,815.00 美元**。

7 小结与延伸

  • 工具使用(Tool Use / Function Calling)让智能体能够按需调用外部能力,获取实时数据、执行精确计算或触发外部系统,是构建「可感知、可推理、可行动」智能体的基础模式。
  • 流程上:工具定义 → 模型决策与生成 tool_calls → 框架执行工具 → 结果回传 → 模型再推理或回复,多步时可循环直至模型不再发起调用。
  • LangGraph中:状态用带add_messagesmessages承载对话与工具结果;Agent 节点使用bind_tools绑定工具并产生可能带tool_calls的 AIMessage;条件边根据是否有 tool_calls 决定进入ToolNodeENDToolNode执行工具并追加 ToolMessage,再回到 Agent,结构清晰、易于扩展。
  • 工具描述(docstring + 参数)和系统提示中的「何时用哪个工具」会显著影响模型行为,建议写清用途与示例。
  • 若需多轮对话记忆,可引入checkpointer(如 MemorySaver),让同一会话的多次 invoke 共享消息历史;若需更复杂的编排(如先路由再工具、多智能体分工),可在现有图上增加节点与条件边,或与 Reflection、Parallelization 等模式组合。
http://www.jsqmd.com/news/467407/

相关文章:

  • 变量的定义与分类
  • 2026年米特科斯鱼片机性价比分析,质量好不好看这里 - 工业品网
  • 多路io(select/epoll)
  • 光伏电池建模及仿真:探索PV曲线与IV曲线的奥秘
  • 2026年上海热门的别墅座椅电梯厂家,Uzin优行值得选吗 - 工业设备
  • 2026做轻量化单兵无人机系统比较好的公司有哪些推荐?猎翼无人机的飞行体验 - 品牌2026
  • 阿里云轻量服务器搭建 WireGuard (wg-easy) 指南
  • DevOps技术面试指南:容器、云原生与内核核心问题
  • ACWing 3497 质数
  • 浙江润鑫轴线车无线汽车称重仪:智能无线传输,称重检测一步到位 - 速递信息
  • 【操作系统学习日记】《现代处理器性能的三重奏:ISA架构、流水线与缓存系统》
  • 基于C# WinForm的PLC通讯上位机开发之旅:Modbus协议与SQL 2008的融合
  • 探索微观孔隙建模插件:开启多领域模拟的新大门
  • 【LeetCode】1. 两数之和(Two Sum)— 哈希表经典题解(C语言)
  • ESP32-S3 基础介绍
  • 探索 COMSOL 中含裂缝地层的流动与传热耦合模拟:油藏数值模拟实战
  • 基于二进制粒子群算法的配电网故障诊断—Matlab 应用选取配电网故障诊断,采用二进制粒子群优化算法
  • 自动药片装瓶机的“神经中枢“是如何炼成的
  • CPU_多线程操作图片_代码详解
  • 纯电动汽车动力经济性仿真:Cruise 与 Simulink 联合之旅
  • 【教学类-133-01】20260309狮虎旗(井字棋)01豆包初稿HTML+ CSS + JavaScript
  • 西门子200smart模拟量处理:滤波与报警的完美结合
  • 从DeepSig RadioML 2018.01A到定制化数据集:单信噪比单调制数据的提取与实战应用
  • 玩转PLC液体混合作业线(附全套工业组态方案)
  • 性价比优先:预算低情景下自动化立体仓库公司的选型指南 - 品牌策略主理人
  • Claude Code Hooks 实战:8大事件与10+脚本的自动化开发指南
  • STM32四轴联动运动控制:直线圆弧插补技术,编码器反馈与加减速控制,原理图和源代码全解析
  • 猎翼无人机,提升探测效率:2026军用目标识别无人机蜂群系统供应商推荐 - 品牌2026
  • 探索风光储交流微网中的双向储能变流器
  • 【小龙虾-OpenClaw】Railway如何部署小龙虾-OpenClaw