🌟 LangChain 30 天保姆级教程 · Day 6|让 AI 严格输出 JSON!用 PydanticOutputParser 自动校验结构化数据

2 阅读3分钟

系列目标:30 天从 LangChain 入门到企业级部署
今日任务:理解结构化输出的重要性 → 掌握 PydanticOutputParser → 构建可靠、可解析的 AI 接口!


🎯 一、为什么需要结构化输出?

你可能遇到过这些场景:

  • 让 AI 返回用户信息,结果它回了一段话:“张三,男,28岁……”
  • 要求返回 JSON,它却多加了注释、用了单引号、字段名拼错
  • 后端程序 json.loads() 直接报错 ❌

问题根源:大模型是“语言艺术家”,不是“程序员”——它不保证格式正确!

PydanticOutputParser 就是 LangChain 提供的“格式守门员”

  1. 自动生成格式指令(告诉 AI 要返回什么)
  2. 自动解析并校验输出
  3. 若格式错误,可自动重试或抛出异常

✅ 今天,我们就让 AI 成为“靠谱的数据接口”!


🧱 二、核心工具:Pydantic + OutputParser

什么是 Pydantic?

  • Python 最流行的数据验证库
  • 用类定义数据结构,自动校验类型、必填字段等

LangChain 如何集成?

  • PydanticOutputParser 基于 Pydantic 模型,自动生成 prompt 指令 + 解析逻辑

🛠️ 三、动手实践:让 AI 返回标准用户信息

步骤 1:定义数据结构(Pydantic Model)

# day6_structured_output.py
from langchain_ollama import ChatOllama
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser

# 1. 定义期望的输出结构
class UserInfo(BaseModel):
    name: str = Field(description="用户姓名")
    age: int = Field(description="用户年龄", ge=0, le=150)
    city: str = Field(description="居住城市")

💡 Field 可添加描述(用于 prompt)和校验规则(如 ge=0 表示 ≥0)


步骤 2:创建 OutputParser

# 2. 创建解析器
parser = PydanticOutputParser(pydantic_object=UserInfo)

步骤 3:构建带格式指令的 Prompt

# 3. 构建 Prompt(关键:插入 parser.get_format_instructions())
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个用户信息提取助手。"),
    ("human", "请从以下描述中提取用户信息:\n{input}\n\n{format_instructions}")
]).partial(format_instructions=parser.get_format_instructions())

🔑 parser.get_format_instructions() 会自动生成类似这样的指令:

请按以下 JSON 格式回答,不要包含其他内容:
{
  "name": "string",
  "age": "integer (0-150)",
  "city": "string"
}

步骤 4:组装 Chain 并调用

# 4. 初始化模型
llm = ChatOllama(model="qwen:7b", temperature=0)

# 5. 构建完整 Chain:prompt → llm → parser
chain = prompt | llm | parser

# 6. 调用(输入自然语言描述)
try:
    result = chain.invoke({
        "input": "我叫李华,今年32岁,住在杭州。"
    })
    print("✅ 解析成功!")
    print(f"姓名:{result.name}")
    print(f"年龄:{result.age}")
    print(f"城市:{result.city}")
    print(f"类型:{type(result)}")  # <class 'UserInfo'>
except Exception as e:
    print("❌ 解析失败:", e)

▶️ 成功输出示例:

✅ 解析成功!
姓名:李华
年龄:32
城市:杭州
类型:<class '__main__.UserInfo'>

✅ 不仅格式正确,还自动转成了 Python 对象!可直接用于业务逻辑。


🔄 四、自动重试机制(可选增强)

如果模型偶尔输出格式错误,我们可以让 LangChain 自动重试

from langchain_core.runnables import RunnableLambda

def retry_parse(attempts=3):
    def _run(input_str):
        for _ in range(attempts):
            try:
                return parser.parse(input_str)
            except:
                continue
        raise ValueError("多次解析失败")
    return RunnableLambda(_run)

# Chain 改为:
chain = prompt | llm | retry_parse()

💡 更高级方案:使用 RetryOutputParser(LangChain 内置),后续进阶篇会讲。


🌐 五、兼容 OpenAI?依然无缝切换!

# from langchain_openai import ChatOpenAI
# llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

from langchain_ollama import ChatOllama
llm = ChatOllama(model="qwen:7b", temperature=0)

✅ 同一套结构化输出逻辑,支持所有模型!


⚠️ 六、注意事项 & 最佳实践

问题建议
中文模型忽略格式指令使用 Qwen、GLM 等中文优化模型;在 system prompt 中强调“严格按格式”
字段缺失或类型错误在 Pydantic 模型中标记 required=True(默认就是)
输出包含 Markdown 或 ```json在 prompt 中明确禁止:“不要使用代码块,只返回纯 JSON”
性能要求高设 temperature=0 减少随机性
需要嵌套结构?Pydantic 支持嵌套模型(如 Address 类),放心用!

🔒 生产建议:所有对外 AI 接口都应使用 OutputParser,避免脏数据进入系统!


📦 七、配套代码结构

langchain-30-days/
└── day6/
    └── structured_output.py  # 结构化输出 + 自动校验

📝 八、今日小结

  • ✅ 理解了结构化输出的必要性
  • ✅ 学会了用 Pydantic 定义数据模型
  • ✅ 掌握了 PydanticOutputParser 自动生成指令 + 解析
  • ✅ 实现了从自然语言到可靠 Python 对象的转换
  • ✅ 知道了如何处理解析失败(重试/异常)