🎯 课程主题
ToolStrategy 的 schema 参数除了 Pydantic 之外,还支持 TypedDict、JSON Schema、@dataclass 以及 Union 联合类型四种定义方式。
📝 核心知识点
1. TypedDict 方式
- 概念说明:使用
typing.TypedDict+Annotated定义字段,输出结果为纯 JSON 字典而非对象。 - 关键细节:
- 继承
TypedDict,字段使用Annotated[类型, FieldInfo(...)]或Annotated[类型, ...]声明。 ...(Ellipsis)表示required=True,即必填字段。- 输出格式与原生的 JSON 一致,无需额外序列化。
- 适用场景:需要最终输出为 JSON 格式、对类型校验要求不高的场景。
- 继承
2. JSON Schema 方式
- 概念说明:直接传入符合 JSON Schema Draft 规范的字典,手动定义所有字段。
- 关键细节:
- 结构为
{"title": "...", "description": "...", "type": "object", "properties": {...}, "required": [...]}。 - 需要严格遵循 JSON Schema 语法,手写极易出错。
- 字段多时编写和维护成本极高。
- 不推荐生产使用,仅作了解;若只需 JSON 格式输出,用 TypedDict 即可。
- 结构为
3. @dataclass 方式
- 概念说明:使用 Python 标准库
dataclasses.dataclass装饰器定义结构化类型。 - 关键细节:
- 相比 Pydantic:不需要继承 BaseModel,只需添加
@dataclass装饰器。 - 字段定义语法与 Pydantic 类似,使用
Field()提供描述信息。 - 必须从
dataclasses导入:from dataclasses import dataclass, field。 - 输出结果为 dataclass 实例,属性和 Pydantic 模型对象类似。
- 适合已有 dataclass 代码的复用场景。
- 相比 Pydantic:不需要继承 BaseModel,只需添加
4. Union 联合类型(多 Schema 模式)
- 概念说明:使用
Union包裹多个不同的 Pydantic 类型,Agent 根据用户输入自动选择最匹配的 schema。 - 关键细节:
- 不是"多个 schema 格式选一个输出",而是"根据内容匹配最合适的实体类型"。
- 例如:用户问联系方式 → 匹配
ContactInfo;问事件详情 → 匹配EventInfo。 - 通过
Union[TypeA, TypeB]声明,传入ToolStrategy(schema=...)即可。
🏗️ 架构与工作流
ToolStrategy(schema=Union[ContactInfo, EventInfo])
│
├─ 用户输入:"小明的邮箱是..." → 自动匹配 ContactInfo
└─ 用户输入:"2026年高考人数突破1200万" → 自动匹配 EventInfo
四种 schema 定义方式对比:
| 方式 | 输出格式 | 编写难度 | 推荐度 |
|---|---|---|---|
| Pydantic | 对象 | 低 | ★★★★★ |
| TypedDict | JSON字典 | 中 | ★★★★ |
| @dataclass | 对象 | 低 | ★★★ |
| JSON Schema | JSON字典 | 高 | ★★ |
💻 代码实战
from typing import TypedDict, Annotated, Literal, Union, Optional
from dataclasses import dataclass, field
from pydantic import BaseModel, Field
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy
# ========== 方式1:TypedDict ==========
class ContactInfoDict(TypedDict):
name: Annotated[str, ..., Field(description="用户姓名")]
email: Annotated[str, ..., Field(description="邮箱地址")]
phone: Annotated[str, ..., Field(description="电话号码")]
agent = create_agent(
model=model,
response_format=ToolStrategy(schema=ContactInfoDict)
)
# 输出结果为 JSON 字典:{"name": "小明", "email": "...", "phone": "..."}
# ========== 方式2:JSON Schema(不推荐) ==========
json_schema = {
"title": "ContactInfo",
"description": "用户联系方式",
"type": "object",
"properties": {
"name": {"type": "string", "description": "用户姓名"},
"email": {"type": "string", "description": "邮箱地址"},
"phone": {"type": "string", "description": "电话号码"},
},
"required": ["name", "email", "phone"]
}
agent = create_agent(
model=model,
response_format=ToolStrategy(schema=json_schema)
)
# ========== 方式3:@dataclass ==========
@dataclass
class ContactInfoDC:
name: str = field(metadata={"description": "用户姓名"})
email: str = field(metadata={"description": "邮箱地址"})
phone: str = field(metadata={"description": "电话号码"})
agent = create_agent(
model=model,
response_format=ToolStrategy(schema=ContactInfoDC)
)
# ========== 方式4:Union 联合类型 ==========
class ContactInfo(BaseModel):
name: str = Field(description="用户姓名")
email: str = Field(description="邮箱地址")
phone: str = Field(description="电话号码")
class EventInfo(BaseModel):
event_name: str = Field(description="事件名称")
date: str = Field(description="发生日期")
description: Optional[str] = Field(default=None, description="事件描述")
agent = create_agent(
model=model,
response_format=ToolStrategy(schema=Union[ContactInfo, EventInfo])
)
# 测试:问联系方式 → 匹配 ContactInfo
response = agent.invoke({
"messages": [{"role": "user", "content": "小明的邮箱是 test@163.com,电话是 13012341234"}]
})
print(response["structured_response"]) # ContactInfo 类型
# 测试:问事件 → 匹配 EventInfo
response = agent.invoke({
"messages": [{"role": "user", "content": "2026年高考报名人数突破1200万"}]
})
print(response["structured_response"]) # EventInfo 类型
⚠️ 常见问题与避坑指南
- JSON Schema 手写极易出错:字段多时强烈建议用 Pydantic 定义后用其自带的
.model_json_schema()生成,而非手写。 - TypedDict 的
Annotated导入要区分:TypedDict来自typing,Annotated来自typing(Python 3.9+),...表示必填。 - @dataclass 需额外处理
metadata:Pydantic 的Field(description=...)在 dataclass 中需改为field(metadata={"description": "..."}),两者语法不同。 - Union 类型需确保各类型的字段语义有明确区分:否则 Agent 可能无法正确匹配到期望的 schema。
- Python 版本兼容:
Annotated和TypedDict在 Python 3.9+ 原生支持,更早版本需从typing_extensions导入。
💡 个人总结与延伸
- Pydantic 仍是首选:类型安全、校验完备、生态成熟。
- TypedDict 是 Pydantic 的轻量替代:适合只关心 JSON 输出格式、不需要复杂校验的场景。
- JSON Schema 仅作了解:其主要价值在于与外部系统(如 API 文档生成、跨语言 schema 共享)对接时的标准兼容,日常开发不推荐手写。
- Union 联合类型为 Agent 增加灵活性:允许同一个 Agent 根据对话上下文输出不同类型的结果,是多功能 Agent 的常见模式。