LangChain 结构化输出(Structured Output)技术文档

📅 发布时间:2026/7/18 16:30:09
LangChain 结构化输出(Structured Output)技术文档 在 LangChain 的 Model I/O 架构中结构化输出是连接大语言模型LLM与下游业务系统的关键桥梁。它负责将模型生成的非结构化自然语言转化为程序可直接消费、校验和处理的强类型数据对象。本章将深入剖析结构化输出的四种 Schema 定义模式、底层校验机制以及生产环境中的最佳实践。一、 核心概念与架构定位1.1 什么是结构化输出结构化输出的核心目标是将“自然语言回答”转化为“程序可稳定消费的数据”。模型最终返回的必须是一个符合预定义结构的数据对象如 Pydantic 模型实例、TypedDict、标准字典而不再是无格式的自然语言文本。工程价值易于代码处理下游系统可直接通过属性或键名读取数据无需编写脆弱的正则表达式或字符串分割逻辑。结果高度稳定消除“模型说法变了但意思差不多”导致的解析失败。适配复杂工作流完美支持表单抽取、分类路由、工具参数生成、Agent 状态传递等场景。1.2 传统方式 vs 结构化输出结构化输出简洁且强类型定义 Pydantic/TypedDict Schemawith_structured_output 绑定模型基于 Schema 约束生成LangChain 自动解析与类型校验直接获取强类型 Python 对象传统方式繁琐且脆弱编写 Prompt 苦求返回 JSON模型生成文本手动 json.loads 解析手动 try-except 捕获异常手动校验字段类型与业务逻辑二、 底层工作流与核心机制2.1with_structured_output的核心作用在 LangChain 1.x 中模型对象通过调用with_structured_output(schema)方法绑定输出模式。该方法返回一个新的 Runnable 对象其内部封装了 Schema 转换、Prompt 注入、结果解析与校验的完整逻辑。2.2 结构化输出的底层工作流大语言模型LangChain开发者大语言模型LangChain开发者模型底层启动语法采样约束(Grammar-based sampling)1. 定义 Schema (如 Pydantic Model)2. 调用 with_structured_output(Schema)3. 协议转换将 Schema 转换为标准 JSON Schema4. 模型交互将 JSON Schema 作为 Tool/Function 注入请求5. 返回符合 JSON 规范的字符串/Tool Call6. 自动解析JSON 字符串 - Python 字典7. 自动校验字典 - Pydantic 实例 (类型/规则校验)8. 返回强类型 Python 对象核心约束机制现代大模型如 OpenAI、Anthropic普遍支持“函数调用”或“JSON Mode”。LangChain 会将 JSON Schema 作为 Tools 传入模型在解码生成 Token 时会严格按照 JSON Schema 的语法树进行选择从而在模型底层保证了输出格式绝不走样。三、 四种 Schema 定义模式深度解析LangChain 支持四种 Schema 定义方式。其中只有 Pydantic 返回的是 Schema 类实例其余三种方式返回的都是字典也只有 Pydantic 在类型不匹配时会抛出异常。3.1 模式一Pydantic生产环境首选Pydantic 提供字段校验、描述、嵌套结构功能最丰富是生产场景的绝对首选。3.1.1 基本使用要素必须继承BaseModel。使用 Python 类型提示str,int,List等。使用Field()添加字段描述Description这直接充当了 Prompt 的一部分帮助 LLM 理解字段含义。frompydanticimportBaseModel,FieldclassMovieModel(BaseModel):电影的详细信息title:strField(description电影标题)year:intField(description电影上映年份)director:strField(description导演)rating:floatField(description电影评分满分十分)# 绑定并调用structured_llmmodel.with_structured_output(MovieModel)responsestructured_llm.invoke(给出盗梦空间的信息)# response 是 MovieModel 实例支持 IDE 自动补全print(response.title)# 输出: 盗梦空间3.1.2 高级特性与平台差异避坑在实际工程中不同模型平台如 CloseAI/OpenAI 与 OpenRouter对 Pydantic 高级特性的支持存在差异需特别注意。特性 1可选字段Optional当 LLM 无法提取某字段时使用Optional可防止校验失败。fromtypingimportOptionalclassPerson(BaseModel):name:strField(description姓名)age:Optional[int]Field(description年龄)# 缺失时返回 None而非报错特性 2默认值DefaultclassConfig(BaseModel):timeout:Optional[int]Field(30,description超时时间(秒))retry:boolField(False,description是否支持重试)⚠️ 平台差异CloseAI (OpenAI) 能正确应用默认值但部分中转平台如早期的 OpenRouter 某些模型可能会忽略默认值导致字段缺失或返回 0/False。特性 3枚举类型限制Enum / Literal限制字段的可选值防止模型“幻觉”生成非法状态。fromtypingimportLiteralfromenumimportEnumclassPriority(str,Enum):LOW低MEDIUM中HIGH高classCustomerInfo(BaseModel):issue:strField(description问题描述)# 方式 A使用 Enum 类urgency_enum:PriorityField(description紧急程度)# 方式 B使用 Literal 直接限定字面量更简洁urgency_literal:Literal[低,中,高]Field(description紧急程度)特性 4限制条件Validation Rules使用 Pydantic 的约束参数如min_length,gt,ge。classProduct(BaseModel):name:strField(min_length2)price:floatField(gt0)# 必须大于 0stock:intField(ge0)# 必须大于等于 0⚠️ 核心避坑模型纠错能力差异当 Prompt 传入非法数据如price-7999时CloseAI (OpenAI)模型具备极强的自我纠错能力会忽略负号返回price7999.0。OpenRouter (部分模型)纠错能力较弱可能会返回price1.0强行满足 gt0 的最小值或直接报错。3.2 模式二TypedDict轻量级类型约束TypedDict是 Python 标准库提供的带有类型声明的字典结构。适合需要快速定义结构且无需 Pydantic 重量级校验的场景。3.2.1Annotated的使用由于TypedDict不支持Field()需使用typing.Annotated附加元数据如字段描述。fromtyping_extensionsimportTypedDict,AnnotatedclassMovieTypedDict(TypedDict):电影的详细信息title:Annotated[str,电影的正式名称]year:Annotated[int,电影的公映年份四位数字]director:Annotated[str,导演全名]rating:Annotated[float,10分制评分]structured_llmmodel.with_structured_output(MovieTypedDict)responsestructured_llm.invoke(介绍下电影《星际穿越》)# response 是标准字典 (dict)print(response[title])3.2.2 特殊占位符...(Ellipsis) 与平台差异在Annotated中使用...表示该字段为必填项不可省略。classMovieDict(TypedDict):title:Annotated[str,...,电影标题]# 必填year:Annotated[int,...,上映年份]# 必填director:Annotated[str,...,导演]# 必填rating:Annotated[float,电影评分]# 非必填无 ...⚠️ 平台差异当输入文本缺失rating信息时CloseAI (OpenAI)严格遵守...约束输出的字典中不包含rating键。OpenRouter可能无法正确识别...的必填语义强行补全一个默认值如rating: 0。3.3 模式三JSON Schema跨语言通用直接按照 JSON Schema 规范拼接 JSON 字符串。这种方式比较繁琐且缺少 Python 层面的类型校验不推荐作为首选但在需要与前后端跨语言接口对齐时非常有用。json_schema{title:Movie,type:object,properties:{title:{type:string,description:The title of the movie},year:{type:integer,description:Release year},rating:{type:number,description:Rating out of 10}},required:[title,year,rating]# 必填字段}# 必须显式指定 methodjson_schemastructured_modelmodel.with_structured_output(json_schema,methodjson_schema)responsestructured_model.invoke(给出盗梦空间的信息)# response 是标准字典 (dict)3.4 模式四dataclassPython 标准库数据类使用 Python 标准库dataclasses提供的dataclass装饰器。代码简洁但不具备运行时强校验能力。fromdataclassesimportdataclassfrompydanticimportField# 注意dataclass 中也可借用 Field 提供描述但校验由 LangChain 底层处理dataclassclassMovie:title:strField(description电影标题)year:intField(description上映年份)rating:floatField(description评分)structured_modelmodel.with_structured_output(Movie)responsestructured_model.invoke(给出盗梦空间的信息)# response 是标准字典 (dict)而非 dataclass 实例四、 运行时类型校验机制核心避坑为了验证不同 Schema 在数据格式不匹配时的处理方式我们构造一个 Fake Server强制返回字段名不匹配的 JSON如返回title1而非title。4.1 四种模式的校验行为对比Schema 模式接收到非法响应后的行为返回值类型工程影响Pydantic抛出ValidationError异常程序中断。Schema 类实例最安全。能在第一时间暴露模型输出错误或网络劫持防止脏数据流入业务逻辑。TypedDict静默失败直接返回原始字典。dict存在隐患。业务代码通过dict[title]取值时会抛出KeyError。JSON Schema静默失败直接返回原始字典。dict同上。dataclass静默失败直接返回原始字典。dict同上。结论在核心业务链路中必须使用 Pydantic以利用其运行时强校验能力。五、 获取结构化结果的两种范式5.1 范式一with_structured_output现代标准强烈推荐这是 LangChain 1.x 最简洁、最稳定的 API。进阶用法include_rawTrue当需要访问底层元数据如 Token 消耗、原始响应或处理解析错误时可传入include_rawTrue。model_with_structuremodel.with_structured_output(Movie,include_rawTrue)respmodel_with_structure.invoke(介绍下电影《星际穿越》)# resp 是一个包含三个键的字典# 1. raw: 原始的 AIMessage 对象包含 usage_metadata 等性能指标# 2. parsed: 解析后的 Pydantic 实例Movie 对象# 3. parsing_error: 解析错误信息如果模型输出不符合 Schema此处会记录异常而非直接崩溃print(resp[parsed].title)print(resp[raw].usage_metadata[total_tokens])5.2 范式二输出解析器JsonOutputParser传统方式不推荐这种方法依赖于在 Prompt 中明确指示模型输出 JSON 文本然后使用解析器进行转换。Prompt 指导要求输出 JSON模型生成纯文本 JSONJsonOutputParser正则/AST 解析Python 字典代码范式结合 LCELfromlangchain_core.output_parsersimportJsonOutputParserfromlangchain_core.promptsimportChatPromptTemplate# 1. 定义结构classMovie(BaseModel):title:strField(description电影标题)year:intField(description上映年份)# 2. 创建解析器parserJsonOutputParser(pydantic_objectMovie)# 3. 创建 Prompt必须注入格式指令prompt_templateChatPromptTemplate.from_messages([(system,回答用户问题,必须始终输出一个包含 title 和 year 的 JSON 对象。{format_instructions}),(human,问题{question})])# 注意需要将 parser 的格式指令注入 Promptpromptprompt_template.partial(format_instructionsparser.get_format_instructions())# 4. 构建 LCEL 链chainprompt|model|parser# 5. 调用返回字典responsechain.invoke({question:介绍电影《盗梦空间》})为什么不推荐极度依赖 Prompt模型可能会在 JSON 前后添加 Markdown 标记如json ...导致解析失败。缺乏底层约束模型是“尽力”生成 JSON而不是像 Function Calling 那样在底层被“强制”生成 JSON错误率较高。冗余代码需要手动处理格式指令的注入。六、 总结与工程决策清单6.1 Schema 模式选型矩阵场景需求推荐模式理由生产环境核心业务Pydantic强类型校验、丰富的约束规则、IDE 完美支持。轻量级脚本/快速原型TypedDict语法简洁无需引入 Pydantic 依赖适合简单字典结构。跨语言/前后端接口对齐JSON Schema标准协议可直接导出给前端或其他语言使用。纯 Python 数据类偏好dataclass符合 Python 原生习惯但需注意其无强校验的缺陷。6.2 生产环境最佳实践清单默认使用 Pydantic利用Field(description...)将业务逻辑说明直接转化为 Prompt减少 Prompt 编写工作量。谨慎处理缺失值对于模型可能无法提取的字段务必使用Optional[T]或设置合理的default值防止ValidationError阻断主流程。利用include_rawTrue做监控在需要统计 Token 成本或记录 LangSmith 追踪时开启此参数以获取底层AIMessage。控制嵌套层级LLM 处理深层嵌套 JSON 的能力有限建议 Pydantic 模型的嵌套层级≤ 3 层必要时拆分为多次调用。废弃JsonOutputParser除非对接极老旧的不支持 Function Calling 的模型否则全面拥抱with_structured_output。