第 4 课 / 共 7 课16 分钟2 个闯关

结构化输出:把文字变成数据

PydanticOutputParser、with_structured_output、解析失败怎么兜底。

模型吐出的是文字,程序要的是数据

你让模型抽取信息,它回你:

好的!根据您提供的内容,我提取到的信息如下:
{"name": "小明", "age": 18}
希望对您有帮助!

json.loads() 直接炸。你开始写正则去切那段 JSON,然后发现有时它用 ```json 包起来,有时用单引号,有时多一个尾逗号。

这是 LLM 工程里最琐碎也最高频的痛点。 输出解析器就是来解决它的。

用 Pydantic 声明你要什么

from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field

class Ticket(BaseModel):
    category: str = Field(description="分类,只能是 售后/财务/技术/其它 之一")
    urgency: int = Field(description="紧急度 1-5,5 最急")
    summary: str = Field(description="一句话摘要,不超过 20 字")

parser = PydanticOutputParser(pydantic_object=Ticket)

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是工单分析器。{format_instructions}"),
    ("user", "{text}"),
]).partial(format_instructions=parser.get_format_instructions())

chain = prompt | llm | parser

result = chain.invoke({"text": "买的东西三天了还没发货,客服也不回,我要退款!"})
print(result.category, result.urgency)    # 售后 4
print(type(result))                        # <class '__main__.Ticket'>

关键在 parser.get_format_instructions():它自动把你的类结构翻译成一段给模型看的格式说明,塞进提示词。你只维护一处定义——那个 class——提示词和解析逻辑都跟着它走。

.partial() 的意思是"先把这个变量填死",剩下的 {text} 留给 invoke。

拿到的 result 是真正的 Python 对象,有类型提示,IDE 能补全,字段名写错立刻报错。

更轻量的选择

from langchain_core.output_parsers import StrOutputParser, JsonOutputParser

StrOutputParser()      # 只要纯文本,最常用
JsonOutputParser()     # 要 dict,但不校验字段

选择准则:要结构且要可靠 → Pydantic;只要文本 → Str;随便看看 → Json。

更稳的路子:函数调用

主流模型现在都支持结构化输出 / 函数调用,由模型厂商在解码层面保证输出一定是合法 JSON。能用就用它,比让模型"努力遵守格式说明"可靠得多:

structured_llm = llm.with_structured_output(Ticket)
result = structured_llm.invoke("买的东西三天了还没发货")
print(result.category)

一行搞定,不用写 format_instructions。注意:这依赖模型厂商支持,本地小模型或老模型可能不支持,那时再退回 PydanticOutputParser。

解析失败了怎么办

再可靠的方案也有失败率,生产代码必须接住:

from langchain_core.exceptions import OutputParserException

try:
    result = chain.invoke({"text": text})
except OutputParserException as e:
    print("解析失败,原始输出:", e)
    result = Ticket(category="其它", urgency=1, summary="解析失败")

三条工程建议:

  1. temperature 设 0。 结构化任务不需要创造力。
  2. 失败重试一次,附上错误信息。 模型常常第二次就对了。
  3. 一定要有兜底值。 别让一个解析异常把整个请求打挂。

10 秒自测

为什么用 Pydantic 解析器比自己写正则切 JSON 好?

因为字段定义、给模型的格式说明、解析校验三者来自同一处定义,改字段只改一个地方;而且校验失败会明确报错,不会把脏数据悄悄放进下游。

广告位(未配置 AdSense,上线后在此展示)

动手闯关

读懂了不算会,写出来才算

0/2 已通过

闯关和判题完全免费,交错了不扣任何东西。卡住随时点「问 AI」,它能看到你正在写的代码。

关卡 01选择+10 点 · +15 XP

为什么用 Pydantic 而不是自己切 JSON

相比用正则从模型输出里抠 JSON,用 PydanticOutputParser 的核心好处是什么?

  • 可能是多选,选错不扣点数,随便试。
登录后闯关拿点数
关卡 02选择+12 点 · +15 XP

生产环境的三条保险

做结构化输出时,下面哪些是应该做的?(可多选)

  • 可能是多选,选错不扣点数,随便试。
登录后闯关拿点数

学完了?标记一下

标记完成会记进度、涨 20 XP,还会点亮连续学习天数。