上一篇我们讲述了消息与提示词模版,本篇我们看一下如何使用工具。工具是赋予大语言模型 与外部世界交互能力 的关键组件,从而能让智能体执行搜索、计算、数据库查询、邮件发送或调用第三方API等,进而构建功能强大的AI应用。借助工具,大模型才能从“ 认识世界 ” 走向“ 改变世界 ”。
在LangChain中,工具(Tools)实际上是指明确定义了输入和输出的 可调用函数 。因此, 工具调用 (Tool Calling) 也被称为 函数调用(Function Calling) 。
python
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv()
model = init_chat_model(
model="qwen3.7-plus",
model_provider="openai", # 关键:指定使用openai兼容协议
api_key=os.getenv("DASHSCOPE_API_KEY"),
base_url=os.getenv("DASHSCOPE_API_BASE_URL")
)
# 定义工具
@tool
def get_weather(city: str) -> str:
"""获取指定城市的天气"""
# 你的实现
return "晴天,温度 15°C"
# 绑定工具
model_with_tools = model.bind_tools([get_weather])
# AI 可以决定是否调用工具
response = model_with_tools.invoke("北京天气如何?")
# 检查 AI 是否要调用工具
if response.tool_calls:
print("AI 想调用工具:", response.tool_calls)
else:
prAint("AI 直接回答:", response.content)
# AI 想调用工具: [{'name': 'get_weather', 'args': {'city': '北京'}, 'id': 'call_2b913327adab4fcebffdaa13', 'type': 'tool_call'}]
大模型只能分析出是否调用工具,以及传递的参数,具体怎么调用还是得交给ai应用。让我们补充上工具调用的部分
python
from langchain.messages import HumanMessage
@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 = 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}")
输出如下:
plaintext<class 'langchain_core.messages.tool.ToolMessage'> =====================> messages <===================== ================================ Human Message ================================= 今天北京天气如何 ================================== Ai Message ================================== Tool Calls: get_weather (call_24ae81aeea504a42a0dbd842) Call ID: call_24ae81aeea504a42a0dbd842 Args: city: 北京 ================================= Tool Message ================================= Name: get_weather 北京天气晴朗~ final_response: content='今天北京天气晴朗~ ☀️' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 52, 'prompt_tokens': 316, 'total_tokens': 368, 'completion_tokens_details': {'accepted_prediction_tokens': None, 'audio_tokens': None, 'reasoning_tokens': 39, 'rejected_prediction_tokens': None, 'text_tokens': 52}, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0, 'text_tokens': 316}}, 'model_provider': 'openai', 'model_name': 'qwen3.7-plus', 'system_fingerprint': None, 'id': 'chatcmpl-64a3ce5b-0209-949e-8e64-57490fda1146', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--019fd794-e4d0-7d62-953d-17fd215cc6cf-0' tool_calls=[] invalid_tool_calls=[] usage_metadata={'input_tokens': 316, 'output_tokens': 52, 'total_tokens': 368, 'input_token_details': {'cache_read': 0}, 'output_token_details': {'reasoning': 39}}
pythondef get_weather(city: str):
"""获取天气的工具"""
return f"{city}天气晴朗~"
def get_weather(city: str): """获取天气的工具""" return f"{city}天气晴朗~"
response = model_with_tools.invoke(messages)
response的类型是AiMessage,其中关于工具调用的部分如下:
tool_calls=[ { 'name': 'get_weather', 'args': {'city': '北京'}, 'id': 'call_ECvZNV7RLTWpKQSjhvdGzKBd', 'type': 'tool_call' } ]
执行 model.bind_tools([get_weather]) ,底层最终会调用 convert_to_openai_tool 生成工具描述。所以我们可以直接调用后者查看解析后的工具描述。
pythonfrom 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))
输出如下:
json{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '查询天气的工具',
'parameters': {'properties': {'city': {'type': 'string'}}, 'required': ['city'], 'type': 'object'}
}
}
结果字段说明:
convert_to_openai_tool 会从 docstring(文档字符串) 加载工具的描述信息
convert_to_openai_tool 会从 docstring 加载参数说明,这里的 docstring 必须遵循 Google 风格 。
https://google.github.io/styleguide/pyguide.htmlhttps://www.sphinx-doc.org/en/master/usage/extensions/example_google.htmlhttps://peps.python.org/pep-0257/使用 Args: 、 Returns: 、 Raises: 等关键字,这种方式可读性强。Agent通过工具的这些注释来理解工具的用途和调用时机,因此清晰、准确的文档字符串是工具能被正确调用的前提。
pythondef get_weather(city: str) -> str:
"""
天气查询工具
Args:
city: 城市名称
Returns:
该地区的天气情况
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
参数类型来源于函数的类型注解,如果参数没有默认值,则会包含 在required对应的列表 中
json{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '天气查询工具',
'parameters': {
'properties': {'city': {'description': '城市名称', 'type': 'string'}},
'required': ['city'],
'type': 'object'
}
}
}
@tool的方式使用 @tool 装饰器修饰,可以自动将普通 Python 函数转化为智能体可调用的工具。此方式 最直接 ,代码量极少,非常适合快速验证想法或创建参数简单的工具。
pythonfrom langchain_core.tools import tool
from langchain_core.utils.function_calling import convert_to_openai_tool
from rich import print as rprint
@tool
def get_weather(city: str):
"""获取天气的工具"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
@tool 会从 docstring 生成描述信息,同样要求遵循 Google docstring 规范 。如果没有 docstring则报错
@tool 的参数 description 可以更改工具描述,优先级高于 docstring 的函数说明.
pythonfrom langchain.tools import tool
@tool(description="根据城市名称查询当日天气的工具")
def get_weather(city: str):
"""
天气查询工具
"""
return f"{city}天气晴朗"
rprint(convert_to_openai_tool(get_weather))
输出如下:
json{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '根据城市名称查询当日天气的工具',
'parameters': {'properties': {'city': {'type': 'string'}}, 'required': ['city'], 'type': 'object'}
}
}
当我们没有向 @tool 传递 description 参数时,默认情况下, tool 会将 docstring 整体视为description
python@tool
def get_weather(city: str, units: str = "celsius", include_forecast: bool =
False) -> str:
"""
获取当日天气,可选择是否同时查询未来五日天气预报
Args:
city: 城市
units: 气温单位,可选:celsius-摄氏度,fahrenheit-华氏度
include_forecast: 是否包含未来五日的天气预报
"""
temp = 22 if units == "celsius" else 72
result = f'{city}当天气温: {temp} {"摄氏度" if units == "celsius" else "华氏度"}'
if include_forecast:
result += "\n未来五天都是晴天"
return result
rprint(convert_to_openai_tool(get_weather))
输出如下:
json{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '获取当日天气,可选择是否同时查询未来五日天气预报\n\nArgs:\n city: 城市\n units:
气温单位,可选:celsius-摄氏度,fahrenheit-华氏度\n include_forecast: 是否包含未来五日的天气预报',
'parameters': {
'properties': {
'city': {'type': 'string'},
'units': {'default': 'celsius', 'type': 'string'},
'include_forecast': {'default': False, 'type': 'boolean'}
},
'required': ['city'],
'type': 'object'
}
}
}
我们发现解析的description是错误的,
解决办法:通过将 parse_docstring 设置为True,docstring会被解析,填充到相应的字段描述中。@tool(parse_docstring=True)
json{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '获取当日天气,可选择是否同时查询未来五日天气预报',
'parameters': {
'properties': {
'city': {'description': '城市', 'type': 'string'},
'units': {
'default': 'celsius',
'description': '气温单位,可选:celsius-摄氏度,fahrenheit-华氏度',
'type': 'string'
},
'include_forecast': {
'default': False,
'description': '是否包含未来五日的天气预报',
'type': 'boolean'
}
},
'required': ['city'],
'type': 'object'
}
}
}
要注意:使用了@tool(parse_docstring=True) 如果docstring不符合规范,回抛出错误。
默认情况,使用函数名作为工具名称,但可以向@tool 传参 name_or_callable ,以更改工具名称。
pythonfrom langchain.tools import tool
# @tool("getWeather") 也可以省略name_or_callable参数名称
@tool(name_or_callable="getWeather")
def get_weather(city: str):
"""
天气查询工具
"""
return f"{city}天气晴朗"
print(convert_to_openai_tool(get_weather))
当工具的参数变得复杂,需要 枚举值 、 范围限制 或 更复杂的业务逻辑验证 时,Pydantic 模型是理想的选择,提供强大的类型检查和数据验证。 使用Pydantic 的主要优势在于能够精确控制工具参数的格式和验证规则,让大模型更准确地理解如何调用工具。
利用 Pydantic 的类型系统进行参数验证,当大模型需要调用工具前,Pydantic 会自动验证参数的类型和有效性。
① BaseModel基类
通过继承核心基类 BaseModel 定义数据模型,从而声明字段结构、类型约束、默认值以0及校验规则。
python#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@Time : 2026/8/7 08:49
@Author: sql668
@File : pydamic-1.py
"""
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 as rprint
class WeatherInput(BaseModel):
city: str = Field(
default="北京",
description="城市"
)
unit: Literal["celsius", "fahrenheit"] = Field(
default="celsius",
description="气温单位"
)
include_forecast: bool = Field(
default=False,
description="是否包含未来五日天气预报"
)
@tool(args_schema=WeatherInput)
def get_weather(city: str, units: str = "celsius", include_forecast: bool =
False) -> str:
"""
获取当日天气,可选择是否同时查询未来五日天气预报
"""
temp = 22 if units == "celsius" else 72
result = f'{city}当天气温: {temp} {"摄氏度" if units == "celsius" else "华氏度"}'
if include_forecast:
result += "\n未来五天都是晴天"
return result
rprint(convert_to_openai_tool(get_weather))
输出如下:
json{
'type': 'function',
'function': {
'name': 'get_weather',
'description': '获取当日天气,可选择是否同时查询未来五日天气预报',
'parameters': {
'properties': {
'city': {
'default': '北京',
'description': '城市',
'type': 'string'
},
'unit': {
'default': 'celsius',
'description': '气温单位',
'enum': ['celsius', 'fahrenheit'],
'type': 'string'
},
'include_forecast': {
'default': False,
'description': '是否包含未来五日天气预报',
'type': 'boolean'
}
},
'type': 'object'
}
}
}
Field() :用来“ 定制字段 ”的函数,可用于设置默认值、描述等。 每个字段的 description 参数至关重要,它直接影响大模型理解参数含义的能力。
Literal :表示字段不能是任意某种类型的值,而只能是几个固定字面量之一。
在 LangChain 中,还可以直接使用 JSON Schema 字典 来定义工具的参数模式。这种方式提供了极大的灵活性。 因为工具参数模式可以基于数据库配置或用户输入在 运行时动态生成 ,所以这种方式特别适合参数结 构需要动态生成的场景。
pythonjson_schema = {
'properties': {
'city': {'default': '北京', 'description': '城市', 'type': 'string'},
'unit': {
'default': 'celsius',
'description': '气温单位',
'enum': ['celsius', 'fahrenheit'],
'type': 'string'
},
'include_forecast': {
'default': False,
'description': '是否包含未来五日天气预报',
'type': 'boolean'
}
},
'type': 'object'
}
@tool(args_schema=json_schema)
def get_weather(city: str, units: str = "celsius", include_forecast: bool =
False) -> str:
"""
获取当日天气,可选择是否同时查询未来五日天气预报
"""
temp = 22 if units == "celsius" else 72
result = f'{city}当天气温: {temp} {"摄氏度" if units == "celsius" else "华氏度"}'
if include_forecast:
result += "\n未来五天都是晴天"
return result
rprint(convert_to_openai_tool(get_weather))


本文作者:繁星
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!