LangChain中的结构化输出
模型默认返回的是⾃由⽂本但程序需要结构化数据。以下是支持结构化输出的五种方式前三种需要搭配with_structured_output使用能让模型按你定义的Schema输出1.Pydantic# 定义你期望的输出结构Pydantic 模型 from pydantic import BaseModel, Field from langchain_openai import ChatOpenAI class MovieInfo(BaseModel): 电影信息 title: str Field(description电影名称) year: int Field(description上映年份) director: str Field(description导演) rating: float Field(description评分10分制) structured_llm llm.with_structured_output(MovieInfo)#structured_llm经过 .with_structured_output() 包装后的模型实例包装后的 ChatOpenAI 对象 result structured_llm.invoke(介绍一下电影《星际穿越》) # 返回的是 MovieInfo 对象可以直接用属性访问 print(f片名{result.title}) print(f年份{result.year}) print(f导演{result.director}) print(f评分{result.rating})拆解llm.with_structured_output(MovieInfo)原始对象llmRunnable输入消息 → 返回 AIMessage.with_structured_output()内部逻辑对 ChatModel 做一层包装Runnable 封装创建一个全新的包装类Runnable即structured_llm然后用包装后的对象执行invoke()包装后的对象structured_llm.invoke()内部流程调用原来的llm.invoke()→ AIMessage提取内容 / 读取 tool_calls解析、实例化成 Pydantic 对象返回结构化实例因而我们说invoke返回的是 MovieInfo 对象可以直接用属性访问,无需.content2.TypedDictfrom typing_extensions import TypedDict # 方式二TypedDict更轻量无运行时校验 class MovieTypedDict(TypedDict): title: str year: int director: str rating: float structured_llm llm.with_structured_output(MovieTypedDict) result structured_llm.invoke(介绍一下电影《流浪地球》) # 返回的是普通 dict print(result[title])3.进阶版本的原⽣JSON Schemajson_schema { title: MovieInfo, description: 电影信息对象, type: object, properties: { title: {type: string, description: 电影名称}, year: {type: integer, description: 上映年份}, director: {type: string, description: 导演}, rating: {type: number, description: 评分10分制} }, required: [title, year, director, rating] } structured_llm llm.with_structured_output(json_schema) result structured_llm.invoke(介绍一下电影《哪吒之魔童降世》) print(result) # 返回的是 dict不止强制输出 JSON还可以直接定义 JSON 必须包含什么字段、什么类型。 相当于接口层面直接带上数据规范不用 Pydantic 二次校验。4.原生response_format接口层面强制定位最原始的不加任何 Prompt 哄骗、不用 LangChain 解析器直接命令大模型你输出必须是标准 JSON不准输出别的闲聊文字。response_format是模型底层原生支持的参数OpenAI、通义千问、文心、千问都兼容这个规范。参数放在client.chat.completions.create()顶层参数replay client.chat.completions.create( modelqwen-plus, messages[...], # 原生强制JSON response_format{type: json_object} )优势模型底层约束稳定性远高于单纯 prompt 局限只能保证是合法 JSON不能约束 JSON 里面有哪些字段模型有可能返回{name:xxx}但你想要{title:,year:}字段缺失管控不了。5.PydanticOutputParser纯上层代码方案只靠 Prompt 告诉模型输出格式没有启用接口原生强制稳定性最差。from pydantic import BaseModel, Field from langchain_openai import ChatOpenAI # 1.定义Pydantic类 class MovieInfo(BaseModel):... # 2.创建 PydanticOutputParser 实例 parser #入参 pydantic_objectMovieInfo告诉解析器将来解析出来的数据要遵守 MovieInfo 的格式规范 parser PydanticOutputParser(pydantic_objectMovieInfo) #3.读取Pydantic 结构自动生成一段告诉大模型「该输出什么样 JSON」的提示文字 format_prompt parser.get_format_instructions() # 手动组合提示词利用prompt诱导模型输出JSON prompt f介绍一下电影《星际穿越》 {format_prompt} #4.调用解析器方法 .parse() #原始llm没有任何包装 # res_msg 类型AIMessage里面只有字符串content必须手动提取文本、交给parser解析 res_msg llm.invoke(prompt) #功能接收 JSON 字符串校验格式、字段类型生成 MovieInfo 的实例对象赋值给 movie movie parser.parse(res_msg.content) #5.用 .属性名 读取内容 print(movie.title) print(movie.year) print(movie.director) print(movie.rating)拓展什么时候依然会用到 PydanticOutputParser极少数场景你拿到外部已经获取好的 JSON 字符串只是单纯做校验转换老旧项目历史代码维护补充对比with_structured_output模型包装方案和PydanticOutputParser传统解析器方案特性PydanticOutputParserwith_structured_outputllm.invoke () 原生返回AIMessage必须手动拿.content封装后 invoke 直接返回 Pydantic 实例实现原理独立解析组件解析纯文本字符串包装 ChatModel内置完整调用 解析流水线模型能力仅依靠提示词要求输出 JSON优先调用模型原生结构化 / 工具调用 API容错性低模型多输出一句话、json 就容易炸更高原生结构化规避文本解析问题适用场景兼容老旧代码、自定义解析逻辑新项目、RAG、信息抽取官方推荐为什么两者原生返回一个必须手动.content一个直接返回返回的就是实例呢两者设计层级、工作链路完全不一样1.PydanticOutputParser 独立解析组件接收json字符串返回实例对象需要手动调用parser.get_format_instructions()读取Pydantic 结构自动生成一段告诉大模型「该输出什么样 JSON」的提示文字,然后手动将其拼接到提示词上——也是因为这一步所以我们说PydanticOutputParser纯依赖提示词诱导输出 JSON大模型给出的回答还是可能包含多余文字或者格式不一致从而报错稳定性一般调用LLM 返回 AIMessage.content拿到字符串手动执行parser.parse(AIMessage.content)返回实例对象用 .属性名 读取内容风险模型输出多余文字 → 直接解析报错底层单纯依靠提示词约束输出 JSON2..with_structured_output() 对 ChatModel 做一层包装Runnable 封装封装后的模型框架structured_llm有更多功能封装层自动判断模型若支持原生 json 模式就优先自动调用模型原生response_formatJSON 强制能力response_format强制JSON 就是在调用大模型的接口里加入response_format{type:json_object}这行命令顶层命令而非只靠提示词诱导输出的稳定性更高#接口层response_format强制JSON代码示例 resp client.chat.completions.create( modeldeepseek-v4-flash, messages[{role: user, content: prompt}], response_format{type: json_object} )自动填充格式提示词内部自动完成调用模型 → 提取 content → JSON 解析 → 实例化 Pydantic 对象最终直接返回模型实例对外只需要.invoke()直接返回实体极简代码6.日常开发总结response_formatjson_object强制返回 JSON 字符串杜绝多余文字但不控制内部字段可能你要的是title,但返回的是actor如果想要【规定必须有 title、year、rating 这些固定字段】 单纯 response_format 不够需要搭配 Pydantic也就是with_structured_output方案对比记忆只想要纯净 JSON 文本 → 使用原生 response_format想要纯净 JSON 自动校验字段、自动转为对象 → LangChain structured_output