🎯 课程主题
总结 LangChain 中获取结构化结果的两种方式:最新的 with_structured_output() API 和传统的输出解析器(Output Parser)链式调用方式,并对比两者的差异与适用场景。
📝 核心知识点
1. 方式一:with_structured_output()(推荐)
- 概念说明:LangChain v1.2 最新、最简洁的 API,直接让模型理解目标数据结构并在返回时完成解析。
- 关键细节:
- 在
invoke()之前就将结构绑定到模型,模型在生成时就按指定结构输出 - 输出精度高,不会掺杂额外字段
- 支持四种格式:Pydantic、TypedDict、JSON Schema、@dataclass
- 在
2. include_raw 参数
- 概念说明:
with_structured_output()中可传入include_raw=True,使输出包含原始响应信息。 - 关键细节:
- 默认
include_raw=False:返回纯粹的解析结果(Pydantic 实例或 dict) - 设置
include_raw=True后,返回一个包含三部分的 dict:raw:原始的 AIMessage,包含 content、token 使用情况、工具调用等信息parsed:解析后的结构化输出(与 False 时的返回值相同)parsing_error:解析错误信息(无错误时为 None)
- 注意:开启后返回类型变为 dict,不再是纯 Pydantic 实例
- 默认
3. 方式二:传统输出解析器(了解即可)
- 概念说明:LangChain 0.3 版本的主流做法,通过「提示词模板 → 模型 → 输出解析器」三段式链式调用获取结构化结果。
- 关键细节:
- 组件:
ChatPromptTemplate+ChatModel+JsonOutputParser(或其他 Parser) - 支持管道符
|连接(Chain 思想的体现) - 模型先生成文本,再由 Parser 解析 → 生成阶段没有结构约束,可能多出额外字段
- 稳定性不如
with_structured_output(),多次运行可能返回不一致的额外字段
- 组件:
4. 两种方式的核心差异
- 概念说明:差异在于"结构约束的时机"——方式一在生成前就绑定结构,方式二在生成后才解析。
- 关键细节:
- 方式一(with_structured_output):结构在前,模型按格式生成 → 精度高
- 方式二(Output Parser):结构在后,模型自由生成后再解析 → 可能掺杂多余信息
- 方式一支持
include_raw追溯原始信息,方式二没有此能力
🏗️ 架构与工作流
方式一:with_structured_output(推荐路线)
Model.with_structured_output(Schema) → StructuredModel → invoke(prompt) → 结构化结果
(提前绑定结构) (精确输出)
方式二:Output Parser(传统路线)
PromptTemplate → Model → invoke() → 原始文本 → OutputParser.parse() → 结构化结果
(模板) (生成) (自由文本) (事后解析,可能多出字段)
💻 代码实战
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel
# ========== 方式一:with_structured_output ==========
class Movie(BaseModel):
"""电影信息"""
title: str
year: int
structured_model = model.with_structured_output(Movie)
response = structured_model.invoke("介绍星际穿越")
print(type(response)) # <class 'Movie'>
print(response) # title='星际穿越' year=2014
# include_raw=True:获取原始响应
structured_model_raw = model.with_structured_output(Movie, include_raw=True)
response = structured_model_raw.invoke("介绍星际穿越")
print(type(response)) # <class 'dict'>
print(response["raw"]) # 原始 AIMessage
print(response["parsed"]) # 解析后的 Movie 实例
print(response["parsing_error"]) # None
# ========== 方式二:传统输出解析器 ==========
# 定义解析器
parser = JsonOutputParser(pydantic_object=Movie)
# 构建 Chain
prompt = ChatPromptTemplate.from_template(
"请提取以下电影的信息:{text}\n{format_instructions}"
)
chain = prompt | model | parser
# 调用方式 A:语法糖链式调用
response = chain.invoke({"text": "星际穿越,2014年上映"})
print(response) # dict 类型
# 调用方式 B:手动分步调用
prompt_value = prompt.invoke({"text": "星际穿越,2014年上映"})
model_output = model.invoke(prompt_value)
response = parser.invoke(model_output)
print(response) # dict 类型,可能包含多余字段
⚠️ 常见问题与避坑指南
include_raw=True后返回类型改变:从 Pydantic 实例变为 dict,取解析结果需用response["parsed"]。- Output Parser 方式可能输出多余字段:模型在自由生成阶段可能添加 schema 之外的字段,
with_structured_output不会有此问题。 - 新版优先用
with_structured_output:Output Parser 是 LangChain 0.3 的老用法,v1.2 中应优先使用with_structured_output()。
💡 个人总结与延伸
with_structured_output() 是 LangChain v1.2 的核心推荐 API,通过"先绑定结构再生成"的方式大幅提升了结构化输出的准确性和稳定性。include_raw 参数在需要调试或记录原始响应时非常实用。传统的 Output Parser 方式在 0.3 版本中曾是主流,但在 1.2 中仅作了解即可——它的"先自由生成再解析"模式在精确性和一致性上均不如新 API。