我叫老王,在一家中型电商公司做后端开发。去年双十一,我们上线了一套基于 AI 的智能客服系统,上线第一天就崩了——不是并发扛不住,而是 AI 返回的数据格式完全不可控。客服问"帮我查一下订单20231111的状态",AI 返回了一段自然语言描述,但我们的订单系统需要的是 JSON 结构:{"order_id": "20231111", "status": "shipped", "timestamp": "..."}。
这正是 LangChain 输出解析(Output Parsing)的核心价值所在。在我们的场景中,AI 不只是生成文本,而是要与下游系统对接,必须返回机器可读的、严格结构化的数据。本文将完整记录我从踩坑到解决问题的全过程,包含可复制的代码和真实价格对比。
为什么需要 Output Parsing
在传统 AI 应用中,调用大模型后我们得到的是一段字符串。如果下游是另一个 AI 系统,这没问题;但如果下游是数据库、API、前端组件,你就需要做大量字符串解析工作。更要命的是,AI 的输出不稳定——今天返回"已发货",明天返回"快递已发出",你永远不知道它会说什么。
Output Parsing 的本质是给 AI 一个"输出契约":无论输入什么,模型必须按照你定义的格式返回数据。这在以下场景尤为关键:
- 电商客服:查询订单、库存、价格,必须返回结构化数据
- RAG 系统:从知识库检索后,需要提取实体、关系、摘要
- 数据抽取:从非结构化文本中提取结构化信息
- 表单填充:根据用户描述自动填充表单字段
环境准备:连接 HolySheheep API
在开始之前,我们需要配置 LangChain 连接 HolySheep 的大模型服务。选择 HolySheep 的原因很实际:汇率 ¥1=$1,比官方节省超过 85%,而且国内直连延迟在 50ms 以内,对高并发的客服场景非常友好。
# 安装必要依赖
pip install langchain langchain-core langchain-community langchain-holysheep
配置 HolySheep API
import os
os.environ["HOLYSHEEP_API_KEY"] = "YOUR_HOLYSHEEP_API_KEY"
os.environ["HOLYSHEEP_API_BASE"] = "https://api.holysheep.ai/v1"
# 初始化 HolySheep LLM
from langchain_holysheep import ChatHolySheep
llm = ChatHolySheep(
model="gpt-4.1", # 或 claude-sonnet-4.5, deepseek-v3.2 等
temperature=0.3,
api_key="YOUR_HOLYSHEEP_API_KEY"
)
测试连接
response = llm.invoke("你好,请回复'连接成功'")
print(response.content) # 应该输出:连接成功
这里有个实战经验:HolySheep 支持 GPT-4.1($8/MTok)和 Claude Sonnet 4.5($15/MTok),对于结构化输出场景,我建议用 GPT-4.1——它对 JSON 格式的遵循度更高,而且价格便宜近一半。如果你的项目成本敏感,也可以用 DeepSeek V3.2,价格只要 $0.42/MTok,性价比极高。
实战场景:电商订单状态查询系统
我们回到最初的场景:用户问"订单20231111现在到哪了",AI 需要返回结构化的订单信息。
方案一:PydanticOutputParser(推荐)
这是最强大的方案,通过 Pydantic 模型定义输出结构,LangChain 会自动引导模型生成符合 schema 的 JSON。
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from pydantic import BaseModel, Field
from typing import Optional
from datetime import datetime
定义订单数据结构
class OrderInfo(BaseModel):
order_id: str = Field(description="订单号")
status: str = Field(description="订单状态:pending/shipped/delivered/cancelled")
current_location: Optional[str] = Field(description="当前物流位置", default=None)
estimated_delivery: Optional[str] = Field(description="预计送达时间", default=None)
carrier: Optional[str] = Field(description="快递公司", default=None)
last_update: str = Field(description="最后更新时间")
创建解析器
parser = PydanticOutputParser(pydantic_object=OrderInfo)
构建提示词模板
prompt = PromptTemplate(
template="""你是一个电商订单查询助手。用户会询问订单状态。
{format_instructions}
用户问题:{query}
请根据订单数据库返回结构化信息。如果订单不存在,返回所有字段为 null(除了 status 设为 "not_found")。""",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
创建链
chain = prompt | llm | parser
执行查询
result = chain.invoke({"query": "帮我查一下订单 20231111 的状态"})
print(f"订单号: {result.order_id}")
print(f"状态: {result.status}")
print(f"物流位置: {result.current_location}")
print(f"快递公司: {result.carrier}")
print(f"最后更新: {result.last_update}")
这段代码的输出会类似:
订单号: 20231111
状态: shipped
物流位置: 上海分拨中心
快递公司: 顺丰速运
最后更新: 2023-11-11 14:32:00
关键是 parser.get_format_instructions() 会自动生成一段提示词告诉模型如何输出。LangChain 内部会在 prompt 末尾追加类似这样的指令:
The output should be formatted as a JSON instance that conforms to the JSON schema below.
Here is the output schema:
{"properties": {"order_id": {"description": "订单号", "type": "string"}, "status": {...}}, "required": ["order_id", "status", "last_update"]}
方案二:JsonOutputParser
如果你的数据结构比较简单,不需要复杂的验证逻辑,可以用 JsonOutputParser。
from langchain.output_parsers import JsonOutputParser
定义简单的 JSON schema
json_schema = {
"type": "object",
"properties": {
"answer": {"type": "string", "description": "回复用户的自然语言答案"},
"confidence": {"type": "number", "description": "答案置信度 0-1"},
"requires_escalation": {"type": "boolean", "description": "是否需要转人工"}
},
"required": ["answer", "confidence", "requires_escalation"]
}
parser = JsonOutputParser(schema=json_schema)
prompt = PromptTemplate(
template="""作为客服助手,回答用户问题并评估是否需要转人工。
{format_instructions}
用户问题:{question}""",
input_variables=["question"],
partial_variables={"format_instructions": parser.getFormatInstructions()}
)
chain = prompt | llm | parser
result = chain.invoke({"question": "这个商品能便宜点吗?我是老客户"})
print(result)
输出: {"answer": "