Jean's Blog

一个专注软件测试开发技术的个人博客

0%

LangChain核心功能之结构化输出

介绍

核心是让 AI 输出从 “非结构化自然语言” 升级为 “可直接使用的标准化数据”

功能核心定位

结构化输出是 LangChain 1.0 的核心能力,核心目标是让 AI 模型返回特定、可预测格式的数据,彻底替代难以解析、易出错的自然语言文本,解决传统 AI 应用中 “文本解析成本高、数据格式不可控” 的痛点。

核心价值(解决的五大关键问题)

告别文本解析:无需再编写复杂的正则表达式、字符串处理逻辑来提取 AI 输出中的关键信息,大幅降低开发成本。

类型安全:返回的数据严格符合预定义的模式结构(如 JSON 规范、Pydantic 模型),避免因格式混乱导致的运行时错误。

直接可用:输出的 JSON 对象、Pydantic 模型或其他数据类,可直接集成到业务代码中(如存入数据库、调用其他接口),无需二次转换。

智能策略适配:LangChain 会自动选择最优实现方式 —— 若模型原生支持结构化输出则直接调用,若不支持则通过工具调用等方式兜底,保证功能兼容性。

实现方式

  1. 配置入口:在通过 create_agent 创建代理时,通过 response_format 参数指定结构化输出的格式要求。
  2. 数据流转与验证:模型生成数据后,LangChain 会自动对其进行格式验证,验证通过后,将结构化数据存入代理状态的 structured_response 键中,供后续业务逻辑直接调用。

四种输出格式类型

Pydantic

提供了企业级的类型安全和数据验证。

核心定位

Pydantic Models 是 LangChain 结构化输出中最强大的格式,通过 BaseModelField() 定义数据结构,实现:

  • 完整的运行时类型验证
  • IDE 智能提示
  • 企业级错误处理
  • 精确控制每个字段的验证规则和描述信息

核心优势

  • 运行时类型验证:确保 AI 返回的数据严格符合定义的类型和约束。
  • 自动数据转换:自动将 AI 输出的 JSON 转换为 Pydantic 对象,无需手动解析。
  • 详细的错误信息:当数据验证失败时,提供清晰的错误提示,便于调试。
  • IDE 智能提示支持:在代码编辑器中获得字段名和类型的自动补全。

适用场景

  • 字段验证要求严格的场景
  • 企业级应用开发
  • 需要 IDE 智能提示的复杂项目
  • 涉及复杂数据验证和嵌套结构的任务

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1. 定义数据模型
from pydantic import BaseModel, Field
from langchain.agents.structured_output import ProviderStrategy

class MeetingAction(BaseModel):
topic: str = Field(description="会议主题")
participants: list = Field(description="参会人员列表")
action_items: list = Field(description="行动项")
deadline: str = Field(description="截止时间")

# 2. 创建代理
meeting_agent = create_agent(
model=model,
response_format=ProviderStrategy(MeetingAction)
)

# 3. 调用获取结果
result = meeting_agent.invoke({
"messages": [{"role": "user", "content": "从会议纪要中提取: 项目评审会议, 张三和李四参加, 需要完成代码审查, 截止下周三"}]
})

meeting_data = result['structured_response']
print(f"会议主题: {meeting_data.topic}")
print(f"参会人员: {meeting_data.participants}")
  1. 定义模型:使用 BaseModel 定义 MeetingAction,每个字段通过 Field() 提供清晰的描述,帮助 AI 理解输出要求。
  2. 创建代理:在 create_agent 中通过 response_format=ProviderStrategy(MeetingAction) 指定结构化输出格式。
  3. 调用与使用:AI 返回的数据会自动验证并转换为 MeetingAction 对象,直接通过属性访问,无需手动解析 JSON。

Dataclasses

是 Pydantic Models 的轻量化替代方案。

核心定位

Dataclasses 是 Python 3.7+ 原生支持的标准库,在 LangChain 中作为轻量级数据容器实现结构化输出。相比 Pydantic,它舍弃了运行时验证能力,但换来更优的性能和更少的依赖,专为简单数据结构、快速原型开发设计。

核心优势

  1. Python 原生支持:无需额外安装第三方库,直接使用标准库 dataclasses 即可。
  2. 零额外依赖:摆脱对 Pydantic 的依赖,简化项目依赖结构。
  3. 轻量级性能:无复杂验证逻辑,执行效率更高,资源占用更低。
  4. 简单易用:语法简洁,学习成本低,快速上手定义数据结构。

适用场景

  • 仅需基础数据结构化,无需严格运行时验证的场景。
  • 追求轻量级、高性能的 Python 原生代码开发。
  • 项目初期的快速原型验证,快速搭建结构化输出能力。
  • 数据模型简单(无嵌套、无复杂约束)的业务场景。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 1. 定义数据模型
from dataclasses import dataclass
from langchain.agents.structured_output import ProviderStrategy

@dataclass # 原生装饰器定义数据类
class BookInfo:
title: str # 书籍标题
author: str # 作者
isbn: str # ISBN编号
year: int # 出版年份

# 2. 创建代理
book_agent = create_agent(
model=model, # 已初始化的模型实例
# 指定输出格式为自定义的 BookInfo 数据类
response_format=ProviderStrategy(BookInfo)
)

# 3. 调用并获取结果
result = book_agent.invoke({
"messages": [{
"role": "user",
"content": "提取这本书的信息:《百年孤独》加西亚·马尔克斯,ISBN 9787544291170,1967年出版"
}]
})

# 从代理状态中获取结构化结果(自动转为 BookInfo 对象)
book_data = result['structured_response']
print(f"书名:{book_data.title}")
print(f"作者:{book_data.author}")

TypedDict

是一种兼顾字典灵活性与类型安全的方案。

核心定位

TypedDict 是 Python 3.8+ 的类型系统扩展,它结合了字典的灵活性和类型检查的安全性。在 LangChain 中,它允许你为字典键提供类型注解,在保持字典访问方式的同时,获得类型检查的好处,特别适合需要字典语法但又希望有类型安全的场景。

核心优势

  • 字典语法兼容:保持了 Python 字典的直观访问方式,学习成本低。
  • 类型安全检查:在开发阶段提供静态类型检查,减少因键名或类型错误导致的问题。
  • 运行时类型验证:在 LangChain 中,可配合 ProviderStrategy 进行运行时数据验证,确保 AI 输出符合预期。
  • 灵活的数据结构:支持嵌套结构和可选字段,适配复杂数据场景。

适用场景

  • 数据以字典结构存储和传递的场景。
  • 需要类型安全检查,避免键名或类型错误的项目。
  • 需要运行时验证 AI 输出结构的应用。
  • 希望保持字典语法,又想获得类型提示的开发者。

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 1. 定义数据模型
from typing_extensions import TypedDict
from langchain.agents.structured_output import ProviderStrategy

class MovieInfo(TypedDict):
title: str
director: str
year: int
genre: str

# 2. 创建代理
movie_agent = create_agent(
model=model,
response_format=ProviderStrategy(MovieInfo)
)

# 3. 调用获取结果
result = movie_agent.invoke({
"messages": [{
"role": "user",
"content": "提取这部电影的信息:肖申克的救赎,导演弗兰克·德拉邦特,1994年上映,剧情片"
}]
})

movie_data = result['structured_response']
print(f"电影:{movie_data['title']}")
print(f"导演:{movie_data['director']}")
  1. 定义模型:继承 TypedDict 定义 MovieInfo,为每个键指定类型。
  2. 创建代理:在 create_agent 中通过 response_format=ProviderStrategy(MovieInfo) 指定结构化输出格式。
  3. 调用与使用:AI 返回的数据会自动验证并转换为符合 MovieInfo 结构的字典,通过键名直接访问。

JSON Schema

以纯 JSON 数据结构定义输出规范,支持运行时动态构建,是跨语言、无代码场景的优选方案。

核心定位

JSON Schema 是 LangChain 四种结构化输出格式里灵活性最高的一种,核心特点是无需编写 Python 类,直接用标准 JSON 字典定义数据结构与验证规则。它完全遵循 JSON 规范,支持运行时动态调整结构,特别适配需要根据不同条件动态变更输出格式的业务场景。

核心优势

  1. 纯 JSON 定义:基于标准 JSON Schema 规范,与编程语言解耦,具备天然的跨语言兼容性。
  2. 运行时动态构建:可在代码运行过程中根据业务逻辑(如用户需求、场景变化)实时修改 JSON Schema 结构,无需提前定义静态模型。
  3. 跨语言兼容:输出的结构化数据可直接被其他语言(如 JavaScript、Java)解析使用,适合多技术栈协作的项目。
  4. 最灵活配置:支持复杂的嵌套结构、可选字段、枚举值、数据格式校验(如邮箱、手机号)等,适配各类动态数据需求。

适用场景

  • 动态模式定义:需要根据运行时条件(如不同用户角色、业务类型)调整输出字段的场景。
  • 无代码结构:不想编写 Python 类(如 Dataclasses、Pydantic),希望用纯数据结构定义输出的场景。
  • 运行时模式构建:需通过配置文件、数据库等外部来源动态加载输出规范的场景。
  • 灵活配置:跨语言协作、第三方系统集成等对数据格式兼容性要求高的场景。

代码示例

提取个人联系信息为例,分三步实现基于 JSON Schema 的结构化输出,核心是用 JSON 字典定义规范并接入 LangChain 代理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 1. 定义 JSON Schema
from langchain.agents.structured_output import ProviderStrategy

# 纯 JSON 字典定义:指定类型、描述、字段及必选约束
contact_info_schema = {
"type": "object", # 根节点为对象类型
"description": "Contact information for a person.", # 整体描述(辅助AI理解)
"properties": { # 定义具体字段及规则
"name": {"type": "string", "description": "用户姓名"},
"email": {"type": "string", "description": "用户的邮箱地址"},
"phone": {"type": "string", "description": "用户的手机号码"}
},
"required": ["name", "email", "phone"] # 必选字段,缺失则验证失败
}

# 2. 创建代理
contact_agent = create_agent(
model=model, # 已初始化的模型实例
# 将 JSON Schema 传入 ProviderStrategy,指定输出格式
response_format=ProviderStrategy(contact_info_schema)
)

# 3. 调用并获取结果
result = contact_agent.invoke({
"messages": [{
"role": "user",
"content": "请提取以下文本中的联系信息:John Doe, john@example.com, (555) 123-4567"
}]
})

# 从代理状态中获取结构化结果(已验证的 JSON 字典)
contact_data = result['structured_response']
print(f"姓名:{contact_data['name']}")
print(f"邮箱:{contact_data['email']}")

高级特性

智能错误处理

核心是通过灵活配置 ToolStrategy,解决结构化输出验证失败的问题,提供多维度的错误处理策略。

核心功能定位

智能错误处理是 LangChain 结构化输出的配套能力,专门用于灵活处理数据验证失败的场景。当 AI 生成的输出不符合预设的结构化规范(如 ContactInfo 模型)时,可通过配置实现自动重试、自定义提示或精准异常捕获,避免因一次验证失败导致流程中断。

核心配置载体:ToolStrategy

在创建代理时,通过 response_format=ToolStrategy(...) 替代之前的 ProviderStrategy,并传入错误处理相关参数,实现高级错误管控。

三种核心错误处理策略(代码配置详解)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from langchain.agents import create_agent
from langchain.agents.structured_output import ToolStrategy

# 定义结构化输出模型
class ContactInfo(BaseModel):
name: str
email: str
phone: str

# 创建代理
agent = create_agent(
model=model,
response_format=ToolStrategy(
schema=ContactInfo,
handle_errors=True # 自动重试
# handle_errors="请提供有效的联系信息"
# handle_errors=ValueError
)
)

代码以 ContactInfo 为结构化规范,展示了 handle_errors 参数的三种核心用法,覆盖从全量重试到精准管控的不同需求:

  1. 全局自动重试(布尔值模式)

    1
    handle_errors=True  # 捕获所有错误并自动让模型重试
    • 作用:捕获结构化输出验证过程中的所有类型异常,无需人工干预,直接让模型重新生成符合规范的结果。
    • 适用:基础场景,希望快速容错、降低开发成本。
  2. 自定义错误提示(字符串模式)

    1
    handle_errors="请确保提供有效的联系信息"  # 自定义错误消息反馈给模型
    • 作用:验证失败时,将自定义的自然语言提示传递给模型,明确告知错误原因和修正要求,让模型更精准地重试。
    • 适用:需要引导模型修正特定问题的场景,提升重试成功率。
  3. 精准异常捕获(异常类型模式)

    1
    handle_errors=ValueError  # 仅捕获指定类型的异常(如值类型错误)
    • 作用:只对特定类型的异常进行处理(如值类型不匹配、必选字段缺失),其他异常则按原生逻辑抛出,便于精准排查问题。
    • 适用:复杂业务场景,需要区分异常类型、针对性处理的情况。

核心价值

  1. 提升鲁棒性:避免因 AI 输出格式不规范导致的应用崩溃,保障结构化输出流程的稳定性。
  2. 降低开发成本:内置重试与提示机制,无需手动编写复杂的错误捕获和重试逻辑。
  3. 灵活适配场景:三种策略可按需选择,兼顾快速开发的便捷性和复杂场景的精准性。

联合类型支持

核心是通过 Pydantic 的数据模型来约束输出结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from typing import Union, Literal
from pydantic import BaseModel

# 定义邮箱联系模型
class EmailContact(BaseModel):
email: str
type: Literal["email"] = "email"

# 定义电话联系模型
class PhoneContact(BaseModel):
phone: str
type: Literal["phone"] = "phone"

# 定义联合类型
ContactUnion = Union[EmailContact, PhoneContact]

# 创建 AI Agent
agent = create_agent(
model=model,
response_format=ContactUnion
)
  • Union 是 Python 类型提示(typing)模块中的一个工具,用于表示一个变量可以是多种类型中的一种。
  • EmailContact:表示一个邮箱联系方式,必须包含 email 字段(字符串)和 type 字段,且 type 只能是 "email"
  • PhoneContact:表示一个电话联系方式,必须包含 phone 字段(字符串)和 type 字段,且 type 只能是 "phone"
  • Literal 用于限定字段的取值范围,确保 type 字段的值是固定的,这有助于模型区分不同的输出类型。
  • 定义了一个联合类型 ContactUnion,它表示一个值可以是 EmailContact 类型,也可以是 PhoneContact 类型。
  • 在创建 AI Agent 时,将 response_format 设置为刚才定义的联合类型 ContactUnion

  • 这样,当模型生成输出时,它会根据输入的上下文,自动判断应该返回 EmailContact 还是 PhoneContact格式的数据,并严格按照对应的结构输出。

核心作用

这种方式的核心价值在于:

  • 结构化输出:确保 AI 返回的数据是可预测、可解析的,而不是自由文本。
  • 智能适配:模型可以根据问题的上下文,自动选择最合适的输出格式,例如当用户问 “请提供你的邮箱” 时,返回 EmailContact;当问 “请提供你的电话” 时,返回 PhoneContact
  • 类型安全:在代码层面利用类型提示,提前发现和避免数据结构不匹配的问题。

复杂嵌套结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from typing import List
from pydantic import BaseModel

# 复杂嵌套结构示例
class Address(BaseModel):
street: str
city: str
country: str

class Company(BaseModel):
name: str
address: Address # 嵌套结构
employees: List[str] # 列表字段
founded_year: int

agent = create_agent(
model=model,
response_format=Company # 处理复杂嵌套
)
  1. 定义基础数据模型 Address,这是一个表示地址的数据模型,包含街道、城市和国家三个字符串字段。
  2. 定义嵌套数据模型 Company
    • name:公司名称(字符串)。
    • address:嵌套的 Address 类型,将地址信息作为子结构嵌入到公司信息中。
    • employees:员工姓名列表(字符串列表)。
    • founded_year:公司成立年份(整数)。
  3. 创建 AI Agent 并指定响应格式,将 response_format 设置为 Company 模型。

这会让 AI 模型输出严格符合 Company 结构的数据,包括嵌套的 Address 对象和列表字段,确保输出是结构化、可解析的。

核心作用

  • 深度嵌套支持:通过 Pydantic 模型的嵌套定义,AI 可以生成包含多层子结构的复杂数据,满足企业级业务需求。
  • 结构化输出:强制 AI 输出符合预定义的数据结构,避免自由文本,便于后续代码处理和数据存储。
  • 类型安全:利用类型提示和 Pydantic 的验证功能,提前发现数据格式错误,提升系统稳定性。

实际运用场景

智能数据提取

1
2
3
4
5
6
7
8
9
10
11
12
from typing import Literal
from pydantic import BaseModel, Field

# 会议记录提取
class MeetingAction(BaseModel):
"""从会议记录中提取的行动项"""
task: str = Field(description="需要完成的具体任务")
assignee: str = Field(description="任务负责人")
deadline: str = Field(description="完成截止日期")
priority: Literal["low", "medium", "high"] = Field(description="优先级")

agent = create_agent(model=model, response_format=MeetingAction)

这会让 AI 模型在处理输入的会议记录文本时,自动识别并提取出符合 MeetingAction 结构的行动项,输出结构化、可解析的数据,而不是自由文本。

核心作用

  • 智能数据提取:将非结构化的会议记录等文本,转化为结构化的行动项数据,便于后续自动化处理和任务管理。
  • 结构化输出:强制 AI 输出符合预定义格式的数据,避免信息丢失或格式混乱。
  • 语义引导:通过 Fielddescription 参数,清晰地告诉 AI 每个字段的含义,从而提高提取的准确性。

内容智能分类

1
2
3
4
5
6
7
8
9
10
11
12
from typing import Literal, List
from pydantic import BaseModel, Field

# 客户反馈分类
class FeedbackAnalysis(BaseModel):
"""客户反馈分析结果"""
sentiment: Literal["positive", "negative", "neutral"] = Field(description="情感倾向")
category: str = Field(description="问题类别")
urgency: Literal["low", "medium", "high"] = Field(description="紧急程度")
key_issues: List[str] = Field(description="关键问题点")

agent = create_agent(model=model, response_format=FeedbackAnalysis)

这会让 AI 模型在处理输入的客户反馈文本时,自动进行情感分析、主题识别和紧急度判断,并输出符合 FeedbackAnalysis 结构的结构化数据。

核心作用

  • 内容智能分类:自动对客户反馈等文本进行情感分析、主题识别和优先级标记,将非结构化的反馈转化为结构化的分析结果。
  • 结构化输出:强制 AI 输出符合预定义格式的数据,便于后续的数据分析、工单流转和业务决策。
  • 语义引导:通过 Fielddescription 参数,清晰地告诉 AI 每个字段的含义,从而提高分类和标记的准确性。

结构化代码生成

1
2
3
4
5
6
7
8
9
10
11
12
from typing import Literal, Dict, Any
from pydantic import BaseModel, Field

# API端点生成
class APIEndpoint(BaseModel):
"""API端点定义"""
path: str = Field(description="API路径")
method: Literal["GET", "POST", "PUT", "DELETE"] = Field(description="HTTP方法")
parameters: Dict[str, str] = Field(description="参数定义")
response_schema: Dict[str, Any] = Field(description="响应结构")

agent = create_agent(model=model, response_format=APIEndpoint)

这会让 AI 模型在生成 API 端点定义时,严格按照 APIEndpoint 的结构输出,确保生成的代码或配置文件是结构化、可解析的,便于后续的开发和集成。

核心作用

  • 结构化代码生成:让 AI 生成符合特定格式要求的 API 定义、配置文件或数据结构,避免自由文本输出,提升代码质量和可维护性。
  • 类型安全:通过 Pydantic 模型和类型提示,确保生成的 API 定义在类型上是安全的,减少运行时错误。
  • 语义引导:通过 Fielddescription 参数,清晰地告诉 AI 每个字段的含义,从而提高生成代码的准确性和相关性。

输出策略选择指南

方式 代码示例 适用场景与特点
自动选择 response_format=ContactInfo 简单场景,让系统自动选择策略;使用方便,可控性较低,适合快速开发。
工具策略 response_format=ToolStrategy(ContactInfo) 需要工具调用,或当模型不支持原生结构化输出时使用;兼容性强,通过工具调用包装实现结构化输出。
提供者策略 response_format=ProviderStrategy(ContactInfo, strict=True) 使用模型提供商原生结构化输出能力,需要精确控制输出行为;性能和准确性更好,strict=True 可强制严格格式。
  • 对于需要精确控制输出行为的生产环境,建议显式使用 ProviderStrategyToolStrategy,而不是依赖自动选择。
  • 优先选择 ProviderStrategy,当模型不支持原生结构化输出时,再降级使用 ToolStrategy

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 从LangChain导入结构化输出核心类
from langchain.agents.structured_output import ProviderStrategy, ToolStrategy
from pydantic import BaseModel
# 补充示例中隐含的依赖函数(实际环境需根据LangChain版本适配)
from langchain.chat_models import ChatOpenAI # 示例化聊天模型用
from langchain.agents import create_agent # 创建Agent核心函数

# 初始化聊天模型的工具函数(示例封装)
def init_chat_model(model_name):
"""根据模型名称初始化聊天模型,返回带profile属性的模型实例"""
model = ChatOpenAI(model_name=model_name)
# 为模型添加profile属性,模拟"是否支持原生结构化输出"的标识
# 实际场景中该属性可能由LangChain内置模型配置提供
if model_name in ["gpt-4o", "gpt-4-turbo", "claude-3-opus"]:
model.profile = {"structured_output": True}
else:
model.profile = {"structured_output": False}
return model

# 使用strict参数启用严格模式
def create_strict_agent(model_name, schema):
"""确保strict的代理工厂"""
from langchain.agents.structured_output import ProviderStrategy

model = init_chat_model(model_name)

if model.profile.get("structured_output"):
# 新模型:明确启用strict严格模式
return create_agent(model, response_format=ProviderStrategy(schema, strict=True))
else:
# 老模型:降级使用工具策略
return create_agent(model, response_format=ToolStrategy(schema))

# 示例:定义一个简单的ContactInfo结构(承接前文示例)
class ContactInfo(BaseModel):
name: str
phone: str

# 代码使用
agent = create_strict_agent("gpt-4o", ContactInfo) # 对gpt-4o自动启用strict=True

这是代码的核心逻辑,实现了动态策略适配

分支条件 选用策略 核心参数 适用场景
模型支持原生结构化输出 ProviderStrategy strict=True 新模型(如 GPT-4o),强制模型严格遵守 Pydantic 结构,禁止输出格式外的内容
模型不支持原生结构化输出 ToolStrategy 无 strict 参数 老模型,通过「工具调用」的方式模拟结构化输出,兼容低版本模型
  • strict=True 关键作用:仅对支持该功能的提供商(OpenAI、xAI)生效,强制模型严格遵循 schema 定义的结构,若输出不符合则直接报错,避免格式混乱。
  • 重复导入说明:函数内部再次导入 ProviderStrategy 是为了代码独立性,实际项目中可移至顶部统一导入。

总结

  • ProviderStrategystrict 参数:可选布尔值,默认 None(禁用),仅支持原生结构化输出的模型可用,开启后强制严格格式遵循,是生产环境保证输出准确性的关键。
  • 策略自动降级:通过检测模型能力,实现「新模型用原生严格模式,老模型用工具兼容模式」,提升代码的跨模型兼容性。
  • 适用建议:生产环境需精确控制输出格式时,显式使用 ProviderStrategy + strict=True,优先利用模型原生能力,性能和准确性远高于工具模拟。