概念
Prompt template、Chat Model、Messages、Output parser、Structured output是LangChain框架的重要组件和概念。通过有效地组合这些组件,可以构建出简单的LLM应用。
- Prompt template:用于提取模型"提示"中静态部分的组件(通常是一系列消息)。它们对于序列化、版本控制和重用这些静态部分非常有用。
- Chat Model:通过Chat API暴露的大语言模型,处理一系列消息作为输入并输出一条消息。
- Messages:聊天模型中的通信单位,用于表示模型的输入和输出。
- Output parser:负责将模型的输出转换为更适合下游任务的格式。在工具调用(Tool calling)和结构化输出(Structured output)普及之前,输出解析器主要用于此目的。
- Structured output:一种使ChatModel以结构化格式(如符合给定模式的JSON)响应的技术。
可以以通过两种方式组合这些组件:
- 命令式(Imperative):直接调用组件,例如,
model.invoke(...) - 声明式(Declarative):使用LangChain表达式语言(LCEL, LangChain Expression Language)
关键组件
LLM应用程序本质上是一个链式结构,它由以下三个关键组件构成:
提示(Prompt)指令
- 作为模型的输入指令
- 引导模型产生期望的输出
- 对输出质量有决定性影响
大型语言模型(Large Language Model, LLM)
- 作为应用的核心引擎
- 负责进行预测和响应生成
- 可以是GPT、Claude等各类大语言模型
输出解析器(Output Parser)
- 可选但实用的组件
- 将模型的原始输出转换为所需格式
- 例如:JSON转换、结构化数据提取等
组件接口
在LangChain框架中,所有组件都实现了统一的接口规范,提供三个核心方法:
- invoke:用于同步调用处理
- stream:用于流式数据处理
- batch:用于批量数据处理
组件的组合方式
这些组件的组合主要有两种方式:
1. 命令式(Imperative)组合
- 通过直接调用组件方法实现功能
- 适用场景:需要编写大量自定义逻辑时
- 优势:提供更大的编程灵活性和精细控制
2. 声明式(Declarative)组合
- 使用LCEL(LangChain Expression Language)
- 适用场景:简单组装现有组件,需求相对固定
- 优势:代码简洁,易于维护,快速开发
选择哪种组合方式主要取决的具体需求:如果需要实现复杂的自定义逻辑,建议使用命令式方法;如果主要是组装现有组件完成基础功能,则声明式方法更为合适。
OutputParser和Structured output
LCEL声明式+OutputParser
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
class RestaurantReview(BaseModel):
"""Restaurant review structure"""
restaurant_name: str = Field(description="餐厅的名称")
food_rating: int = Field(description="菜品口味评分,1-5分")
service_rating: int = Field(description="服务质量评分,1-5分")
ambiance_rating: int = Field(description="餐厅环境评分,1-5分")
recommended_dishes: str = Field(description="推荐的特色菜品")
overall_comment: str = Field(description="对餐厅的总体评价和建议")
parser = PydanticOutputParser(pydantic_object=RestaurantReview)
prompt_template = ChatPromptTemplate.from_messages(
[
("system", "你是一个专业的餐厅评价分析助手,能够从用户描述中提取结构化信息。"),
(
"human",
"请根据用户的描述,生成一个结构化的餐厅评价。\n{format_instructions}\n用户描述: {query}",
),
]
)
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
chain = prompt_template | model | parser
review_result = chain.invoke(
{
"query": "我昨天去了一家叫'江南小厨'的餐厅,菜品很美味,尤其是红烧肉和清蒸鱼非常好吃。服务员态度一般,有时需要等很久才能叫到人。餐厅装修很有江南风格,环境优雅。总的来说还是值得一去的。",
"format_instructions": parser.get_format_instructions(),
}
)
assert isinstance(review_result, RestaurantReview)
print(review_result)restaurant_name='江南小厨' food_rating=5 service_rating=3 ambiance_rating=4 recommended_dishes='红烧肉, 清蒸鱼' overall_comment='总的来说还是值得一去的。'LCEL声明式+结构化输出(Structured output)
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
from langchain_core.prompts import ChatPromptTemplate
class RestaurantReview(BaseModel):
"""Restaurant review structure"""
restaurant_name: str = Field(description="餐厅的名称")
food_rating: int = Field(description="菜品口味评分,1-5分")
service_rating: int = Field(description="服务质量评分,1-5分")
ambiance_rating: int = Field(description="餐厅环境评分,1-5分")
recommended_dishes: str = Field(description="推荐的特色菜品")
overall_comment: str = Field(description="对餐厅的总体评价和建议")
prompt_template = ChatPromptTemplate.from_messages(
[
("system", "你是一个专业的餐厅评价分析助手,能够从用户描述中提取结构化信息。"),
(
"human",
"请根据用户的描述,生成一个结构化的餐厅评价。\n用户描述: {query}",
),
]
)
model = ChatOpenAI(model="gpt-4o-mini", temperature=0).with_structured_output(
RestaurantReview
)
chain = prompt_template | model
review_result = chain.invoke(
{
"query": "我昨天去了一家叫'江南小厨'的餐厅,菜品很美味,尤其是红烧肉和清蒸鱼非常好吃。服务员态度一般,有时需要等很久才能叫到人。餐厅装修很有江南风格,环境优雅。总的来说还是值得一去的。",
}
)
assert isinstance(review_result, RestaurantReview)
print(review_result)OutputParser和Structured output的区别
Structured output
Structured output是大模型API的原生功能,通过API参数直接要求模型输出符合特定结构的内容。以OpenAI为例,通过/v1/chat/completions端点的response_format参数实现,模型会在底层直接生成符合要求的结构化数据。
特点:
- 由模型API直接支持,实现更可靠
- 不需要在提示中描述输出格式
- 目前OpenAI、Google Gemini和Anthropic Claude等主流模型提供商均已支持
- 通过LangChain的
.with_structured_output()方法可以轻松使用
适用场景:使用支持结构化输出的现代模型时,这是处理结构化数据的首选方法
OutputParser
OutputParser是一种提示工程技术,通过在提示中明确指定输出格式要求,然后使用解析器将模型输出转换为所需的结构。
特点:
- 适用于所有模型,包括不支持原生结构化输出的旧模型
- 将格式要求作为提示的一部分(通过
format_instructions) - 需要额外的解析步骤,可能存在解析失败的风险
- 实际格式要求通过提示注入,在
messages参数中而非API特定参数
使用方式:通过在提示模板中加入格式说明,例如:
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一个专业的餐厅评价分析助手,能够从用户描述中提取结构化信息。"),
("human", "请根据用户的描述,生成一个结构化的餐厅评价。\n{format_instructions}\n用户描述: {query}"),
])
model = ChatOpenAI(model="gpt-4o-mini", temperature=0)
output_parser = PydanticOutputParser(pydantic_object=RestaurantReview)
chain = prompt_template | model | output_parser
review_result = chain.invoke(
{
"query": "我昨天去了一家叫'江南小厨'的餐厅,菜品很美味,尤其是红烧肉和清蒸鱼非常好吃。服务员态度一般,有时需要等很久才能叫到人。餐厅装修很有江南风格,环境优雅。总的来说还是值得一去的。",
"format_instructions": parser.get_format_instructions(),
}
)
assert isinstance(review_result, RestaurantReview)
print(review_result)选择建议
- 对于支持结构化输出的现代模型(如GPT-4o、Claude 3系列等),优先使用Structured output
- 对于需要兼容旧模型或自定义输出格式处理的场景,使用OutputParser
- 在LangChain中,尽可能使用
.with_structured_output()方法,它会根据模型能力自动选择最佳实现方式
参考链接
- https://python.langchain.com/docs/concepts/chat_models/
- https://python.langchain.com/docs/concepts/prompt_templates/
- https://python.langchain.com/docs/concepts/messages/
- https://python.langchain.com/docs/concepts/output_parsers/
- https://python.langchain.com/docs/concepts/structured_outputs/
- https://platform.openai.com/docs/api-reference/chat