假设我们有:
def read_file(
path: str,
encoding: str = "utf-8",
) -> str:
"""读取指定文本文件。"""
Python 已经包含很多信息:
函数名:
read_file
描述:
读取指定文本文件。
参数:
path
encoding
类型:
str
str
必填:
path
可选:
encoding
我们完全可以通过 inspect 自动提取。
这里其实已经出现了自动生成 Schema 的核心思路:函数签名里本来就同时放着“参数名、类型、默认值”三类信息,我们做的只是把它们翻译成模型侧的结构。下面这张图可以把“Python 世界”和“Schema 世界”的对应关系先建立起来。
![图片[1]-小节二:从 Python 类型标注自动生成 Schema——真正把重复劳动交给程序-大赛人网](https://www.dsrw.com/wp-content/uploads/2026/11/image-1478.png)
尤其要分清两个维度:类型标注决定值允许是什么,默认值决定参数能不能省略。 所以 Optional[str] 和“有默认值”不是一回事,这也是后面生成 required 时最容易写错的地方。
这里有一个 Python 3.9 下很容易忽略的细节。代码如果启用了:
from __future__ import annotations
注解可能会以字符串形式保存。此时 parameter.annotation is str 不一定成立,即使源码里明明写了 path: str。因此后面的完整实现会先调用 typing.get_type_hints(),把注解解析成真实类型,再交给转换器。少了这一步,生成器往往不会报错,只会悄悄把类型映射错,这反而更难排查。
Python 函数签名里已经藏着协议
使用:
inspect.signature(func)
可以拿到:
(path: str, encoding: str = 'utf-8') -> str
再遍历:
signature.parameters
就能拿到每个参数。
先建立类型映射
最基础:
PYTHON_TO_JSON_TYPE = {
str: "string",
int: "integer",
float: "number",
bool: "boolean",
}
然后:
annotation = parameter.annotation
如果是:
str
转:
string
如何判断 required?
Python 参数:
inspect.Parameter.empty
表示没有默认值。
所以:
if parameter.default is inspect.Parameter.empty:
required.append(name)
这就自动生成必填字段。
自动生成最小 Schema
下面代码完整可运行:
输出会接近:
{
"name": "read_file",
"description": "读取指定文本文件。",
"parameters": {
"type": "object",
"properties": {
"path": {
"type": "string"
},
"encoding": {
"type": "string"
}
},
"required": [
"path"
]
}
}
现在 Python 函数一改:
def read_file(
path: str,
encoding: str = "utf-8",
max_chars: int = 10000,
):
...
Schema 会自动跟着变化。
这就是自动生成的价值。
list 怎么处理?
例如:
def read_files(
paths: list[str],
):
...
Schema 应该更像:
{
"type": "array",
"items": {
"type": "string"
}
}
所以类型映射不能永远只是一个 dict。
我们需要读取:
typing.get_origin()
typing.get_args()
Optional 怎么处理?
例如:
from typing import Optional
def search_files(
keyword: str,
suffix: Optional[str] = None,
):
...
这里 suffix 可以是 string,也可以为空。
Python 3.9 中使用:
Optional[str]
本质上是联合类型。
我们可以生成:
{
"type": [
"string",
"null"
]
}
或者采用适合目标 Provider 的兼容形式。
这里还要分清两件经常被混在一起的事:可为空 和可省略 不是同一个意思。
suffix: Optional[str] 必须传,但值可以是 null
suffix: str = ".py" 可以不传;传入时应是字符串
suffix: Optional[str] = None 可以不传,也可以显式传 null
类型标注决定一个值能不能是 null,有没有默认值决定参数是否进入 required。只看见 Optional 就从 required 中删除,是很常见的 Schema 生成错误。
课程这里先理解原理。
不同模型 Provider 最终如何包装 Schema,会留在 Provider Adapter 层。
一个增强版类型转换器
下面代码支持:
str
int
float
bool
list[T]
dict[str, T]
Optional
关于可变默认值的一个额外提醒
上面为了展示 Schema,我们写了:
roots: list[str] = ["."]
真实代码里不建议把可变 list 直接作为默认值。
更稳的是:
roots: Optional[list[str]] = None
然后函数内部:
if roots is None:
roots = ["."]
这不是 Tool Schema 特有问题,而是 Python 本身的重要工程习惯。
自动生成并不意味着“完全不用人工设计”
这是本节非常重要的一个观点。
自动生成最适合处理:
name
参数名
参数类型
required
但 Tool Description、参数语义、业务限制,很多时候仍然需要人工设计。
例如:
def query_metric(
metric: str,
):
"""查询指标"""
虽然 Schema 能自动生成,但模型仍然不知道:
metric 能填什么?
收入叫 revenue 还是 income?
什么时候用这个 Tool?
所以后面我们还要给参数增加描述、枚举、范围等能力。















请登录后查看评论内容