LangChain-结构化输出:告别解析困难,让AI返回标准格式
一、问题场景
假设你需要AI返回用户信息:
fromlangchain_openaiimportChatOpenAI model=ChatOpenAI(model="gpt-4o-mini")response=model.invoke("请给我一个用户信息,包含姓名、年龄、职业")print(response.content)输出可能是:
姓名:张三 年龄:28岁 职业:软件工程师或者:
张三,28岁,是一名软件工程师或者:
{"name":"张三","age":28,"job":"软件工程师"}问题:输出格式不可控,难以程序化处理。
二、输出解析器的作用
输出解析器负责将模型返回的非结构化文本转换为结构化数据:
AI输出(文本) → 解析器 → 结构化数据(对象/JSON)LangChain提供了多种输出解析器:
| 解析器 | 用途 |
|---|---|
| StrOutputParser | 获取纯文本 |
| JsonOutputParser | 解析JSON |
| PydanticOutputParser | 解析为Pydantic对象 |
| CommaSeparatedListOutputParser | 解析列表 |
三、StrOutputParser
3.1 基本用法
最简单的解析器,从消息对象中提取文本内容:
fromlangchain_openaiimportChatOpenAIfromlangchain_core.output_parsersimportStrOutputParserfromlangchain_core.promptsimportChatPromptTemplate# 创建组件prompt=ChatPromptTemplate.from_template("讲一个关于{topic}的笑话")model=ChatOpenAI(model="gpt-4o-mini")parser=StrOutputParser()# 组装链chain=prompt|model|parser# 执行result=chain.invoke({"topic":"程序员"})print(result)# 直接是字符串print(type(result))# <class 'str'>3.2 为什么需要它?
# 不使用解析器response=model.invoke(messages)print(type(response))# <class 'AIMessage'>print(response.content)# 需要手动访问content属性# 使用解析器result=parser.invoke(response)print(type(result))# <class 'str'>,直接是字符串四、PydanticOutputParser
4.1 定义数据结构
使用Pydantic定义期望的输出结构:
frompydanticimportBaseModel,FieldfromtypingimportListclassUserInfo(BaseModel):"""用户信息模型"""name:str=Field(description="用户姓名")age:int=Field(description="用户年龄")job:str=Field(description="职业")hobbies:List[str]=Field(description="兴趣爱好")4.2 创建解析器
fromlangchain_core.output_parsersimportPydanticOutputParser parser=PydanticOutputParser(pydantic_object=UserInfo)# 获取格式说明print(parser.get_format_instructions())输出:
The output should be formatted as a JSON instance that conforms to the JSON schema below. { "name": "用户姓名", "age": "用户年龄", "job": "职业", "hobbies": ["兴趣爱好"] }4.3 完整示例
fromlangchain_openaiimportChatOpenAIfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportPydanticOutputParserfrompydanticimportBaseModel,FieldfromtypingimportList# 1. 定义数据模型classUserInfo(BaseModel):name:str=Field(description="用户姓名")age:int=Field(description="用户年龄")job:str=Field(description="职业")hobbies:List[str]=Field(description="兴趣爱好,最多3个")# 2. 创建解析器parser=PydanticOutputParser(pydantic_object=UserInfo)# 3. 创建提示模板(包含格式说明)prompt=ChatPromptTemplate.from_messages([("system","你是一个数据生成助手。\n{format_instructions}"),("human","请生成一个{age}岁左右的用户信息")])# 4. 格式化提示词messages=prompt.format_messages(format_instructions=parser.get_format_instructions(),age=30)# 5. 调用模型model=ChatOpenAI(model="gpt-4o-mini")response=model.invoke(messages)# 6. 解析输出user_info=parser.parse(response.content)print(user_info)print(type(user_info))输出:
name='张伟'age=30job='产品经理'hobbies=['读书','跑步','摄影']<class'__main__.UserInfo'>4.4 使用解析后的数据
# 访问属性print(user_info.name)# 张伟print(user_info.age)# 30print(user_info.hobbies)# ['读书', '跑步', '摄影']# 转换为字典user_dict=user_info.model_dump()print(user_dict)# {'name': '张伟', 'age': 30, 'job': '产品经理', 'hobbies': ['读书', '跑步', '摄影']}# 转换为JSONimportjson user_json=user_info.model_dump_json()print(user_json)# {"name":"张伟","age":30,"job":"产品经理","hobbies":["读书","跑步","摄影"]}五、JsonOutputParser
5.1 基本用法
更简单的JSON输出方式:
fromlangchain_core.output_parsersimportJsonOutputParserfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_openaiimportChatOpenAI# 定义JSON结构schema={"type":"object","properties":{"name":{"type":"string"},"age":{"type":"integer"},"city":{"type":"string"}}}parser=JsonOutputParser()prompt=ChatPromptTemplate.from_messages([("system","你是一个数据生成助手。\n{format_instructions}"),("human","生成一个来自{city}的用户信息")])model=ChatOpenAI(model="gpt-4o-mini")chain=prompt|model|parser result=chain.invoke({"format_instructions":parser.get_format_instructions(),"city":"北京"})print(result)print(type(result))输出:
{'name':'李明','age':28,'city':'北京'}<class'dict'>六、列表解析器
6.1 CommaSeparatedListOutputParser
fromlangchain_core.output_parsersimportCommaSeparatedListOutputParserfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_openaiimportChatOpenAI parser=CommaSeparatedListOutputParser()prompt=ChatPromptTemplate.from_template("列出5种流行的编程语言。\n{format_instructions}")model=ChatOpenAI(model="gpt-4o-mini")chain=prompt|model|parser result=chain.invoke({"format_instructions":parser.get_format_instructions()})print(result)# ['Python', 'JavaScript', 'Java', 'Go', 'Rust']6.2 自定义分隔符
fromlangchain_core.output_parsersimportListOutputParser parser=ListOutputParser()# 或者继承自定义七、处理解析错误
7.1 常见错误
fromlangchain_core.output_parsersimportPydanticOutputParserfrompydanticimportBaseModelclassUser(BaseModel):name:strage:intparser=PydanticOutputParser(pydantic_object=User)# 模型可能返回不符合格式的数据invalid_json='{"name": "张三", "age": "不是数字"}'try:user=parser.parse(invalid_json)exceptExceptionase:print(f"解析失败:{e}")7.2 重试机制
fromlangchain_core.output_parsersimportPydanticOutputParserfromlangchain.output_parsersimportRetryWithErrorOutputParserfrompydanticimportBaseModelclassUser(BaseModel):name:strage:intparser=PydanticOutputParser(pydantic_object=User)# 带重试的解析器retry_parser=RetryWithErrorOutputParser.from_llm(parser=parser,llm=ChatOpenAI(model="gpt-4o-mini"))# 解析失败时会自动让LLM修正try:result=retry_parser.parse_with_prompt(invalid_output,prompt_value)exceptExceptionase:print(f"解析失败:{e}")八、实战案例:信息提取
fromlangchain_openaiimportChatOpenAIfromlangchain_core.promptsimportChatPromptTemplatefromlangchain_core.output_parsersimportPydanticOutputParserfrompydanticimportBaseModel,FieldfromtypingimportList# 定义输出模型classPersonInfo(BaseModel):"""人物信息"""name:str=Field(description="姓名")occupation:str=Field(description="职业")company:str=Field(default="",description="公司")achievements:List[str]=Field(default_factory=list,description="成就列表")# 创建解析器和提示parser=PydanticOutputParser(pydantic_object=PersonInfo)prompt=ChatPromptTemplate.from_messages([("system","你是信息提取专家。从文本中提取人物信息。\n{format_instructions}"),("human","{text}")])model=ChatOpenAI(model="gpt-4o-mini")chain=prompt|model|parser# 执行提取text=""" 马斯克是特斯拉CEO,也是SpaceX的创始人。他成功发射了可回收火箭, 推动了电动汽车革命,还在 Neuralink 和 The Boring Company 担任领导职务。 """result=chain.invoke({"format_instructions":parser.get_format_instructions(),"text":text})print(result)输出:
name='马斯克'occupation='CEO、企业家'company='特斯拉'achievements=['成功发射可回收火箭','推动电动汽车革命','Neuralink创始人','The Boring Company创始人']九、小结
| 解析器 | 输入 | 输出 | 适用场景 |
|---|---|---|---|
| StrOutputParser | AIMessage | str | 获取纯文本 |
| PydanticOutputParser | str | Pydantic对象 | 结构化数据 |
| JsonOutputParser | str | dict | JSON数据 |
| ListOutputParser | str | list | 列表数据 |
最佳实践:
- 定义清晰的Pydantic模型
- 使用Field添加描述
- 在提示词中包含格式说明
- 处理解析异常
