关注

三、LangChain基础 Model-IO(下)

文章目录

一、提示词模版(prompts)

1.1 什么是提示词模版

在大模型应用开发中,我们通常需要根据不同的用户输入来构造不同的Prompt。如果每次都手动拼接字符串,不仅代码冗余,还容易出错、无法统一管控、不支持流式/异步LCEL链路。
提示词模板(Prompt Template)核心思想:固定指令与动态变量解耦,预定义模板骨架,运行时注入变量自动生成合规PromptValue,无缝对接LLM/ChatModel。

核心价值
优势说明
复用性一次定义模板,多业务/多会话复用,无需重复写指令
参数化支持静态变量、动态函数变量、批量插值
标准化统一托管所有提示词,支持文件加载、版本管理、A/B测试
LCEL原生兼容全部模板实现Runnable协议,可直接用`
自动校验自动检测缺失变量、变量名不匹配,提前抛错

1.2 提示词模板类型完整概览

1)基础底层模板
  • PromptTemplate:纯字符串模板,适配老式文本LLM(不推荐现代对话场景)
  • ChatPromptTemplate:聊天消息模板,适配GPT/Qwen/Llama等ChatModel,企业开发首选
  • MessagesPlaceholder:消息列表插槽,专门承载对话历史List[BaseMessage]
2)进阶少样本模板
  • FewShotPromptTemplate:纯文本少样本,仅兼容老式LLM
  • FewShotChatMessagePromptTemplate聊天场景专用少样本模板,兼容ChatPromptTemplate,生产对话机器人必用

1.3 提示词模板统一工作原理

所有模板统一处理链路:
变量字典 → Template.invoke() → PromptValue → LLM/ChatModel

⚠️ 废弃旧API:.format() 仅返回字符串,不兼容LCEL;新标准统一使用.invoke(),输出PromptValue中间对象,自动兼容文本模型/聊天模型,支持流式、异步

标准工作流程
  1. 输入:传入变量字典 {key: value}partial预填充变量可省略传参
  2. 格式化:模板自动匹配占位符注入变量,自动校验变量完整性
  3. 输出:生成PromptValue(统一中间格式,分StringPromptValue/ChatPromptValue)
  4. 调用模型:PromptValue可直接丢给LLM/ChatModel,无需手动转换

核心优势:一套模板同时兼容文本生成、多轮对话,无缝接入LCEL管道。

1.4 PromptTemplate 纯字符串模板(老式LLM专用)

仅适用于非对话文本生成(摘要、翻译、简单文案),现代聊天业务优先使用ChatPromptTemplate

1. 基础两种创建方式
from langchain_core.prompts import PromptTemplate

# 方式1:from_template(自动解析占位符,无需手动写input_variables,推荐)
prompt = PromptTemplate.from_template(
    "讲一个关于{topic}的{adjective}小故事,控制在50字内"
)

# 方式2:构造函数手动声明变量(模板复杂、需要严格校验时使用)
prompt = PromptTemplate(
    template="讲一个关于{topic}的{adjective}小故事,控制在50字内",
    input_variables=["topic", "adjective"]  # 必须和模板占位符完全一致,缺省抛错
)

# LCEL标准调用 invoke(返回PromptValue)
formatted_val = prompt.invoke({"topic": "人工智能", "adjective": "治愈"})
print(formatted_val.text)
# 输出:讲一个关于人工智能的治愈小故事,控制在50字内
2. partial 预填充变量

两类预填充方案,生产场景清晰区分:

  1. partial_variables:定义模板时静态固定常量,写入模板定义,全局统一默认值
  2. .partial():运行时动态绑定,支持延迟函数(获取当前时间、请求ID、配置动态值),支持链式多次partial
from datetime import datetime
from langchain_core.prompts import PromptTemplate

# 方案1:构造函数 partial_variables 静态固定
prompt_static = PromptTemplate(
    template="请用{style}解释{concept},当前日期:{now}",
    input_variables=["concept"],
    partial_variables={
        "style": "通俗大白话",
        "now": "2026-7-13",
    }
)
print(prompt_static.invoke({"concept": "递归"}).text)

# 方案2:.partial() 动态绑定,支持字符串 + 无参函数(高阶生产用法)
prompt_base = PromptTemplate.from_template("请用{style}解释{concept},当前日期:{now}")
# 延迟函数:每次调用模板实时计算,而非固定死值
prompt_dynamic = prompt_base.partial(
    style="专业技术术语",
    now=lambda: datetime.now().strftime("%Y-%m-%d")
)
print(prompt_dynamic.invoke({"concept": "LCEL"}).text)

# 链式多次partial(分步绑定变量,接口分层场景常用)
step1 = prompt_base.partial(style="极简短句")
step2 = step1.partial(now=lambda: datetime.now().strftime("%m月%d日"))
print(step2.invoke({"concept": "RAG"}).text)
partial 与 partial_variables 完整决策表
场景选用方案优势
固定值写死、全局统一规范(如统一输出风格)partial_variables模板自描述,一眼看清默认常量,适合沉淀通用模板
运行时才确定值(接口入参、配置文件读取).partial()灵活派生多个模板实例,不污染原模板
需要动态实时值(当前时间、请求ID、随机参数).partial(变量=lambda函数)每次invoke动态刷新,静态partial_variables无法实现
分层分步绑定变量(多层业务链路逐步填充).partial()支持链式调用,拆分参数填充逻辑

1.5 ChatPromptTemplate 聊天模板(现代业务首选)

适配所有对话大模型,承载system/human/ai/assistant多角色消息列表,是对话机器人、RAG、Agent的基础。

1. 基础from_messages创建(推荐元组写法)
from langchain_core.prompts import ChatPromptTemplate

# 元组创建:(角色标识, 模板字符串),支持变量插值
chat_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是资深{role},回答简洁不超过三句话"),
    ("human", "请解答{topic}相关问题"),
    ("ai", "没问题,请说出你的具体疑问"),
    ("human", "{question}")
])

# LCEL标准invoke,返回ChatPromptValue(内置messages消息列表)
msg_val = chat_prompt.invoke({
    "role": "Python全栈讲师",
    "topic": "装饰器",
    "question": "装饰器底层原理是什么?"
})
# 打印结构化消息列表(调试必备)
print(msg_val.messages)
完整合法角色标识
角色标识对应消息类适用场景
"system"SystemMessage设定AI角色、全局规则、约束条件
"human"HumanMessage用户提问、用户输入
"ai" / "assistant"AIMessageAI历史回复,两种标识全模型兼容
2. Message对象混合写法(固定无变量消息专用)

纯静态消息用对象更直观,可和变量元组混合使用:

from langchain_core.messages import SystemMessage, HumanMessage, AIMessage

chat_prompt = ChatPromptTemplate.from_messages([
    SystemMessage(content="你是严谨的LangChain技术讲师,禁止编造知识点"),
    HumanMessage(content="什么是Runnable协议?"),
    AIMessage(content="Runnable是LangChain所有组件统一执行标准"),
    ("human", "详细解释{component}底层执行流程")
])

res = chat_prompt.invoke({"component": "MessagesPlaceholder"})
print(res.messages)

选择建议:无变量固定消息 → Message对象;含{变量}动态插值 → 元组写法。

1.6 MessagesPlaceholder 对话历史插槽(修正致命生产坑)

核心作用:预留插槽,批量注入完整历史消息列表List[BaseMessage],是带记忆对话系统核心组件。

关键强制规则(线上高频报错点)
  1. 传入variable_name对应的值必须是消息对象列表,不能传字符串;传字符串会直接打印[HumanMessage(...)]垃圾文本
  2. 搭配记忆组件时,记忆必须开启return_messages=True
  3. 等价简写("placeholder", "{history}")仅v0.1+支持,旧版本不兼容,生产优先显式MessagesPlaceholder保证兼容性
标准完整示例(对接记忆的规范写法)
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.messages import AIMessage, HumanMessage

# 显式创建占位符(生产推荐,兼容性拉满)
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是AI对话助手,结合上下文连贯回答"),
    MessagesPlaceholder(variable_name="chat_history"),  # 历史消息插槽
    ("human", "{user_input}")
])

# 注入标准消息列表
msg_result = prompt.invoke({
    "chat_history": [
        HumanMessage(content="什么是LCEL?"),
        AIMessage(content="LCEL是LangChain表达式语言,用|串联组件"),
    ],
    "user_input": "LCEL相比旧LLMChain优势是什么?"
})
print(msg_result.messages)

# 兼容简写写法(开发调试可用,大型项目不推荐)
prompt_short = ChatPromptTemplate.from_messages([
    ("system", "你是AI对话助手"),
    ("placeholder", "{chat_history}"),
    ("human", "{user_input}")
])
高频报错坑

ValueError: Unsupported chat history format: <class ‘str’>
原因:把拼接后的字符串传给占位符,解决方案:统一使用return_messages=True输出消息列表。

1.7 FewShot 少样本提示模板

1. FewShotPromptTemplate(仅老式文本LLM使用,不用于对话)
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_openai import OpenAI  # 老式文本模型,非ChatOpenAI

# 1. 定义示例数据
examples = [
    {"input": "高兴", "output": "开心"},
    {"input": "难过", "output": "悲伤"},
    {"input": "生气", "output": "愤怒"}
]
# 2. 单条示例格式化模板
example_formatter = PromptTemplate(
    template="输入: {input}\n输出: {output}",
    input_variables=["input", "output"]
)
# 3. 组装少样本模板
few_shot_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_formatter,
    prefix="以下是同义词转换示例,严格模仿格式输出,不要额外文字",
    suffix="\n输入: {input}\n输出:",
    input_variables=["input"],
    example_separator="\n\n"  # 示例之间分隔符,默认两行换行
)

# 调用模板 + 老式文本LLM
prompt_val = few_shot_prompt.invoke({"input": "兴奋"})
llm = OpenAI(model="gpt-3.5-turbo-instruct")
res = llm.invoke(prompt_val)
print(res)
2. 企业对话场景专用:FewShotChatMessagePromptTemplate

用于ChatModel,示例是多轮对话消息,可直接嵌入ChatPromptTemplate,客服、意图识别高频使用:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate

load_dotenv()

API_KEY = os.getenv('DEEPSEEK_API_KEY')
BASE_URL = os.getenv('DEEPSEEK_BASE_URL')

# 1. 对话式示例(human+ai成对)
chat_examples = [
    {"input": "怎么退款", "output": "您可以进入订单页申请售后退款,1-3个工作日原路返回"},
    {"input": "快递多久到", "output": "普通快递3-5天,顺丰次日达"},
]

# 2. 单条对话示例模板
example_prompt = ChatPromptTemplate.from_messages([
    ("human", "{input}"),
    ("ai", "{output}"),
])

# 3. 聊天少样本模板
few_shot_chat = FewShotChatMessagePromptTemplate(
    examples=chat_examples,
    example_prompt=example_prompt
)

# 4. 嵌入主对话模板(标准生产写法)
final_prompt = ChatPromptTemplate.from_messages([
    ("system", "你是电商客服,根据示例规范回答用户售后问题"),
    few_shot_chat,  # 插入批量对话示例
    ("human", "{user_question}")
])


# 创建模型实例
llm = ChatOpenAI(
    model='deepseek-v4-flash',
    api_key=API_KEY,
    base_url=BASE_URL
)

# 完整LCEL链路串联示例

# LCEL管道:模板 | 聊天模型 | 字符串输出解析器
chain = final_prompt | llm | StrOutputParser()
# 直接调用链路,一键生成结果
result = chain.invoke({"user_question": "商品破损怎么处理?"})
print(result)

1.8 提示词模板生产级最佳实践

实践规范落地说明
系统指令前置system消息放在模板最顶部,模型优先读取规则约束
示例数量控制少样本2-5个最佳,过多提升token成本、稀释核心指令
变量名统一规范历史对话统一chat_history,用户输入统一user_input,避免冲突
区分静态/动态模板全局固定规则用partial_variables,接口动态参数用.partial()
聊天业务禁用PromptTemplate全部使用ChatPromptTemplate,兼容多角色、历史消息插槽
模板外部文件托管超长提示词存入txt文件,PromptTemplate.from_file("./prompt.txt")加载,便于运营修改
链路统一LCEL模板不单独调用format,全部接入

二、输出解析器(Output Parsers)

2.1 为什么需要输出解析器

在对话场景中,大模型直接返回自然语言文本即可。但在实际生产环境,我们通常需要将大模型用于非对话场景(如数据提取、内容生成流水线等),此时需要模型以结构化的格式(如JSON、列表)输出结果,以便程序进一步处理。

LangChain 在 langchain_core.output_parsers 包中提供了一系列输出解析器,全部实现 Runnable 协议,原生支持 LCEL | 管道串联,专门解决如何将模型的自然语言输出转换为结构化数据这一问题。

要让大模型输出结构化数据,有两种基本策略:

策略方式可靠性适用场景
Prompt约束在提示词中注入自动生成的格式指令,引导模型输出指定格式文本,代码手动解析依赖模型能力,小模型极易出现格式错误通用,兼容所有开源/闭源模型,无厂商API限制
厂商原生能力调用模型API内置结构化参数,底层强制输出规范结构极高,API层校验拦截非法格式OpenAI、Gemini、通义千问等新版商用模型,企业生产首选

下面分别介绍这两种策略的具体实现。

2.2 策略一:通过Prompt约束(JsonOutputParser)

JsonOutputParser 的工作原理是:将你定义的JSON Schema转化为一段标准化格式说明文字,插入到Prompt中,引导模型输出纯净JSON字符串;解析器负责清洗文本、提取JSON、转为Python字典。

使用标准步骤
  1. 通过Pydantic BaseModel定义目标JSON数据结构
  2. 实例化 JsonOutputParser,绑定Pydantic模型
  3. 调用 get_format_instructions() 获取格式约束文本,注入System提示词
  4. 使用LCEL串联 prompt | llm | parser,一键完成调用+解析
完整可运行代码
import os
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import JsonOutputParser
from pydantic import BaseModel, Field
from dotenv import load_dotenv

load_dotenv()

API_KEY = os.getenv('DEEPSEEK_API_KEY')
BASE_URL = os.getenv('DEEPSEEK_BASE_URL')

# 创建模型实例
llm = ChatOpenAI(
    model='deepseek-v4-flash',
    api_key=API_KEY,
    base_url=BASE_URL
)

# 1. 定义目标JSON结构,优化字段描述,避免模型数据错位
class Prime(BaseModel):
    prime: list[int] = Field(description="素数数字列表,5个1000-100000之间的素数")
    count: list[int] = Field(description="对应素数以内所有素数的总个数,与prime列表一一对应")

# 2. 构造JSON解析器
json_parser = JsonOutputParser(pydantic_object=Prime)
# 获取给模型的强制格式说明
format_rules = json_parser.get_format_instructions()

# 3. 构建聊天提示词模板,注入格式约束
prompt = ChatPromptTemplate.from_messages([
    ("system", "严格遵守以下输出规范,禁止输出任何解释、换行、markdown代码块:\n{format_instr}"),
    ("human", "任意生成5个1000-100000之间的素数,并标出小于该素数的素数个数")
])

# 4. LCEL标准链式调用:模板→模型→解析器,一步完成
chain = prompt | llm | json_parser
parsed_res = chain.invoke({"format_instr": format_rules})

print(parsed_res)
print(type(parsed_res)) # <class 'dict'>,普通字典
关键参数 partial 完整释义

构造解析器支持入参 partial: bool,默认 False,专门适配流式输出场景:

  1. partial=True:流式增量解析
    • 适用场景:stream() 实时打字流式输出,模型分段吐出不完整JSON
    • 逻辑:自动用 parse_json_markdown 提取文本中可用JSON片段,能解析的字段直接返回,缺失字段填充空值,不会抛出异常,等待后续token补全
  1. partial=False(默认):完整结果强校验
    • 适用场景:invoke() 一次性完整输出
    • 逻辑:要求模型返回闭合、无缺失、语法正确的完整JSON;格式错误、字段缺失直接抛出 OutputParserException,快速定位模型输出问题

一句话区分:partial=True 适配边生成边流式解析partial=False 适配生成完成后一次性严格校验

注意事项
  1. Prompt约束依赖模型的理解能力。7B/13B参数量较小的开源模型极易输出带注释、markdown代码块、多余文字的不规范JSON,导致解析失败。对于生产环境,建议优先使用策略二厂商原生结构化方案。
  2. 捕获解析异常兜底(生产必备):
from langchain_core.exceptions import OutputParserException
try:
    parsed_res = chain.invoke({"format_instr": format_rules})
except OutputParserException as e:
    print(f"JSON格式解析失败:{e}")
    # 业务可实现自动重试、文本清洗、人工兜底逻辑

2.3 策略二:通过厂商原生能力

主流大模型厂商的API已经提供了专门的结构化输出参数,在API层面强制模型输出符合指定Schema的结构化数据,相比Prompt约束稳定性大幅提升。

OpenAI原生parse接口示例
from openai import OpenAI
from pydantic import BaseModel

client = OpenAI()

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

# 重要限制:仅 gpt-4o / gpt-4o-mini 新版接口支持,gpt-3.5-turbo不兼容该写法
response = client.chat.completions.parse(
    model="gpt-4o-mini",
    messages=[
        {"role": "user", "content": "Alice and Bob are going to a science fair on Friday."}
    ],
    response_format=CalendarEvent
)

# 直接返回校验完成的Pydantic实体对象,无需手动解析
event = response.choices[0].message.parsed
print(event)
# CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob'])

注意:response_format 不是普通输入参数,作用分为两层:

  1. 约束层:告诉API强制模型按照Pydantic结构生成纯JSON文本,禁止多余描述;
  2. 解析层:API内部自动完成JSON解析+Pydantic类型校验,结果存入 message.parsed
    输入仅由 messages 控制,response_format 只改变输出格式逻辑。
Google Gemini示例
from google import genai
from pydantic import BaseModel

class CalendarEvent(BaseModel):
    name: str
    date: str
    participants: list[str]

client = genai.Client(
    api_key="sk-xxxx",
    http_options={
        "base_url": "https://api.openai-proxy.org/google"
    },
)

response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents="Alice and Bob are going to a science fair on Friday.",
    config={
        "response_mime_type": "application/json",
        # 将Pydantic模型转为标准JSON Schema,下发给模型做输出约束
        "response_json_schema": CalendarEvent.model_json_schema(),
    },
)
# 手动将返回JSON字符串校验转为Pydantic对象
event = CalendarEvent.model_validate_json(response.text)
print(event)

说明:

  1. CalendarEvent.model_json_schema():将Pydantic类转换标准JSON Schema,传递给模型接口,约束输出字段、类型、必填项;
  2. CalendarEvent.model_validate_json(response.text):对模型返回的JSON字符串做类型、必填项校验,不符合规则抛出 ValidationError
厂商原生方案痛点

OpenAI、Gemini、文心一言各自结构化入参、返回格式完全不统一,如果业务需要切换模型,必须重写整套调用逻辑,耦合度极高;这正是LangChain封装 with_structured_output 的核心动机。

2.4 LangChain统一封装:with_structured_output

LangChain 在 ChatModel 上提供 with_structured_output() 方法,屏蔽所有厂商API差异,一套代码兼容OpenAI、Gemini、通义千问、本地Qwen等支持结构化输出的模型,调用方式完全统一,是企业生产标准方案。

补充核心参数 method

with_structured_output(schema, method="xxx") 两种底层执行模式:

  1. method="json_mode":开启模型原生JSON输出模式,强制返回纯JSON字符串,后序自动校验匹配Schema;兼容更多型号模型
  2. method="function_calling":底层调用Function Calling工具机制,约束力度最强,支持多层嵌套复杂Schema,推荐业务数据抽取场景
完整标准代码示例
import os
from langchain_openai import ChatOpenAI
from pydantic import BaseModel

# 1. 初始化LLM
llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.0,
    base_url=os.getenv("OPENAI_BASE_URL"),
    api_key=os.getenv("OPENAI_API_KEY")
)

# 2. 定义Pydantic数据结构
class CalendarEvent(BaseModel):
    name: str = Field(description="活动全称")
    date: str = Field(description="活动举办日期")
    participants: list[str] = Field(description="参与人员名单")

# 3. 封装结构化Runnable,指定底层调用方式
structured_llm = llm.with_structured_output(
    schema=CalendarEvent,
    method="function_calling"
)

# 4. 直接调用,无需额外解析器,原生返回Pydantic对象
result = structured_llm.invoke("Alice and Bob are going to a science fair on Friday.")
print(result)
# CalendarEvent(name='Science Fair', date='Friday', participants=['Alice', 'Bob'])
print(type(result)) # <class 'CalendarEvent'>
核心优势
  1. 跨模型兼容:切换OpenAI/Anthropic/Gemini仅需修改LLM初始化代码,结构化调用逻辑完全不动;
  2. 内置校验:底层API强制规范输出,极少出现解析报错,减少异常兜底代码;
  3. 原生支持LCEL、流式stream、异步ainvoke,适配高并发业务。

2.5 其他常用输出解析器

除JSON结构化解析外,LangChain内置多种适配不同业务格式的解析器,全部位于 langchain_core.output_parsers
官方文档地址:https://reference.langchain.com/python/langchain-core/output-parsers

1. StrOutputParser 基础文本解析器

最简单的解析器,唯一作用:提取 AIMessage.content 模型原始文本,去除消息包装对象,返回纯字符串;基础对话、文案生成链路必备。

from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate

parser = StrOutputParser()
# LCEL标准串联写法,日常开发高频使用
prompt = ChatPromptTemplate.from_messages([("human", "详细讲解LCEL语法")])
chain = prompt | llm | StrOutputParser()
text_result = chain.invoke({})
print(type(text_result)) # str
2. PydanticOutputParser 强校验解析器

在介绍这个解析器之前,我们需要先了解Pydantic是什么。

Pydantic简介

Pydantic 是 Python 主流数据校验库,依托类型注解自动完成数据类型校验、自动类型转换。简单来说,自定义数据模型类后,Pydantic会强制校验输入数据的结构、字段类型、自定义业务规则。

最小使用案例
from pydantic import BaseModel, Field

# 定义一个数据模型
class User(BaseModel):
    name: str               # 名字必须是字符串
    age: int                # 年龄必须是整数
    email: str = Field(description="用户的邮箱地址")

# 创建实例 - 数据会自动校验和转换
user = User(name="张三", age=25, email="[email protected]")
print(user.name) # 张三
print(user.age)  # 25

# 类型不匹配时,Pydantic会自动尝试转换
user2 = User(name="李四", age="30") # age传入字符串"30",自动转为整数30
print(user2.age) # 30(已自动转换为int)

# 数据不合法时会抛出ValidationError
# User(name="王五", age="abc") # 报错:无法将"abc"转为整数
Pydantic的核心价值
  • 类型安全:自动校验数据类型,拦截非法数据
  • 自动转换:兼容数字字符串、布尔字符串等可转换格式
  • 清晰的错误提示:校验失败输出精准字段错误信息
  • JSON双向转换:支持模型转JSON、JSON反向解析为模型实例
PydanticOutputParser 作用

它依靠Prompt引导模型输出合规JSON,最终返回完整Pydantic实体对象,区别于JsonOutputParser仅返回字典;支持字段内置约束、自定义校验器,适合对数据精度有严格要求的场景。

PydanticOutputParser完整示例
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from pydantic import BaseModel, Field, field_validator
from langchain_openai import ChatOpenAI

class MovieReview(BaseModel):
    """电影评论结构化输出模型"""
    title: str = Field(description="电影完整标题")
    rating: int = Field(description="评分,1-10分", ge=1, le=10)
    summary: str = Field(description="简短剧情简介,50字以内")
    recommended: bool = Field(description="是否推荐观看")

    # 自定义字段校验器
    @field_validator('rating')
    @classmethod
    def rating_must_be_valid(cls, v):
        if v < 1 or v > 10:
            raise ValueError('评分必须在1-10区间内')
        return v

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 实例化Pydantic解析器
parser = PydanticOutputParser(pydantic_object=MovieReview)
format_instr = parser.get_format_instructions()

# 组装提示词
prompt = ChatPromptTemplate.from_messages([
    ("system", "{format_instr}"),
    ("human", "客观评价电影《盗梦空间》")
])

# LCEL链路
chain = prompt | llm | parser
result = chain.invoke({"format_instr": format_instr})
print(f"电影: {result.title}, 评分: {result.rating}/10")
执行流程说明
  1. chain = prompt | llm | parser 管道顺序执行;
  2. 解析器接收模型文本输出,先提取JSON字符串转为Python字典;
  3. 执行 MovieReview.model_validate(字典) 做全量校验:
    • 先校验内置约束 ge=1, le=10
    • 再执行自定义 @field_validator 校验逻辑;
  1. 校验失败,LangChain包装抛出 OutputParserException,捕获后可重试。
_PYDANTIC_FORMAT_INSTRUCTIONS VS JSON_FORMAT_INSTRUCTIONS 区别

两者都是注入到System提示词、引导模型输出JSON的文本约束,核心差异:

  1. _PYDANTIC_FORMAT_INSTRUCTIONS:附带所有字段描述、数值约束、自定义校验说明;允许少量辅助文字,只要求主体为JSON;
  2. JSON_FORMAT_INSTRUCTIONS:强硬性约束,三条强制规则:
    • 只能输出JSON,禁止任何自然语言解释;
    • 禁止使用 ```json markdown代码块包裹;
    • 顶层只能是单一对象/数组,不能分段输出。

核心关键点:二者都只是提示词文本,仅作用于引导模型;最终模型输出依然是字符串,必须经过解析器转换为Python数据。

2.6 自定义输出解析器

当内置解析器无法满足业务特殊格式(逗号列表、表格、自定义标记文本)时,继承 BaseOutputParser 实现自定义解析逻辑。
父类强制要求实现两个抽象方法,缺少任意一个会直接报错:

  1. parse(text: str):核心解析逻辑,入参为模型纯文本,返回自定义数据结构;
  2. get_format_instructions():返回给模型的格式约束提示词。
完整代码
from typing import List
from langchain_core.output_parsers import BaseOutputParser

class CommaListParser(BaseOutputParser[List[str]]):
    """自定义解析器:将英文逗号分割的文本清洗为字符串列表"""
    def parse(self, text: str) -> List[str]:
        # 清洗换行、首尾空格、空元素
        clean_text = text.strip().replace("\n", "")
        item_list = [item.strip() for item in clean_text.split(",")]
        # 过滤空字符串
        return [item for item in item_list if item]

    # 必须实现:告诉模型输出格式规范
    def get_format_instructions(self) -> str:
        return "仅输出结果,多个元素使用英文逗号分隔,不要换行、注释、额外文字、代码块"

# 使用示例
parser = CommaListParser()
result = parser.parse("苹果, 香蕉, 橘子,  ")
print(result) # ['苹果', '香蕉', '橘子']

# LCEL管道串联使用
chain = prompt | llm | CommaListParser()

2.7 输出解析器选型指南

场景推荐方案核心原因
企业生产结构化数据抽取、表单生成llm.with_structured_output()厂商底层强制约束,解析失败率极低,跨模型兼容
本地开源模型、无原生结构化API,需要字段强校验PydanticOutputParser完整Pydantic类型+自定义校验,全模型通用
简单JSON字典提取,无复杂字段校验逻辑JsonOutputParser轻量简洁,仅输出普通字典
普通对话、文案生成、摘要,仅需要纯文本StrOutputParser最轻量,无额外性能开销
自定义特殊分割格式、行业专属文本规范继承 BaseOutputParser 自定义解析器解析逻辑完全自主可控,适配个性化业务

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/2301_79964758/article/details/162848433

文章来源crawl

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--