LangChain 如何让 AI 自动理解你的函数?JSON Schema 自动生成详解
💡 摘要 :本文深入解析
@tool装饰器如何通过 Python 反射和 Pydantic 动态模型,将函数签名自动转换为标准 JSON Schema,让 AI 能够准确理解工具的参数结构。
引言
你写了一个 Python 函数作为 AI 工具:
python
def search_products(category: str, max_price: float = 100.0) -> str:
"""搜索商品"""
return f"找到了 {category} 类商品,价格最高 {max_price} 元"
但 AI 看到的是什么呢?只是一串代码文本 。它不知道 category 是什么、max_price 默认值是多少、返回值是什么格式。
解决方案 :@tool 装饰器会自动将这个函数转换成 AI 能理解的 JSON Schema,让 AI 知道如何正确调用工具。
核心概念
什么是 JSON Schema?
JSON Schema 是一种描述 JSON 数据结构的标准化格式。类比一下:就像料理的菜谱,告诉你需要哪些食材(字段)、每个食材是什么类型(string/number)、哪些是可选的(默认值)。
json
{
"type": "object",
"properties": {
"category": {
"type": "string",
"description": "商品类别"
},
"max_price": {
"type": "number",
"description": "最高价格",
"default": 100.0
}
},
"required": ["category"]
}
为什么需要自动生成?
手动编写 JSON Schema 的问题:
- 繁琐易错:每个工具都要写一遍,参数多了很麻烦
- 难以维护:修改函数签名后要同步更新 Schema
- 类型不一致:手写容易出现类型错误
自动生成:函数即 Schema,代码即文档。
工作原理
处理流程
函数定义
│
▼
┌─────────────────────────┐
│ 1. 解析函数签名 │
│ inspect.signature() │
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ 2. 提取 docstring 描述 │
│ __doc__ │
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ 3. 动态创建 Pydantic 模型 │
│ type.__class__(...) │
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ 4. 生成 JSON Schema │
│ .model_json_schema() │
└─────────────────────────┘
│
▼
StructuredTool 对象
类型映射表
@tool 装饰器将 Python 类型自动映射为 JSON Schema 类型:
| Python 类型 | JSON Schema 类型 | 说明 |
|---|---|---|
str |
"string" |
字符串 |
int |
"integer" |
整数 |
float |
"number" |
浮点数 |
bool |
"boolean" |
布尔值 |
list、List[T] |
"array" |
数组 |
dict、Dict[str, T] |
"object" |
对象 |
Optional[T] |
包含 "null" 的 anyOf |
可选类型 |
Literal["a","b"] |
"enum": ["a","b"] |
枚举 |
Annotated[T, Field(...)] |
添加 description |
带描述的类型 |
代码实战
1. 基础工具定义
python
from langchain_core.tools import tool
@tool
def search_products(category: str, max_price: float = 100.0) -> str:
"""搜索商品
Args:
category: 商品类别(如 "电子产品"、"服装")
max_price: 最高价格,默认 100.0 元
"""
return f"找到了 {category} 类商品,价格最高 {max_price} 元"
2. 查看自动生成的 Schema
python
# 查看生成的 JSON Schema
import json
print("=== 工具名称 ===")
print(search_products.name)
print("\n=== 工具描述 ===")
print(search_products.description)
print("\n=== JSON Schema ===")
print(json.dumps(search_products.args_schema.model_json_schema(), indent=4, ensure_ascii=False))
输出:
=== 工具名称 ===
search_products
=== 工具描述 ===
搜索商品
Args:
category: 商品类别(如 "电子产品"、"服装")
max_price: 最高价格,默认 100.0 元
=== JSON Schema ===
{
"description": "搜索商品\n\nArgs:\n category: 商品类别(如 \"电子产品\"、\"服装\")\n max_price: 最高价格,默认 100.0 元",
"properties": {
"category": {
"title": "Category",
"type": "string"
},
"max_price": {
"default": 100.0,
"title": "Max Price",
"type": "number"
}
},
"required": [
"category"
],
"title": "search_products",
"type": "object"
}
3. 复杂类型:枚举和可选值
python
from typing import Literal
from langchain_core.tools import tool
import json
@tool
def set_priority(level: Literal["low", "medium", "high"], notify: bool = False) -> str:
"""设置任务优先级
Args:
level: 优先级级别
notify: 是否发送通知
"""
return f"已设置优先级为 {level},通知状态: {notify}"
print(json.dumps(set_priority.args_schema.model_json_schema(), indent=4, ensure_ascii=False))
生成的 Schema:
json
{
"description": "设置任务优先级\n\nArgs:\n level: 优先级级别\n notify: 是否发送通知",
"properties": {
"level": {
"enum": [
"low",
"medium",
"high"
],
"title": "Level",
"type": "string"
},
"notify": {
"default": false,
"title": "Notify",
"type": "boolean"
}
},
"required": [
"level"
],
"title": "set_priority",
"type": "object"
}
4. 使用 Annotated 添加详细描述
python
from typing import Annotated
from pydantic import Field
from langchain_core.tools import tool
import json
@tool
def transfer_money(
to_account: Annotated[str, Field(description="目标账户号,8-16位数字")],
amount: Annotated[float, Field(gt=0, description="转账金额,必须大于0")],
remark: Annotated[str | None, Field(default=None, description="备注信息,可选")] = None
) -> str:
"""执行转账操作
Args:
to_account: 目标账户
amount: 转账金额
remark: 备注(可选)
"""
return f"已向账户 {to_account} 转账 {amount} 元"
print(json.dumps(set_priority.args_schema.model_json_schema(), indent=4, ensure_ascii=False))
生成的 Schema 包含验证规则:
json
{
"description": "执行转账操作\n\nArgs:\n to_account: 目标账户\n amount: 转账金额\n remark: 备注(可选)",
"properties": {
"to_account": {
"description": "目标账户号,8-16位数字",
"title": "To Account",
"type": "string"
},
"amount": {
"description": "转账金额,必须大于0",
"exclusiveMinimum": 0,
"title": "Amount",
"type": "number"
},
"remark": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "备注信息,可选",
"title": "Remark"
}
},
"required": [
"to_account",
"amount"
],
"title": "transfer_money",
"type": "object"
}
深入理解
Pydantic 模型动态生成
@tool 装饰器内部会生成一个动态 Pydantic 模型:
python
# 装饰器内部大致等价于:
class SearchProductsArgs(BaseModel):
category: str
max_price: float = 100.0
tool = StructuredTool(
name="search_products",
description="搜索商品...",
args_schema=SearchProductsArgs,
func=search_products
)
为什么用 Pydantic?
- 类型验证:运行时自动验证参数类型
- Schema 生成 :
.model_json_schema()输出标准 JSON Schema - 文档自动生成:Field description 自动转为 Schema description
最佳实践
1. 使用类型注解
python
# ✅ 正确:所有参数都有类型
@tool
def search(query: str, limit: int = 10) -> str:
pass
# ❌ 错误:缺少类型注解
@tool
def search(query, limit=10): # limit 没有类型
pass
2. Docstring 要详细
python
@tool
def get_weather(location: str) -> str:
"""获取天气信息
Args:
location: 城市名称,支持中文或英文
Returns:
天气报告字符串
"""
pass
3. 使用 Annotated 添加验证
python
from typing import Annotated
from pydantic import Field
@tool
def create_user(
name: Annotated[str, Field(min_length=1, max_length=50)],
age: Annotated[int, Field(ge=0, le=150)]
) -> str:
"""创建用户"""
pass
4. 合理使用默认值
python
# ✅ 有默认值的参数放在后面
@tool
def search(query: str, limit: int = 10, offset: int = 0) -> str:
pass
# ❌ 无默认值的参数放在有默认值后面(会导致 required 不明确)
@tool
def search(query: str, limit: int = 10, offset: int) -> str: # offset 没有默认值但在最后
pass
常见问题
Q: 如何查看工具的完整 Schema?
python
import json
print(json.dumps(tool.args_schema.model_json_schema(), indent=4, ensure_ascii=False))
Q: 如何自定义参数名称?
Python 参数名会直接成为 JSON Schema 中的字段名。如需不同名称,可以使用 Annotated 或 Field 的 alias 参数。
Q: 返回值需要定义 Schema 吗?
@tool 装饰器默认不处理返回值。如需定义返回值 Schema,需要使用 @tool 的 response_schema 参数(部分版本支持)。
总结
JSON Schema 自动生成是 @tool 装饰器的核心能力:
- Python 类型 → JSON Schema :通过
inspect.signature和 Pydantic 动态模型实现 - Docstring → description:自动从函数文档中提取描述
- 默认值 → optional:无默认值的参数自动成为 required
- Annotated → 验证规则:通过 Field 添加更细粒度的约束
掌握自动生成机制,编写工具更轻松,AI 调用更准确。