019-JSON-Schema-自动生成

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" 布尔值
listList[T] "array" 数组
dictDict[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?

  1. 类型验证:运行时自动验证参数类型
  2. Schema 生成.model_json_schema() 输出标准 JSON Schema
  3. 文档自动生成: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 中的字段名。如需不同名称,可以使用 AnnotatedFieldalias 参数。

Q: 返回值需要定义 Schema 吗?

@tool 装饰器默认不处理返回值。如需定义返回值 Schema,需要使用 @toolresponse_schema 参数(部分版本支持)。

总结

JSON Schema 自动生成是 @tool 装饰器的核心能力:

  1. Python 类型 → JSON Schema :通过 inspect.signature 和 Pydantic 动态模型实现
  2. Docstring → description:自动从函数文档中提取描述
  3. 默认值 → optional:无默认值的参数自动成为 required
  4. Annotated → 验证规则:通过 Field 添加更细粒度的约束

掌握自动生成机制,编写工具更轻松,AI 调用更准确。


相关推荐
lhjcsubupt1 小时前
第二十二篇 从随机过程到IMU噪声模型
算法·机器学习·概率论
神仙别闹2 小时前
基于C语言处理机调度算法的实现
服务器·c语言·算法
Brilliantwxx2 小时前
【算法从零到千】【16-23】 二分算法
数据结构·算法
8Qi88 小时前
回文子串(Palindromic Substrings)—— 题解
算法·leetcode·职场和发展·动态规划
小宋加油啊12 小时前
机械臂抓取物体 PVN3D算法调研学习
学习·算法·3d
lqqjuly12 小时前
前沿算法深度解析(一)
算法
小欣加油12 小时前
leetcode1926 迷宫中离入口最近的出口
数据结构·c++·算法·leetcode·职场和发展
happymaker062615 小时前
LeetCodeHot100——42.接雨水
算法