LangChain 结构化输出实战指南:从 StrOutputParser 到 with_structured_output 的完整解决方案

📅 2026/7/23 5:06:38
LangChain 结构化输出实战指南:从 StrOutputParser 到 with_structured_output 的完整解决方案
1. 本章目标理解区别明白自然语言输出和结构化输出的不同应用场景掌握基础使用StrOutputParser获取纯文本结果定义结构使用Pydantic定义模型应该返回的数据格式解析输出使用PydanticOutputParser解析模型返回的结构化文本直接获取使用with_structured_output更简洁地获取结构化对象处理异常处理结构化输出失败的情况实战应用完成简历信息抽取和商品评论分析等实际案例学习建议本章内容层层递进建议按顺序学习。先从简单的StrOutputParser开始再逐步学习更复杂的结构化输出。:::2. 为什么需要结构化输出想象一下你正在开发一个简历筛选系统需要从简历中提取以下信息姓名工作年限技能目标岗位场景对比场景一模型返回自然语言候选人姓名是张三工作 3 年熟悉 Python、FastAPI 和 MySQL希望应聘后端开发工程师。程序收到这段文字后还需要用正则表达式或NLP技术提取信息处理各种表达方式“工作3年” vs “有3年工作经验”解析技能列表逗号分隔、顿号分隔等场景二模型返回结构化数据{name:张三,years_of_experience:3,skills:[Python,FastAPI,MySQL],target_position:后端开发工程师}程序可以直接使用# 直接访问属性无需额外解析print(resume.name)# 输出张三print(resume.skills)# 输出[Python, FastAPI, MySQL]结构化输出的应用场景信息抽取从文档中提取特定信息如简历、合同、报告文本分类将文本归类到预定义的类别中情感分析分析文本的情感倾向正面/负面/中性工单分类自动将用户问题分类到相应部门内容审核识别违规内容并分类数据入库将非结构化数据转换为数据库可存储的格式接口调用将模型输出作为其他业务系统的输入关键理解结构化输出让程序能直接处理模型结果而不是让人去阅读和理解。:::3. Output Parser 是什么Output Parser输出解析器是 LangChain 中处理模型输出的组件。你可以把它想象成一个翻译官模型原始输出AIMessage → Output Parser → 程序可用的数据LangChain 中常见的输出方式方式作用适用场景StrOutputParser将模型回复转换成字符串只需要文本内容不关心结构PydanticOutputParser将模型回复解析成Pydantic 对象需要结构化数据且模型返回JSON文本with_structured_output让模型直接按照指定结构返回模型服务支持结构化输出时使用本章学习路线先学StrOutputParser最简单再学PydanticOutputParser最常用最后学with_structured_output最方便:::4. StrOutputParser获取纯文本输出4.1 基本用法当调用大模型时返回的通常是AIMessage对象# 直接调用模型responsemodel.invoke(请介绍 LangChain)print(response.content)# 输出LangChain是一个用于开发大模型应用的框架...StrOutputParser的作用就是把AIMessage对象转换成普通字符串fromlangchain_core.output_parsersimportStrOutputParser parserStrOutputParser()textparser.invoke(response)# 将 AIMessage 转换为字符串print(text)4.2 适用场景StrOutputParser适合以下场景文案生成广告文案、文章创作内容总结长文本摘要普通问答知识问答、咨询回复翻译任务文本翻译重要提示如果业务只需要文本内容不关心数据结构使用StrOutputParser就足够了不需要复杂的结构化输出。:::4.3 深入理解为什么需要 StrOutputParser你可能会有疑问直接打印response.content和使用StrOutputParser有什么区别# 方式一直接打印responsemodel.invoke(请介绍 LangChain)print(response.content)# 方式二使用解析器parserStrOutputParser()textparser.invoke(response)print(text)表面上看两种方式都打印字符串效果几乎一样。实际上StrOutputParser的核心价值在于链式Pipeline支持。4.4 链式编程的重要性LangChain 推崇使用管道|连接各个组件形成处理流水线# ❌ 错误写法.content 是属性不是 Runnable 对象chainmodel|response.content# 报错# ✅ 正确写法使用 StrOutputParserfromlangchain_core.output_parsersimportStrOutputParser parserStrOutputParser()chainmodel|parser# 正确parser 是 Runnable 对象resultchain.invoke(请介绍 LangChain)print(result)# result 直接就是字符串无需再调用 .content关键区别返回类型model.invoke()返回的是AIMessage对象解析器作用StrOutputParser是 LangChain 标准输出解析器链式支持parser是Runnable对象可以参与链式拼接总结如果只是单独调用model.invoke()然后打印两种方式效果几乎无差别一旦需要构建处理链路如模型 → 解析器 → 后续处理必须使用StrOutputParserStrOutputParser的最大价值作为Runnable参与链式拼接:::5. 案例一文本总结让我们通过一个简单的文本总结案例实践StrOutputParser的使用。5.1 创建脚本文件创建01_text_summary.pyfromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplatefromutils.model_factoryimportget_deepSeek_model# 1. 获取模型modelget_deepSeek_model()# 2. 创建提示词模板chat_promptChatPromptTemplate.from_messages([(system,你是一个内容编辑工程师擅长提炼文本重点),(human,请将下面内容总结成一句话不超过 50 字。 内容{question})])# 3. 准备输入promptchat_prompt.invoke({question:LangChain 是一个用于开发大模型应用的框架提供模型调用、Prompt 管理、文档处理、检索和工具调用等能力。})# 4. 调用模型respmodel.invoke(prompt)# 5. 方式一直接获取内容不推荐在链式中使用print( 方式一直接获取 )print(resp.content)print()# 6. 方式二使用 StrOutputParser推荐print( 方式二使用解析器 )parserStrOutputParser()textparser.invoke(resp)print(text)5.2 运行脚本python 01_text_summary.py5.3 运行结果两种方式都会输出类似的内容LangChain是一个大模型应用开发框架提供模型调用、Prompt管理、文档处理等能力。5.4 关键点这个案例最终得到的是普通字符串方式一和方式二在单独使用时效果相同方式二使用StrOutputParser更适合在链式编程中使用练习建议尝试修改提示词让模型用不同风格总结如技术文档风格、产品介绍风格、朋友圈风格。:::6. 使用 Pydantic 定义输出结构Pydantic 可以用来定义模型应该返回哪些字段。dantic 源自 pedantic /pɪˈdæntɪk/✅ pedantic 释义迂腐的、严谨教条的、拘泥规则的:::例如简历信息frompydanticimportBaseModel,FieldclassResumeInfo(BaseModel):name:strField(description候选人姓名)years_of_experience:intField(description工作年限)skills:list[str]Field(description掌握的技术技能)target_position:strField(description目标岗位)这个模型既描述了字段类型也描述了字段含义。如果模型返回的数据不符合字段类型解析时就会报错。7. PydanticOutputParser [ 帕泽 ]PydanticOutputParser可以根据 Pydantic 模型生成格式要求并解析模型返回结果。创建 Parserfromlangchain_core.output_parsersimportPydanticOutputParser parserPydanticOutputParser(pydantic_objectResumeInfo)获取格式说明instructions 指令format_instructionsparser.get_format_instructions()把格式说明传给 Promptprompt_templateChatPromptTemplate.from_messages([(system,你是一名招聘信息分析助手。\n{format_instructions},),(human,请从下面简历中提取信息\n{resume_text}),])最后解析result parser.parse(response.content)8. 完整案例简历信息抽取创建02_resume_extractor.pyfromlangchain_core.output_parsersimportPydanticOutputParserfromlangchain_core.promptsimportChatPromptTemplatefrompydanticimportBaseModel,Fieldfromutils.model_factoryimportget_deepSeek_model modelget_deepSeek_model()classResumeInfo(BaseModel):name:strField(description姓名)years_of_experience:intField(description工作年限)skills:list[str]Field(description掌握的技术技能)target_position:strField(description目标岗位)parserPydanticOutputParser(pydantic_objectResumeInfo)# 格式化指令format_instructionsparser.get_format_instructions()templateChatPromptTemplate.from_messages([(system, 你是一名招聘信息分析助手。 请严格按照指定格式返回结果。 {format_instructions} ),(human,{resume_content})])resume_content 我叫张三我干大模型开发10年了我擅长的技术是 python,langchain,fastapi等我比较喜欢养猫养狗我想找一份智能体开发的工作。 prompttemplate.invoke({format_instructions:format_instructions,resume_content:resume_content})# 调用大模型获取AIMessageresponsemodel.invoke(prompt)# 将大模型的返回值进行格式化输出resultparser.invoke(response)print(result)print(result.name)print(result.years_of_experience)print(result.skills)print(result.target_position)预期得到类似结果姓名张三 工作年限3 技能[Python, FastAPI, MySQL, Redis] 目标岗位Python 后端开发工程师9. with_structured_output较新的 LangChain 模型组件通常提供with_structured_output它可以让模型按照 Pydantic 模型返回结构化结果。基本写法structured_modelmodel.with_structured_output(ResumeInfo)resultstructured_model.invoke(从简历中提取信息)这种写法比手动获取格式说明、再调用 Parser 更简洁。但是否支持、底层使用哪种结构化方式和模型服务能力有关。使用 DeepSeek 的 OpenAI 兼容接口时可以使用 JSON 模式structured_modelmodel.with_structured_output(ResumeInfo,methodjson_mode,)Prompt 中需要明确要求模型返回 JSON。假如你使用了 deepseek 大模型但是没有使用method“json_mode”会报如下错误openai.BadRequestError:Error code:400-{error:{message:This response_format type is unavailable now,type:invalid_request_error,param:None,code:invalid_request_error}}10. 案例三商品评论分析本案例使用with_structured_output分析商品评论。创建03_review_analyzer.pyLiteral[“正面”, “中性”, “负面”]字面量类型约束表示这个字段只能取三个值中的一个“正面” / “中性” / “负面”不允许其他任何字符串有点类似于枚举:::fromtypingimportLiteralfromlangchain_core.promptsimportChatPromptTemplatefrompydanticimportBaseModel,Fieldfromutils.model_factoryimportget_deepSeek_modelclassReviewAnalysis(BaseModel):sentiment:Literal[正面,负面,中性]Field(description情感分析的值)keywords:list[str]Field(description评论中的关键词)summary:strField(description对评论的简短总结)needs_reply:boolField(description商家是否需要回复)modelget_deepSeek_model()templateChatPromptTemplate.from_messages([(system,你是专业商品评论分析助手严格遵守以下规则仅输出纯JSON无任何多余文字、解释、markdown 1. 输出JSON必须包含4个字段sentiment、keywords、summary、needs_reply缺一不可 2. sentiment 仅允许三个中文值「正面」「中性」「负面」绝对不能使用 mixed / positive / negative 等英文 3. keywords 是字符串数组提取评论核心描述词 4. summary 用一句话概括整条评论优缺点 5. needs_reply商品存在质量问题、故障、严重不满设为true单纯好评设为false。 ),(human, 请分析下面的商品评论 {review} )])prompttemplate.invoke({review:鼠标手感不错也很安静但是用了两周滚轮就有异响。})structurted_modelmodel.with_structured_output(ReviewAnalysis,methodjson_mode)responsestructurted_model.invoke(prompt)print(response)print(response.sentiment)print(response.keywords)print(response.summary)print(response.needs_reply)str Literal : 字符串字面量number Literal : 数值字面量:::预期得到类似结果情感负面 关键词[手感, 静音, 滚轮异响] 总结用户认可鼠标手感和静音效果但反馈滚轮出现质量问题。 是否需要回复True问题解析这个代码中的 method“json_mode” 我怎么没有看到任何的 json 格式呢methodjson_mode不是让你代码里手动处理 JSON 字符串。 底层流程是这样LLM 收到指令输出一段合法 JSON 文本LangChain 内部自动把 JSON 解析 → 实例化成你的ReviewAnalysisPydantic 对象你拿到手直接是模型对象看不到原始 JSON你打印result得到的是 Pydantic 实例不是原始 JSON 字符串所以直观上看不到 JSON。:::11. 两种结构化方式怎么选方式特点适合场景PydanticOutputParser通过 Prompt 要求格式再解析文本需要明确学习 Parser 工作方式with_structured_output调用更简洁模型服务支持结构化输出先掌握PydanticOutputParser实际项目优先考虑with_structured_output使用前确认模型服务是否支持对应方式PydanticOutputParser是“让模型按提示输出 JSON然后我在本地解析”with_structured_output是“直接把结构化输出能力绑定到模型调用上让模型/API 尽量按 schema 生成”。12. 案例四工单分类本案例将用户问题分类为程序可以使用的数据。智能体客服接到用户的问题反馈之后自动将用户的问题划分到不同的种类并且显示优先级。:::创建04_ticket_classifier.pyfromtypingimportLiteralfromlangchain_core.output_parsersimportPydanticOutputParserfromlangchain_core.promptsimportChatPromptTemplatefrompydanticimportBaseModel,Fieldfromutils.model_factoryimportget_deepSeek_modelclassTicketResult(BaseModel):category:Literal[订单,物流,退款,产品,其他]Field(description工单分类)priority:Literal[低,中,高]Field(description工单优先级)reason:strField(description分类原因)modelget_deepSeek_model()# 得到解析器parserPydanticOutputParser(pydantic_objectTicketResult)# 得到解析器的规则format_instructionsparser.get_format_instructions()templateChatPromptTemplate.from_messages([(system,你是一名客服工单分类助手。 请根据用户问题完成分类。 {format_instructions}),(human,用户问题{question})])prompttemplate.invoke({format_instructions:format_instructions,question:订单显示已签收但我没有收到商品请尽快处理。})responsemodel.invoke(prompt)resultparser.invoke(response)print(result.category)print(result.priority)print(result.reason)ifresult.priority高:print(转人工客服优先处理)这个结果可以继续用于程序判断if result.priority 高: print(转人工客服优先处理)这就是结构化输出的实际价值模型结果可以继续进入业务流程。13. 处理解析错误模型输出不稳定时可能出现解析失败。可以捕获OutputParserExceptionfromlangchain_core.exceptionsimportOutputParserExceptiontry:resultparser.parse(response.content)print(result)exceptOutputParserExceptionasexc:print(结构化输出解析失败)print(模型原始输出,response.content)print(错误信息,exc)处理建议保存模型原始输出记录错误日志优化 Prompt必要时重新请求模型重要业务不能完全依赖模型自行保证格式fromtypingimportLiteralfromlangchain_core.output_parsersimportPydanticOutputParserfromlangchain_core.promptsimportChatPromptTemplatefrompydanticimportBaseModel,Fieldfromutils.model_factoryimportget_deepSeek_modelclassTicketResult(BaseModel):category:Literal[订单,物流,退款,产品,其他]Field(description工单分类)priority:Literal[低,中,高]Field(description工单优先级)reason:strField(description分类原因)modelget_deepSeek_model()# 得到解析器parserPydanticOutputParser(pydantic_objectTicketResult)# 得到解析器的规则format_instructionsparser.get_format_instructions()templateChatPromptTemplate.from_messages([(system,你是一名客服工单分类助手。 请根据用户问题完成分类。 {format_instructions}),(human,用户问题{question})])prompttemplate.invoke({format_instructions:format_instructions,question:订单显示已签收但我没有收到商品请尽快处理。})responsemodel.invoke(prompt)try:resultparser.invoke(response)print(result.category)print(result.priority)print(result.reason)ifresult.priority高:print(转人工客服优先处理)exceptExceptionasexception:print(失败的原因是,exception)print(大模型响应的内容是,response.content)14. 本章重点本章最重要的是掌握普通文本适合人阅读结构化数据适合程序处理StrOutputParser用于获取字符串Pydantic模型用于定义输出结构PydanticOutputParser用于解析模型文本with_structured_output可以更直接地获取结构化对象结构化输出失败时需要异常处理