一个 Agent 要能干活,除了会调模型,还得会调工具。
模型负责挑工具,LangChain 负责把工具跑起来。
但这之前还有一件事:
工具得先定义出来,模型才知道有这个工具、什么时候该用它。
在 LangChain 里,定义工具有个现成的写法,叫 @tool 装饰器。
一个普通的 Python 函数,加上这个装饰器,就变成了 Agent 能调用的工具。
这篇就把 @tool 的四种写法一次说清。
最基础的写法
先看一个最小的例子:
from langchain_core.tools import tool
@tool
def get_weather(city: str) –> str:
"""查询指定城市的天气"""
return f"{city} 今天晴,26 度"
写一个普通函数,上面加一行 @tool,这个函数就成了工具。
代码里三引号包住的那行说明,在 Python 里有个名字,叫文档字符串。
加装饰器的时候,有三样东西是自动转换的:
| 函数名 get_weather | 工具名 |
| 文档字符串 | 工具描述 |
| 参数的类型注解 | 输入参数的 Schema |
其中文档字符串很重要。
模型主要靠它判断这个工具干什么用、什么时候该调。
不写的话,也可以在装饰器里直接指定 description。
还有一处容易出问题:
导包。
这个装饰器要从 langchain_core.tools 里导,包名别导错。

函数名不直观时,可以自己指定
函数名是给读代码的人看的,不一定适合给模型看。
get_weather 这个名字还算直白,换成项目里那些缩写名,模型未必看得懂。
这时候可以在装饰器里自己指定:
@tool("weather", description="查询指定城市天气的工具")
def get_weather(city: str) –> str:
"""查询指定城市的天气"""
return f"{city} 今天晴,26 度"
装饰器后面第一个位置传一个字符串,工具名就用它,覆盖掉函数名。
再传一个 description 参数,工具描述也跟着覆盖。
改完之后,模型看到的工具叫 weather,描述也换成了你写的那句。
原来的函数名和文档字符串仍然留在代码里,但不再作为这个工具的名称和描述。
参数一多,用 Pydantic 模型来定义
参数少的时候,上面的写法够用。
参数一多、类型一复杂,光靠函数签名和文档字符串就不够了。
Pydantic 是 Python 里做参数校验的一个库。
先定义一个继承 BaseModel 的类,把每个参数写成字段:
from pydantic import BaseModel, Field
class WeatherInput(BaseModel):
location: str = Field(description="城市")
unit: str = Field(description="温度单位")
每个字段后面用 Field 写清楚它是什么,这行说明就是模型看到的参数描述。
然后照常定义函数,装饰器里多传一个 args_schema=WeatherInput,参数就按这个类里写的来:
@tool(args_schema=WeatherInput)
def get_weather(location: str, unit: str) –> str:
"""查询指定城市的天气"""
return f"{location} 的温度是 26 {unit}"
这样做有两个好处。
每个参数的说明就写在它自己的字段上,比全写进文档字符串里清楚。
类型对不对、必填字段缺没缺,Pydantic 都会按你定义的那个类来查。
还有一种 JSON Schema 写法
@tool 也接受直接传一份 JSON Schema,同样挂在 args_schema 上:
weather_schema = {
"type": "object",
"properties": {
"location": {"type": "string", "description": "城市"},
"unit": {"type": "string", "description": "温度单位"},
},
"required": ["location", "unit"],
}
@tool(args_schema=weather_schema)
def get_weather(location: str, unit: str) –> str:
"""查询指定城市的天气"""
return f"{location} 的温度是 26 {unit}"
这种方式灵活,适合已经有现成的 schema,或者参数结构要动态生成的场景。
但它有一个很实际的问题:
手写容易出错。
花括号、引号、字段的对应关系,全靠手敲,错一点整份 schema 就不对了。
所以参数一复杂,优先走 Pydantic 那条路。
四种写法怎么挑
| 基础用法 | 参数少、逻辑简单 | 工具描述要写清楚,模型靠它理解工具 |
| 自定义名称描述 | 函数名不够直观,想给更准的指引 | 名称和描述会覆盖默认值 |
| Pydantic 模型 | 参数多、类型复杂、要校验 | Field 给每个参数写说明,校验交给 Pydantic |
| JSON Schema | 已有现成 schema,或参数结构要动态生成 | 手工写繁琐,容易出错 |

落到实操,可以这么走:
日常定义工具,从基础用法开始,够用就好。
函数名不直观就加个自定义名称,参数一多就换 Pydantic 模型。
JSON Schema 那条路,留给已有现成 schema、或者参数结构要动态生成的场景。



