目录
[二、Tools 工具介绍](#二、Tools 工具介绍)
[2.1 Tools 是什么](#2.1 Tools 是什么)
[2.1.1 Tool 组成结构](#2.1.1 Tool 组成结构)
[2.1.2 Tool 的重要性](#2.1.2 Tool 的重要性)
[2.2 Tools 有什么作用](#2.2 Tools 有什么作用)
[2.3 Tool 的特点](#2.3 Tool 的特点)
[2.4 Tool 使用场景](#2.4 Tool 使用场景)
[3.1 工具的两种调用](#3.1 工具的两种调用)
[3.1.1 直接调用](#3.1.1 直接调用)
[3.1.2 绑定到模型(主流)调用](#3.1.2 绑定到模型(主流)调用)
[3.1.3 工具调用完整流程](#3.1.3 工具调用完整流程)
[3.2 从Message流转看工具的调用](#3.2 从Message流转看工具的调用)
[3.3 工具描述的各部分参数详解](#3.3 工具描述的各部分参数详解)
[3.3.1 convert_to_openai_to](#3.3.1 convert_to_openai_to)
[3.3.2 description 说明](#3.3.2 description 说明)
[3.3.3 参数说明](#3.3.3 参数说明)
[3.3.4 参数类型说明](#3.3.4 参数类型说明)
[3.3.5 参数默认值](#3.3.5 参数默认值)
[3.4 使用@tool装饰器定义工具](#3.4 使用@tool装饰器定义工具)
[3.4.1 自定义工具描述:description](#3.4.1 自定义工具描述:description)
[3.4.2 添加工具描述:description](#3.4.2 添加工具描述:description)
[3.4.3 更改工具名称](#3.4.3 更改工具名称)
[3.5 自定义args_schema](#3.5 自定义args_schema)
[3.5.1 pydantic类型的定义](#3.5.1 pydantic类型的定义)
[3.5.2 限定参数取值范围](#3.5.2 限定参数取值范围)
[3.6 使用Json Schema定义](#3.6 使用Json Schema定义)
一、前言
在使用Langchain 进行智能体开发中,会涉及到一个非常重要的组件调用,就是工具(Tool),工具作为AI应用与大模型的中间层,承载着非常重要的角色,可以说搞清楚了Tool在其中的作用,对AI智能体调用的完整过程就有了一个深刻全面的理解,本篇将详细介绍下Langchain 中的工具使用。
二、Tools 工具介绍
2.1 Tools 是什么
LangChain 工具(Tool)是赋予大模型调用外部能力的标准化接口。
大模型本身只能生成文本,无法主动联网、查数据库、执行代码。工具就是把这些外部能力封装成 LLM 能理解、能调用的函数,让模型从"只会说"变成"能动手做"。
2.1.1 Tool 组成结构
一个工具通常包含三部分:
-
名称(name):唯一标识
-
描述(description):LLM 据此判断何时调用,最关键
-
功能(function):实际执行的代码逻辑
工具包(Toolkit)则是一组相关工具的集合,比如 SQLDatabaseToolkit 把列表、查结构、执行查询等工具打包在一起。
2.1.2 Tool 的重要性
要构建更强大的AI工程应用,只有生成文本这样的" 纸上谈兵 "能力自然是不够的。
工具是赋予大语言模型 与外部世界交互能力 的关键组件,从而能让智能体执行搜索、计算、数据库查询、邮件发送或调用第三方API等,进而构建功能强大的AI应用。借助工具,大模型才能从" 认识世界 "走向" 改变世界 "。

从图中可以看到,工具是构建智能体的核心要素之一,具有非常重要的地位

2.2 Tools 有什么作用
Tool的核心作用是扩展大模型的能力边界,具体包括:
-
突破知识时效性:通过搜索工具获取实时信息,弥补训练数据截止的局限。
-
突破能力限制:通过计算器、代码执行工具完成精确计算和复杂逻辑。
-
连接私有数据:通过数据库、文件系统工具访问企业或个人私有数据。
-
对接外部系统:通过 API 工具调用第三方服务,完成真实业务操作。
-
让 Agent 自主决策:工具是 Agent 的"手脚",Agent 根据任务自主选择并调用合适的工具。
2.3 Tool 的特点
Tool 主要有如下特点:
-
描述驱动:LLM 完全依赖工具的
description来决定是否调用、如何调用,描述写得越清楚,调用越准确。 -
标准化接口:所有工具都遵循统一调用方式(如
.invoke()),便于组合和替换。 -
可自定义:用
@tool装饰器就能把普通 Python 函数变成工具,扩展成本低。 -
可组合:多个工具可同时提供给 Agent,由 Agent 按需调度。
-
安全可控:工具的执行逻辑由开发者定义,可以对输入输出做校验和限制。
2.4 Tool 使用场景
|---------|-----------------------|----------------|
| 场景 | 典型工具 | 解决的问题 |
| 实时信息查询 | DuckDuckGo、SerpAPI | 获取最新新闻、股价、天气 |
| 知识问答 | WikipediaQueryRun | 补充百科类知识 |
| 数据分析/计算 | PythonREPLTool | 精确计算、数据处理 |
| 数据库交互 | SQLDatabaseToolkit | 自然语言查数据库 |
| 文件操作 | FileManagementToolkit | 读写、管理本地文件 |
| 业务系统对接 | OpenAPIToolkit | 按规范调用 REST API |
| 多工具协作 | Agent + 多工具 | 复杂任务自动拆解执行 |
一句话总结:
LangChain 工具是让大模型"能做事"的桥梁,通过标准化封装和描述驱动机制,使 LLM 能自主调用外部能力,适用于一切需要模型与真实世界交互的场景。
三、工具的调用方式
在LangChain中,工具(Tools)实际上是指明确定义了输入和输出的 可调用函数 。因此, 工具调用(Tool Calling) 也被称为 函数调用(Function Calling) 。具体来说主要有2种调用方式,下面分别介绍。
3.1 工具的两种调用
3.1.1 直接调用
直接调用这种方式,适合测试时使用,下面是一个基础的工具调用案例代码
python
from langchain_core.tools import Tool, tool
@tool
def get_weather(city: str) -> str:
"""
获取指定城市的天气信息
参数:
city: 城市名称,如"北京"、"上海"
返回:
天气信息字符串
"""
# 你的实现
return city + "晴天,温度 15°C"
# 使用 .invoke() 方法
result = get_weather.invoke({"city": "北京"})
print(result)
运行一下效果如下

3.1.2 绑定到模型(主流)调用
这种方式,让AI来调用,开发中使用最多的方式
python
from dotenv import load_dotenv
import os
from langchain_openrouter import ChatOpenRouter
# 1、加载配置文件
load_dotenv(override=True)
OPENROUTER_API_KEY = os.getenv ("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv ("OPENROUTER_API_BASE")
# 2、模型初始化
model = ChatOpenRouter(
model="deepseek/deepseek-v3.2",
api_key=OPENROUTER_API_KEY,
base_url=OPENROUTER_API_BASE,
)
from langchain_core.tools import tool
# 定义工具
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气"""
# 你的实现
return "晴天,温度 15°C"
# 绑定工具
model_with_tools = model.bind_tools([get_weather])
# AI 可以决定是否调用工具
response = model_with_tools.invoke("北京天气如何?")
# response = model_with_tools.invoke("2 + 3 = ?")
# 检查 AI 是否要调用工具
if response.tool_calls:
print("AI 想调用工具:", response.tool_calls)
else:
print("AI 直接回答:", response.content)
调用一下,看到下面的效果,在真实的业务场景下,可能会在tool的方法内部对接外部厂商的API ,以获取天气为例,就可以拿到真实的天气信息数据

3.1.3 工具调用完整流程
大模型能根据对话上下文决定何时调用工具以及传递哪些参数。
上面编写的LangChain应用正好对应图中的AI助手或应用
经典流程如下:

3.2 从Message流转看工具的调用
接下来深入了解一下工具内部在代码中是怎么调用的,前提:代码中配置模型初始化
python
from dotenv import load_dotenv
from langchain.messages import HumanMessage, ToolMessage
import os
from langchain_openrouter import ChatOpenRouter
# 1、加载配置文件
load_dotenv(override=True)
OPENROUTER_API_KEY = os.getenv ("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv ("OPENROUTER_API_BASE")
# 2、模型初始化
model = ChatOpenRouter(
model="deepseek/deepseek-v3.2",
api_key=OPENROUTER_API_KEY,
base_url=OPENROUTER_API_BASE,
)
1、不使用@tool修饰
通过定义普通的方法形式使用,完整代码如下
python
from dotenv import load_dotenv
from langchain.messages import HumanMessage, ToolMessage
import os
from langchain_openrouter import ChatOpenRouter
# 1、加载配置文件
load_dotenv(override=True)
OPENROUTER_API_KEY = os.getenv ("OPENROUTER_API_KEY")
OPENROUTER_API_BASE = os.getenv ("OPENROUTER_API_BASE")
# 2、模型初始化
model = ChatOpenRouter(
model="deepseek/deepseek-v3.2",
api_key=OPENROUTER_API_KEY,
base_url=OPENROUTER_API_BASE,
)
from langchain_core.tools import tool
def get_weather(city: str):
"""获取天气的工具"""
return f"{city}天气晴朗"
# 模型与工具绑定
model_with_tools = model.bind_tools([get_weather])
messages = [
HumanMessage("今天北京天气如何")
]
# 模型生成调用工具请求
response = model_with_tools.invoke(messages)
# 添加AIMessage
messages.append(response)
tool_calls = response.tool_calls
for tool_call in tool_calls:
if tool_call["name"] == "get_weather":
# 拼接出ToolMessage实例
tool_response = ToolMessage(
content=get_weather(**tool_call["args"]),
tool_call_id=tool_call["id"],
name=tool_call["name"]
)
messages.append(tool_response)
print("=====================> messages <=====================")
for msg in messages:
msg.pretty_print()
print("=====================> messages <=====================")
final_response = model_with_tools.invoke(messages)
print(f"final_response: \n{final_response}")

上述代码对应的完整调用流程如下图

2、使用@tool修饰
使用@tool修饰之后,完整的调用代码如下
python
from langchain_core.tools import tool
@tool
def get_weather(city: str):
"""获取天气的工具"""
return f"{city}天气晴朗"
# 模型与工具绑定
model_with_tools = model.bind_tools([get_weather])
messages = [
HumanMessage("今天北京天气如何")
]
# 模型生成调用工具请求
response = model_with_tools.invoke(messages)
# 添加AIMessage
messages.append(response)
tool_calls = response.tool_calls
for tool_call in tool_calls:
if tool_call["name"] == "get_weather":
tool_response = get_weather.invoke(tool_call)
print(type(tool_response))
messages.append(tool_response)
print("=====================> messages <=====================")
for msg in messages:
msg.pretty_print()
print("=====================> messages <=====================")
final_response = model_with_tools.invoke(messages)
print(f"final_response: \n{final_response}")

说明:
被 @tool 修饰的函数可以调用 invoke 接收模型返回的入参信息执行函数,并返回ToolMessage 实例,我们不再需要手动拼接 ToolMessage 。
工具流程调用总结
如果真正要大模型根据工具调用结果进行回复,完整的调用流程包括如下四个步骤:
-
步骤1:模型绑定工具 :通过model.bind_tools(...)绑定一个或者多个工具
-
步骤2:模型生成工具调用请求 :用户输入问题,调用模型(比如invoke())。如果需要调用工具,模型返回包含工具调用信息(如工具名称和参数)的AIMessage
-
步骤3:开发者手动执行工具 :用户从响应中提取工具调用信息并手动调用对应的工具(比如工具.invoke())
-
步骤4:将工具执行结果ToolMessage传递给模型生成最终结果 :将之前用户提问内容和手动执行工具结果ToolMessage返回模型,模型最终生成回复
特别注意:大模型调用工具是单次推理,直接响应,需要开发者手动执行工具并管理循环,适合简
单、确定的任务。
3.3 工具描述的各部分参数详解
3.3.1 convert_to_openai_to
在上面的代码中,执行 model.bind_tools(get_weather) ,其底层最终会调用 convert_to_openai_tool 生成工具描述。所以可以直接调用后者查看解析后的工具描述。
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
def get_weather(city: str):
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
输入结果如下

上面的结果 字段 说明:
-
type:定义当前数据节点必须是什么数据类型。
- 常见类型有 string, number, integer, boolean, object, array, null。object即是json对象
-
properties:
- 用于定义JSON 对象(Object)中可以包含哪些属性(键),以及每个属性对应的值类型和说明
-
required:
- 当 type为 "object"时使用,是一个数组,列出了对象中必须存在的属性名
到这里有同学有疑问了,为什么不使用@tool装饰器修饰的函数,也可以理解为工具呢?
查看 convert_to_openai_tool 底层源码:
python
elif isinstance(function, langchain_core.tools.base.BaseTool):
oai_function = cast("dict", _format_tool_to_openai_function(function))
elif callable(function):
oai_function = cast(
"dict", _convert_python_function_to_openai_function(function)
)
相当于加了@tool修饰的函数走上面的分支,没有加@tool修饰的函数走下面的分支,后者会基于函数定义和docstring生成pydantic模式的描述,然后转换为规范的tool_schema。
3.3.2 description 说明
convert_to_openai_tool 会从 docstring(文档字符串) 加载工具的描述信息,上面的案例中,docstring 为空,所以抽取的 description 为空。
- docstring,文档字符串,使用三个双引号表示开始和结束
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
def get_weather(city: str):
"""
天气查询工具
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
再次运行,此时在输出结果的描述字段中就可以看到有信息了

3.3.3 参数说明
convert_to_openai_tool 会从 docstring 加载参数说明,这里的 docstring 必须遵循 Google 风格 。
Google 风格 docstring 说明:styleguide | Style guides for Google-originated open-source projectsGoogle 风格 docstring 示例:新建标签页
Python docstring 通用约定:PEP 257 -- Docstring Conventions | peps.python.org
基础用法不必完整阅读规范,只需要按照下面的示例仿写即可。

python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
def get_weather(city: str):
"""
天气查询工具
Args:
city: 城市名称
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
在输出结果中可以看到,参数的详细信息都输出了

使用 Args: 、 Returns: 、 Raises: 等关键字,这种方式可读性强。Agent通过工具的这些注释来理解工具的用途和调用时机,因此清晰、准确的文档字符串是工具能被正确调用的前提。AI 依赖 docstring 来理解工具,下面就是一个好的示例:
python
@tool
def search_products(query: str) -> str:
"""
在产品数据库中搜索产品
Args:
query: 搜索关键词,如"笔记本电脑"、"手机"
Returns:
产品列表的 JSON 字符串
"""
...
3.3.4 参数类型说明
参数类型来源于函数的类型注解,比如下面的代码
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
def get_weather(city):
"""
天气查询工具
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
删除了参数类型注解,则工具描述中也不包含参数类型说明

注意:如果docstring中包含参数说明,则对应的参数必须有类型注解,否则报错,比如下面的代码
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
def get_weather(city):
"""
天气查询工具
Args:
city: 城市名称
"""
return "天气晴朗"
rprint(convert_to_openai_tool(get_weather))
运行直接报下面的错误

3.3.5 参数默认值
如果参数没有默认值,则会包含 在required对应的列表 中。
反之,则参数的描述信息会包含 default 字段,并且 不会出现在required列表 中。
下面的代码中,给入参增加一个默认值
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
def get_weather(city : str = "北京"):
"""
天气查询工具
Args:
city: 城市名称
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
在输出的结果中在default 这里就能看到这个参数默认值了

3.4 使用@tool装饰器定义工具
使用 @tool 装饰器修饰,可以自动将普通 Python 函数转化为智能体可调用的工具。
此方式 最直接 ,代码量极少,非常适合快速验证想法或创建参数简单的工具。
3.4.1 自定义工具描述:description
在下面的代码中,方法使用@tool 修饰,但是没有描述(docstring)信息,运行的时候将会报错
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool
def get_weather(city):
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

添加上去之后就正常了

小结:
-
在bind_tools()调用时,先将函数封装为 BaseTool 类型的对象,再传递给 convert_to_openai_tool 函数,生成工具的描述。
-
@tool 会从 docstring 生成描述信息,同样要求遵循 Google docstring 规范 。如果没有 docstring 则报错,如下。
Traceback...
ValueError: Function must have a docstring if description not provided.
3.4.2 添加工具描述:description
@tool 的参数 description 可以更改工具描述,优先级高于 docstring 的函数说明
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool(description="获取具体城市的天气信息")
def get_weather(city):
"""
获取城市天气信息
Args:
city: 城市名称
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
运行一下可以看到@tool后面的描述信息被输出了

解析docstring:parse_docstring
当我们没有向 @tool 传递 description 参数时,默认情况下, tool 会将 docstring 整体视为 description ,
-
通过将 parse_docstring 设置为True,docstring会被解析,填充到相应的字段描述中
-
不使用 @tool 装饰器时,docstring不合法会被视为普通文本,作为 description ,但如果使用了 @tool 时 docstring 不合法,将会抛出异常
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool(description="获取具体城市的天气信息",parse_docstring=True)
def get_weather(city):
"""
获取城市天气信息
Args:
city: 城市名称
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
下面这样被认定为不合法的话会抛出异常

3.4.3 更改工具名称
默认情况,使用函数名作为工具名称,但可以向@tool 传参 name_or_callable ,以更改工具名称,如下代码,通过name_or_callable 这个参数重新赋值给工具名称
- **注意:**虽然有这种方式能对工具重命名,但在开发中,习惯使用函数名作为工具名称,不推荐自定义工具名称
python
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
@tool(description="获取具体城市的天气信息",name_or_callable="weather_tool")
def get_weather(city):
"""
获取城市天气信息
Args:
city: 城市名称
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
此时工具的名称已经变了

3.5 自定义args_schema
当工具的参数变得复杂,需要 枚举值 、 范围限制 或 更复杂的业务逻辑验证 时,Pydantic 模型是理想的选择,提供强大的类型检查和数据验证。
使用Pydantic 的主要优势在于能够精确控制工具参数的格式和验证规则,让大模型更准确地理解如何调用工具。
3.5.1 pydantic类型的定义
通过继承核心基类 BaseModel 定义数据模型,从而声明字段结构、类型约束、默认值以0及校验规则。
python
from pydantic import BaseModel
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
from langchain_core.tools import tool
class WeatherInput(BaseModel):
city: str
@tool(args_schema=WeatherInput)
def get_weather(city):
"""
获取城市天气信息
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))

WeatherInput 对象中还可以补充更多的信息,如下,增加描述和默认值
python
class WeatherInput(BaseModel):
city: str = Field(
description="具体的城市",
default="北京"
)

3.5.2 限定参数取值范围
某些情况下为了安全起见,对参数的取值范围做限定,可以使用 Literal类型限定参数为固定选项
- Literal :表示字段不能是任意某种类型的值,而只能是几个固定字面量之一
python
class WeatherInput(BaseModel):
city: str = Field(
description="具体的城市",
default="北京"
),
unit: Literal["celsius", "fahrenheit"]

3.6 使用 Json Schema定义
在 LangChain 中,还可以直接使用 JSON Schema 字典 来定义工具的参数模式。这种方式提供了极大的灵活性。因为工具参数模式可以基于数据库配置或用户输入在 运行时动态生成 ,所以这种方式特别适合参数结构需要动态生成的场景。
如下完整的代码
python
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
json_schema = {
'parameters': {
'properties': {
'city': {'type': 'string'},
'unit': {'enum': ['celsius', 'fahrenheit'], 'type': 'string'}
},
'required': ['unit'],
'type': 'object'
}
}
@tool(args_schema=json_schema)
def get_weather(city):
"""
获取城市天气信息
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
运行一下,效果与上面的输出效果一致

四、写在最后
本文详细介绍了Langchain 中工具的调用,并通过 案例的方式详细演示了内部的参数使用,希望对看到的同学有用,本篇到此结束,感谢观看。