模型吐出的是文字,程序要的是数据
你让模型抽取信息,它回你:
好的!根据您提供的内容,我提取到的信息如下:
{"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="解析失败")
三条工程建议:
- temperature 设 0。 结构化任务不需要创造力。
- 失败重试一次,附上错误信息。 模型常常第二次就对了。
- 一定要有兜底值。 别让一个解析异常把整个请求打挂。
10 秒自测
为什么用 Pydantic 解析器比自己写正则切 JSON 好?
因为字段定义、给模型的格式说明、解析校验三者来自同一处定义,改字段只改一个地方;而且校验失败会明确报错,不会把脏数据悄悄放进下游。