搜索文档
工具的概述
工具是赋予大语言模型与外部世界交互能力的关键组件,从而能让智能体执行搜索、计算、数据库查询、邮件发送或调用第三方API等,进而构建功能强大的AI应用。借助工具,大模型才能从 “认识世界” 走向 “改变世界”。 
工具是构建智能体的核心要素之一!

工具调用
在LangChain中,工具(Tools)实际上是指明确定义了输入和输出的 可调用函数 。因此, 工具调用 (Tool Calling) 也被称为 函数调用(Function Calling) 。
具体有两种调用方式:直接调用和绑定到模型
from langchain_core.tools import tool
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气信息"""
return f"{city} 天气非常好"
print(get_weather.invoke({"city": "北京"}))import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
load_dotenv(verbose=True)
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气信息"""
return f"{city} 天气非常好"
# 创建模型并绑定工具
model = init_chat_model(
model="deepseek-flash",
api_key = os.getenv("DEEPSEEK_API_KEY"),
base_url = os.getenv("DEEPSEEK_API_BASE"),
).bind_tools([get_weather])
res = model.invoke("北京的天气怎么样?")
if res.tool_calls:
print(f"AI 需要调用工具,待调用列表:{res.tool_calls}")
else:
print(f"AI 不需要调用工具,输出结果:{res.content}")工具调用的整体流程

工具定义:不使用 @tool
from langchain.chat_models import init_chat_model
def get_weather(city:str):
return f"{city}天气很好"
model = init_chat_model(
model="ollama:qwen3.5"
).bind_tools([get_weather])
res = model.invoke("北京天气怎么样?")
print(res)convert_to_openai_tool
执行 bind_tools([get_weather]) ,底层最终会调用 convert_to_openai_tool 生成工具描述。
from langchain_core.utils.function_calling import convert_to_openai_tool
def get_weather(city:str):
return f"{city}天气很好"
print(convert_to_openai_tool(get_weather))
''' 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '',
'parameters': {'properties': {'city': {'type': 'string'}}, 'required': ['city'], 'type': 'object'}
}
}
'''parameters 参数解析:
- properties:用于定义JSON 对象(Object)中可以包含哪些属性(键),以及每个属性对应的值类型和说明。
- required:当 type 为 "object" 时使用,是一个数组,列出了对象中必须存在的属性名。
- type:定义当前数据节点必须是什么数据类型。常见类型有 string, number, integer, boolean, object, array, null。object即是json对象。
面试题:为什么不使用@tool装饰器修饰的函数,也可以理解为工具呢?
convert_to_openai_tool 底层源码:
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。
description 说明
convert_to_openai_tool 会从 docstring(文档字符串) 加载工具的描述信息,上面的案例中,docstring 为空,所以抽取的 description 为空。
一个成熟的工具必须具备 docstring(文档字符串)
from langchain_core.utils.function_calling import convert_to_openai_tool
def get_weather(dt:str, city:str="北京"):
""" 查询天气的工具
Args:
dt: 日期
city: 城市名
"""
return f"{dt}:{city}天气很好"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '查询天气的工具',
'parameters': {
'properties': {
'dt': {'description': '日期', 'type': 'string'},
'city': {'default': '北京', 'description': '城市名', 'type': 'string'}
},
'required': ['dt'],
'type': 'object'
}
}
}
"""使用 Args: 、 Returns: 、 Raises: 等关键字,这种方式可读性强。
工具定义:使用 @tool
使用 @tool 装饰器修饰,可以自动将普通 Python 函数转化为智能体可调用的工具。
自定义工具描述:description
@tool 会从 docstring 生成描述信息,同样要求遵循 Google docstring 规范。如果没有 docstring 则报错。
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print
@tool
def get_weather(dt:str, city:str="北京"):
return f"{dt}:{city}天气很好"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
ValueError: Function must have a docstring if description not provided.
"""@tool 的参数 description 可以更改工具描述,优先级高于 docstring 的函数说明。
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print
@tool(description="天气查询工具")
def get_weather(dt:str, city:str="北京"):
"""工具1"""
return f"{dt}:{city}天气很好"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '天气查询工具',
'parameters': {
'properties': {
'dt': {'type': 'string'},
'city': {'default': '北京', 'type': 'string'}
},
'required': ['dt'],
'type': 'object'
}
}
}
"""当我们没有向 @tool 传递 description 参数时,默认情况下, tool 会将 docstring 整体视为 description。
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print
@tool
def get_weather(dt:str, city:str="北京"):
"""工具1
Args:
dt: 具体日期
city: 城市
"""
return f"{dt}:{city}天气很好"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '工具1\n\nArgs:\n dt: 具体日期\n city: 城市',
'parameters': {
'properties': {
'dt': {'type': 'string'},
'city': {'default': '北京', 'type': 'string'}
},
'required': ['dt'],
'type': 'object'
}
}
}
"""通过将 parse_docstring 设置为 True,docstring 会被解析,填充到相应的字段描述中。
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print
@tool(parse_docstring=True)
def get_weather(dt:str, city:str="北京"):
"""工具1
Args:
dt: 具体日期
city: 城市
"""
return f"{dt}:{city}天气很好"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '工具1',
'parameters': {
'properties': {
'dt': {'description': '具体日期', 'type': 'string'},
'city': {
'default': '北京',
'description': '城市',
'type': 'string'
}
},
'required': ['dt'],
'type': 'object'
}
}
}
"""更改工具名称:name_or_callable
默认情况,使用函数名作为工具名称,但可以向 @tool 传参 name_or_callable ,以更改工具名称。
但通常不这么写,工具名称等于函数名。
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print
@tool(name_or_callable='getWeatherPlus')
def get_weather(dt:str, city:str="北京"):
"""工具1"""
return f"{dt}:{city}天气很好"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'getWeatherPlus',
'description': '工具1',
'parameters': {
'properties': {
'dt': {'type': 'string'},
'city': {'default': '北京', 'type': 'string'}
},
'required': ['dt'],
'type': 'object'
}
}
}
"""自定义 args_schema
当工具的参数变得复杂,需要 枚举值 、 范围限制 或 更复杂的业务逻辑验证 时,Pydantic 模型是理想的选择,提供强大的类型检查和数据验证。
使用Pydantic 的主要优势在于能够精确控制工具参数的格式和验证规则,让大模型更准确地理解如何调用工具。
方式1:使用 Pydantic 模型定义
- 创建参数说明类
weatherInput此类必须继承BaseModel。 Field():用来定制字段的函数,可用于设置默认值、描述等。- 可以使用
Literal类型限定参数为固定选项。 - 通过
@tool(args_schema=weatherInput)将这个 Pydantic 模型与工具函数关联。
from typing import Literal
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from pydantic import BaseModel, Field
from rich import print
class weatherInput(BaseModel):
city: str = Field(description="城市")
unit: Literal["摄氏度", "华氏度"] = Field(
description="温度单位",
default="摄氏度"
)
@tool(description="查询天气的工具", args_schema=weatherInput)
def get_weather(city, unit):
return f"{city}天气很好,温度20{unit}"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '查询天气的工具',
'parameters': {
'properties': {
'city': {'description': '城市', 'type': 'string'},
'unit': {
'default': '摄氏度',
'description': '温度单位',
'enum': ['摄氏度', '华氏度'],
'type': 'string'
}
},
'required': ['city'],
'type': 'object'
}
}
}
"""方式2:使用 Json Schema 定义
from langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print
args_json = {
'properties': {
'city': {'description': '城市', 'type': 'string'},
'unit': {
'default': '摄氏度',
'description': '温度单位',
'enum': ['摄氏度', '华氏度'],
'type': 'string'
}
},
'required': ['city'],
'type': 'object'
}
@tool(description="查询天气的工具", args_schema=args_json)
def get_weather(city, unit):
return f"{city}天气很好,温度20{unit}"
print(convert_to_openai_tool(get_weather))
""" 输出结果:
{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '查询天气的工具',
'parameters': {
'properties': {
'city': {'description': '城市', 'type': 'string'},
'unit': {
'default': '摄氏度',
'description': '温度单位',
'enum': ['摄氏度', '华氏度'],
'type': 'string'
}
},
'required': ['city'],
'type': 'object'
}
}
}
"""单工具调用
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class ArgsWeather(BaseModel):
city: str = Field(
description="城市信息",
default="北京"
)
next_day: bool = Field(
description="是否显示次日信息",
default=False
)
@tool(description="天气查询工具", args_schema=ArgsWeather)
def get_weather(city, next_day):
res = f"{city}今天天气很好"
if next_day:
res += "明天下雨"
return res
model = init_chat_model(
model="ollama:qwen3.5"
).bind_tools([get_weather])
messages = [HumanMessage("北京的天气怎么样?明天的天气怎么样")]
res = model.invoke(messages)
messages.append(res)
if res.tool_calls:
for tool in res.tool_calls:
if tool["name"] == "get_weather":
messages.append(get_weather.invoke(tool))
res = model.invoke(messages)
messages.append(res)
for msg in messages:
msg.pretty_print()
""" 输出结果:
================================ Human Message =================================
北京的天气怎么样?明天的天气怎么样
================================== Ai Message ==================================
Tool Calls:
get_weather (dd4faa84-6738-40f4-82bb-3595ae7fa845)
Call ID: dd4faa84-6738-40f4-82bb-3595ae7fa845
Args:
city: 北京
next_day: True
================================= Tool Message =================================
Name: get_weather
北京今天天气很好明天下雨
================================== Ai Message ==================================
北京今天天气不错,很晴朗;不过明天会下雨,出门记得带伞哦。
"""多工具调用
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from pydantic import BaseModel, Field
class ArgWeather(BaseModel):
city: str = Field(
description="城市",
default="北京"
)
@tool(description="天气查询工具", args_schema=ArgWeather)
def get_weather(city):
return f"{city} 天气晴朗"
@tool(description="新闻查询工具", args_schema=ArgWeather)
def get_news(city):
news = {
"北京": ["北京新闻1", "北京新闻2", "北京新闻3"],
"上海": ["上海新闻4", "上海新闻5", "上海新闻6"]
}
return news.get(city, [])
model = init_chat_model(
model="ollama:qwen3.5"
).bind_tools([get_weather, get_news])
messages = [HumanMessage("北京今天的天气怎么样?近期有没有什么新闻?")]
while True:
res = model.invoke(messages)
messages.append(res)
if not res.tool_calls:
break
for tool in res.tool_calls:
if tool["name"] == "get_weather":
messages.append(get_weather.invoke(tool))
elif tool["name"] == "get_news":
messages.append(get_news.invoke(tool))
for msg in messages:
msg.pretty_print()
""" 输出结果:
================================ Human Message =================================
北京今天的天气怎么样?近期有没有什么新闻?
================================== Ai Message ==================================
Tool Calls:
get_weather (36926a53-4d57-4941-bfae-27af987260ea)
Call ID: 36926a53-4d57-4941-bfae-27af987260ea
Args:
city: 北京
get_news (6e79651b-76d9-4d07-bb13-934d6eeb5a72)
Call ID: 6e79651b-76d9-4d07-bb13-934d6eeb5a72
Args:
city: 北京
================================= Tool Message =================================
Name: get_weather
北京 天气晴朗
================================= Tool Message =================================
Name: get_news
['北京新闻1', '北京新闻2', '北京新闻3']
================================== Ai Message ==================================
今天的北京天气晴朗。最近有一些新闻,包括“北京新闻1”、“北京新闻2”和“北京新闻3”。如果您需要更具体的信息,可以告诉我!
"""强制使用工具
bind_tools 可以传递参数 tool_choice ,用于控制是否强制使用工具。
该字段最终会作为 payload 的 tool_choice 字段传递给模型,OpenAI和Deepseek的官方API服务对于 tool_choice 的取值做了相同的规定。
tool_choice 的取值:
none:模型不会调用任何工具。auto:默认值,模型可以自主决定不调用或调用任意数量的工具。required:模型必须调用工具,数量不限。
此外, tool_choice 还支持传递 any ,等价于 required 。
当 tool_choice 值为工具名时表示使用特定工具。
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
from langchain_core.tools import tool
from pydantic import BaseModel, Field
from rich import print as rprint
load_dotenv(verbose=True)
class ArgWeather(BaseModel):
city: str = Field(
description="城市",
default="北京"
)
@tool(description="天气查询工具1", args_schema=ArgWeather)
def get_weather1(city):
return f"{city} 天气晴朗"
@tool(description="天气查询工具", args_schema=ArgWeather)
def get_weather2(city):
return f"{city} 会下雨"
model = init_chat_model(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url=os.getenv("DEEPSEEK_API_BASE_URL"),
).bind_tools([get_weather1, get_weather2], tool_choice="get_weather2")
rprint(model.invoke("北京今天天气怎么样?"))
""" 输出结果:
AIMessage(
...
tool_calls=[
{
'name': 'get_weather2',
'args': {'city': '北京'},
'id': 'call_00_KAYBpRVJwXTd5WdePiCI8078',
'type': 'tool_call'
}
],
...
)
"""