🎯 课程主题
掌握 Pydantic 高级特性中的列表提取(List)、嵌套结构(Nested Model)和字段限制条件(Field Constraints)的用法与注意事项。
📝 核心知识点
1. 列表提取 List
- 概念说明:当输入中包含多个同类信息时,使用
list[...]声明列表字段来批量提取。 - 关键细节:
- 定义方式:
persons: list[Person],列表元素的类型可以是基础类型或自定义 Pydantic 模型 - 典型场景:产品评论中的优缺点列表、发票中的商品明细列表、多人物信息提取
- 定义方式:
2. 嵌套结构 Nested Model
- 概念说明:在一个 Pydantic 模型内部引用另一个 Pydantic 模型,构成嵌套的数据结构。
- 关键细节:
- 嵌套层级限制:建议 ≤ 3 层。大模型能力有限,嵌套过深会识别失败
- 必要时拆分多个调用,而非无限加深嵌套
- 嵌套 + 列表的组合场景很常见:如电影的演员列表,每个演员又有名称和饰演角色
3. 字段限制条件 Field Constraints
- 概念说明:通过
Field(ge=0, le=150, min_length=2, max_length=50)等方式为字段添加数值范围、字符串长度等约束。 - 关键细节:
ge/le:大于等于 / 小于等于gt/lt:大于 / 小于min_length/max_length:字符串最小 / 最大长度- 约束会在 Pydantic 实例化时自动校验
4. 平台差异对限制条件的影响
- CloseAI 平台:遇到不满足约束的输入(如价格负数),模型会自动修正(去掉负号),约束校验被绕过
- OpenRouter 平台:遇到不满足约束时,模型会取满足条件的最小值(如 price 负数 → 1.0,stock 负数 → 0),更严谨
🏗️ 架构与工作流
💻 代码实战
列表提取示例
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str = Field(description="姓名")
age: int = Field(description="年龄")
class PersonList(BaseModel):
"""人物列表"""
persons: list[Person] = Field(description="人物列表")
structured_model = model.with_structured_output(PersonList)
result = structured_model.invoke("张三,30岁;李四,40岁")
for p in result.persons:
print(p.name, p.age) # 张三 30 / 李四 40
嵌套结构示例
class Address(BaseModel):
"""地址信息"""
city: str = Field(description="城市")
district: str = Field(description="区域")
class Company(BaseModel):
"""公司信息"""
name: str = Field(description="公司名称")
address: Address = Field(description="公司所在地")
result = structured_model.invoke("阿里巴巴在杭州滨江区")
print(result.name) # 阿里巴巴
print(result.address.city) # 杭州
print(result.address.district) # 滨江区
嵌套 + 列表示例(电影)
class Actor(BaseModel):
name: str = Field(description="演员姓名")
role: str = Field(description="饰演角色")
class Movie(BaseModel):
title: str = Field(description="电影名称")
year: int = Field(description="上映年份")
director: str = Field(description="导演")
rating: float = Field(description="评分")
cast: list[Actor] = Field(description="演员列表") # 列表 + 嵌套
字段限制条件示例
class Product(BaseModel):
name: str = Field(min_length=2, max_length=50, description="产品名称")
price: float = Field(gt=0, description="价格")
stock: int = Field(ge=0, description="库存")
class User(BaseModel):
name: str = Field(min_length=2, max_length=50, description="姓名")
age: int = Field(le=150, description="年龄")
email: str = Field(description="邮箱")
# 不满足约束时会抛出 ValidationError
try:
user = User(name="Tom", age=200, email="tom@test.com")
except ValidationError as e:
print("校验失败:", e)
⚠️ 常见问题与避坑指南
- 嵌套不要超过3层:大模型能力有限,深层嵌套会导致识别率下降,必要时拆分为多次调用
- description 要写清楚:嵌套结构中的每个子模型和每个字段都要写好 description,帮助模型理解
- CloseAI 会"过度修正"输入:违反约束的数值会被自动改为合法值,可能掩盖真实意图
- OpenRouter 更严谨:会取满足约束的最小合法值,行为更可预期
- 三层循环法则类比:编程中很少写超过三层的循环,嵌套结构同理,过深应考虑重构
💡 个人总结与延伸
列表、嵌套和限制条件让 Pydantic 几乎可以描述任意复杂的数据结构,配合 LangChain 的结构化输出,能直接将大段自由文本转化为结构化数据。实际项目中,这些特性结合使用(如嵌套的列表)才是常态。限制条件在 CloseAI 上"静默修正"的行为值得注意——如果你期望严格的校验,推荐 OpenRouter;如果容许模型帮用户纠正输入,CloseAI 反而更方便。