LangChain 知识总结详解(一)--- 基础组件
Models · Messages · Prompts · Output Parsers
📂 LangChain 知识总结详解系列
目录
- [一、LangChain 概述](#一、LangChain 概述)
- [二、Models 模型组件](#二、Models 模型组件)
- [三、Messages 消息类型](#三、Messages 消息类型)
- [四、Prompts 提示模板组件](#四、Prompts 提示模板组件)
- [五、Output Parsers 输出解析组件](#五、Output Parsers 输出解析组件)
一、LangChain 概述
1.1 什么是 LangChain
LangChain 是面向大语言模型(LLM)的生产级 AI 应用编排框架,帮助开发者将 LLM 与外部数据源、工具和业务逻辑组合成完整的应用程序。
LLM 虽然强大,但存在三个关键短板,LangChain 正是为了解决这些问题而生:
| LLM 的短板 | 问题描述 | LangChain 的解决方案 |
|---|---|---|
| 无记忆 | 每次调用都是全新的,不知道上一轮说了什么 | Memory 组件 --- 管理对话上下文,实现多轮对话 |
| 无外部知识 | 只懂训练数据,无法获取最新信息或私有数据 | RAG 检索 --- 先查资料再回答,减少幻觉 |
| 无法执行任务 | 只能"说"不能"做",无法与外部系统交互 | Agent + Tools --- 让 AI 能调用工具完成实际任务 |
1.2 核心组件架构图
┌──────────────────────────────────────────────────────────────────┐
│ LangChain 应用架构 │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │
│ │ Models │ │ Prompts │ │ Output Parsers │ │
│ │ (模型调用) │ │ (提示模板) │ │ (输出解析) │ │
│ └──────┬───────┘ └──────┬───────┘ └────────┬─────────┘ │
│ │ │ │ │
│ └─────────────────┼────────────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Chain (LCEL) │ ← 管道串联 (| 操作符) │
│ └───────┬───────┘ │
│ │ │
│ ┌─────────────────┼─────────────────┐ │
│ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ Memory │ │ Retrieval │ │ Agent │ │
│ │ (记忆) │ │ (RAG检索) │ │ (智能体) │ │
│ └─────────────┘ └─────────────┘ └──────┬──────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Middleware │ │
│ │ (中间件拦截) │ │
│ └─────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Messages (消息类型) --- 贯穿所有组件的通信协议 │ │
│ │ SystemMessage / HumanMessage / AIMessage / ToolMessage │ │
│ └──────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
1.3 LangChain vs LangGraph vs Deep Agents
| 框架 | 定位 | 核心能力 | 适用场景 |
|---|---|---|---|
LangChain (create_agent) |
高度可配置的 Agent 编排 | 灵活配置模型、工具、中间件 | 定制化需求、简单到中等复杂度 |
| LangGraph | 底层图引擎 | 确定性状态图 + 条件分支 | 确定性 + 智能体混合工作流 |
| Deep Agents | 开箱即用的完整 Agent | 自动上下文压缩、虚拟文件系统、子 Agent | 快速构建生产级 Agent |
选择建议 :快速原型用 Deep Agents,精细控制用 LangChain
create_agent,复杂工作流用 LangGraph。
1.4 官方四大核心依赖库
| 依赖库 | 作用 | 包含内容 |
|---|---|---|
langchain-core |
底层核心抽象 | LCEL 语法、Runnable 接口、消息类型、基础提示模板 |
langchain |
高阶业务能力 | Agent 创建、中间件系统、链路编排 |
langchain-community |
第三方生态集成 | 各厂商模型适配、向量库、文档加载器 |
langgraph |
工作流引擎 | Agent 循环、状态持久化、图编排 |
二、Models 模型组件
2.1 三类模型
Models 是 LangChain 最底层组件,统一所有大模型的调用协议,屏蔽不同厂商 API 差异。
| 类型 | 用途 | 推荐程度 | 示例 |
|---|---|---|---|
| ChatModel | 对话模型(消息列表 → AI消息) | ⭐ 首选 | ChatOpenAI, ChatOllama, ChatAnthropic |
| LLM | 文本补全模型(字符串 → 字符串) | ⚠️ 遗留 | OpenAI (text-davinci) |
| Embeddings | 文本向量化(字符串 → 浮点数组) | 专用 | OpenAIEmbeddings, DashScopeEmbeddings |
重要:ChatModel 是现代 LLM 应用的标准接口,LLM(纯文本补全)已不推荐用于新项目。
2.2 init_chat_model() 统一初始化(V1.x 推荐)
LangChain V1.x 引入了 init_chat_model() 函数,一个入口初始化所有厂商的 ChatModel,无需手动导入不同的类:
python
from langchain.chat_models import init_chat_model
# 方式1:自动推断提供商(根据模型名称)
model = init_chat_model("gpt-5.5")
# 方式2:显式指定提供商(更明确)
model = init_chat_model("openai:gpt-5.5")
model = init_chat_model("anthropic:claude-sonnet-4-20250514")
model = init_chat_model("google_genai:gemini-2.5-pro")
# 方式3:带参数配置
model = init_chat_model(
"gpt-5.5",
model_provider="openai",
api_key="sk-...",
temperature=0.7,
max_tokens=2048,
)
支持的提供商:
| 提供商 | model_provider 值 | 示例模型 |
|---|---|---|
| OpenAI | openai |
gpt-5.5, gpt-4o |
| Anthropic | anthropic |
claude-sonnet-4-20250514 |
| Google Gemini | google_genai |
gemini-2.5-pro |
| Azure OpenAI | azure |
Azure 部署的 GPT |
| AWS Bedrock | bedrock |
Bedrock 上的各种模型 |
| HuggingFace | huggingface |
开源模型 |
| OpenRouter | openrouter |
多模型聚合路由 |
| Ollama | ollama |
qwen3.5, llama3 |
2.3 三种核心调用方法
所有 ChatModel 共享三个核心调用方法,适用于不同的使用场景:
invoke() --- 单次调用
等待模型生成完整回复后一次性返回,返回 AIMessage 对象:
python
from langchain_core.messages import HumanMessage
result = model.invoke([HumanMessage(content="你好")])
print(type(result)) # <class 'langchain_core.messages.AIMessage'>
print(result.content) # "你好!有什么可以帮助你的吗?"
stream() --- 流式输出
逐 Token 返回,降低首字延迟(TTFT),返回 AIMessageChunk 迭代器:
python
# 逐块输出,用户体验更流畅
for chunk in model.stream("请写一首关于春天的诗"):
print(chunk.content, end="", flush=True)
# 流式块可以累加成完整消息
full_message = None
for chunk in model.stream("你好"):
if full_message is None:
full_message = chunk
else:
full_message += chunk # AIMessageChunk 支持累加
print(full_message.content) # 完整回复
关键特性 :
AIMessageChunk对象支持+运算符,多个 chunk 可以累加成一个完整的AIMessage。
batch() --- 批量并行
一次处理多个输入,自动并行化,返回结果列表:
python
results = model.batch(["问题1", "问题2", "问题3"])
# results: [AIMessage(...), AIMessage(...), AIMessage(...)]
# 带配置的批量调用
results = model.batch(
["问题1", "问题2", "问题3"],
config={"max_concurrency": 5} # 最大并发数
)
batch_as_completed() --- 无序批量(先完成先返回):
python
# 适合某些输入耗时差异大的场景
for index, result in model.batch_as_completed(["简单问题", "复杂问题"]):
print(f"第{index}个输入完成: {result.content}")
2.4 关键参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
model |
str | 必填 | 模型名称 |
api_key |
str | 环境变量 | API 密钥,也可通过环境变量设置 |
temperature |
float | 0.7 | 随机性控制,0=确定性,1=创造性 |
max_tokens |
int | 模型默认 | 最大生成 Token 数 |
timeout |
int | 600 | 请求超时时间(秒) |
max_retries |
int | 6 | 失败重试次数 |
2.5 连接弹性与自动重试
LangChain 内置了自动重试机制,遇到以下错误会自动使用指数退避策略重试:
- 网络连接错误
- HTTP 429(速率限制)
- HTTP 5xx(服务器错误)
python
model = ChatOpenAI(
model="gpt-5.5",
max_retries=6, # 最多重试6次(默认值)
timeout=60, # 单次请求超时60秒
)
# 无需手动处理重试逻辑,框架自动完成
2.6 Tool Calling(工具调用)
通过 model.bind_tools() 让模型知道可用的工具,模型会在需要时返回工具调用请求:
python
from langchain_core.tools import tool
@tool
def search_web(query: str) -> str:
"""搜索互联网信息。"""
return f"搜索结果: {query} 的相关信息..."
@tool
def calculator(expression: str) -> str:
"""计算数学表达式。"""
return str(eval(expression))
# 绑定工具到模型
model_with_tools = model.bind_tools([search_web, calculator])
# 模型会在需要时自动决定是否调用工具
response = model_with_tools.invoke("123 * 456 等于多少?")
# response.tool_calls → [{"name": "calculator", "args": {"expression": "123*456"}, "id": "..."}]
2.7 Structured Output(结构化输出)
通过 model.with_structured_output() 约束模型输出符合指定 Schema 的结构化数据:
python
from pydantic import BaseModel, Field
class MovieReview(BaseModel):
title: str = Field(description="电影名称")
rating: int = Field(description="评分1-10")
sentiment: str = Field(description="情感倾向: positive/negative/neutral")
summary: str = Field(description="一句话总结")
# 绑定结构化输出
structured_model = model.with_structured_output(MovieReview)
result = structured_model.invoke("评价电影《星际穿越》")
# result 是 MovieReview 对象
print(result.title) # "星际穿越"
print(result.rating) # 9
print(result.sentiment) # "positive"
2.8 高级特性
多模态输入
支持图片、PDF、音频、视频等多模态内容:
python
from langchain_core.messages import HumanMessage
# 图片 URL 输入
message = HumanMessage(content=[
{"type": "text", "text": "这张图片里有什么?"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
])
response = model.invoke([message])
推理模型
部分模型(如 OpenAI o1/o3)支持深度推理模式:
python
# 推理模型会先"思考"再回答,适合复杂逻辑问题
reasoning_model = init_chat_model("openai:o3-mini")
本地模型(Ollama)
完全离线运行,数据不出本机:
python
from langchain_ollama import ChatOllama
model = ChatOllama(model="qwen3.5:2b", temperature=0.7)
Prompt Caching(提示缓存)
对重复的 System Prompt 部分启用缓存,减少 Token 消耗和延迟:
python
model = ChatAnthropic(
model="claude-sonnet-4-20250514",
cache=True, # 启用提示缓存
)
Rate Limiting(速率限制)
控制请求频率,避免超出 API 限额:
python
from langchain_core.rate_limiters import InMemoryRateLimiter
rate_limiter = InMemoryRateLimiter(
requests_per_second=2, # 每秒最多2个请求
check_every_n_seconds=0.1,
max_bucket_size=10,
)
model = ChatOpenAI(model="gpt-5.5", rate_limiter=rate_limiter)
2.9 本地模型 vs 云端模型
| 对比维度 | 本地模型(Ollama) | 云端 API |
|---|---|---|
| 费用 | 免费 | 按量付费 |
| 网络 | 完全离线 | 需要联网 |
| 隐私 | 数据不出本机 | 数据发送到云端 |
| 能力 | 受限于本地硬件 | 最强模型可用 |
| 部署 | 需要本地 GPU | 即开即用 |
| 适用 | 开发测试、隐私敏感场景 | 生产环境、复杂任务 |
python
# 本地 Ollama
from langchain_ollama import ChatOllama
model = ChatOllama(model="qwen3.5:2b")
# 云端 OpenAI
from langchain_openai import ChatOpenAI
model = ChatOpenAI(model="gpt-5.5", api_key="sk-...")
# 云端 阿里云(兼容 OpenAI 接口)
from langchain_openai import ChatOpenAI
import os
model = ChatOpenAI(
model="qwen-plus",
api_key=os.getenv("ALIYUN_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
2.10 统一模型封装模式(推荐)
开发用本地、上线切云端,业务代码无需改动:
python
def get_model(provider: str = "ollama"):
"""工厂函数:根据 provider 返回对应的 ChatModel"""
if provider == "ollama":
from langchain_ollama import ChatOllama
return ChatOllama(model="qwen3.5:2b")
elif provider == "qwen":
from langchain_openai import ChatOpenAI
return ChatOpenAI(
model="qwen-plus",
api_key=os.getenv("ALIYUN_API_KEY"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
)
elif provider == "deepseek":
from langchain_openai import ChatOpenAI
return ChatOpenAI(
model="deepseek-chat",
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com"
)
elif provider == "openai":
from langchain_openai import ChatOpenAI
return ChatOpenAI(model="gpt-5.5")
else:
raise ValueError(f"不支持的服务商: {provider}")
# 切换模型只需改一个字符串
model = get_model("ollama") # 开发阶段(免费本地)
# model = get_model("qwen") # 上线阶段(云端 API)
三、Messages 消息类型
3.1 四种消息类型
Messages 是 LangChain 中所有组件通信的基础协议,LLM 的输入输出都是消息对象:
| 消息类型 | 角色 | 作用 | 示例 |
|---|---|---|---|
SystemMessage |
导演 | 设定 AI 的人设和行为准则 | "你是一位友好的助手" |
HumanMessage |
观众 | 用户的输入,支持多模态 | "什么是人工智能" |
AIMessage |
演员 | AI 的回复,包含丰富元数据 | "AI 是..." |
ToolMessage |
工具人 | 工具执行结果,必须匹配 tool_call_id | 查询结果数据 |
3.2 SystemMessage --- 设定 AI 人设
同一个问题,不同 SystemMessage 产生完全不同的回答风格:
python
from langchain_core.messages import SystemMessage, HumanMessage
# 严肃科学家
messages = [
SystemMessage(content="你是一位严肃的科学家,说话严谨、准确,使用专业术语。"),
HumanMessage(content="请介绍一下你自己。"),
]
response = model.invoke(messages)
# 幽默脱口秀演员
messages = [
SystemMessage(content="你是一位幽默的脱口秀演员,用搞笑的方式解释一切。"),
HumanMessage(content="请介绍一下你自己。"),
]
response = model.invoke(messages)
3.3 HumanMessage --- 用户输入
用户消息,支持纯文本和多模态内容(图片、音频、视频、PDF):
python
# 纯文本
msg = HumanMessage(content="什么是机器学习?")
# 多模态(文本 + 图片)
msg = HumanMessage(content=[
{"type": "text", "text": "描述这张图片"},
{"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}},
])
# 多模态(文本 + PDF)
msg = HumanMessage(content=[
{"type": "text", "text": "总结这份文档"},
{"type": "file", "file": {"url": "data:application/pdf;base64,..."}},
])
3.4 AIMessage --- 模型响应
AI 的回复不仅仅是文本,还包含丰富的元数据:
python
response = model.invoke([HumanMessage(content="你好")])
# 核心属性
response.text # 纯文本内容(等价于 response.content 当 content 是 str 时)
response.content # 内容(可能是 str 或 list)
response.content_blocks # 结构化内容块列表(TextContentBlock, ReasoningContentBlock 等)
response.tool_calls # 工具调用列表 [{"name": ..., "args": ..., "id": ...}]
response.id # 消息唯一 ID
response.usage_metadata # Token 使用量
response.response_metadata # 原始响应元数据(模型名、完成原因等)
AIMessage 属性详解
| 属性 | 类型 | 说明 |
|---|---|---|
text |
str |
纯文本内容,最常用的属性 |
content |
`str | list` |
content_blocks |
list |
结构化内容块:TextContentBlock、ReasoningContentBlock 等 |
tool_calls |
list[dict] |
工具调用请求,每项包含 name、args、id |
id |
str |
消息唯一标识符 |
usage_metadata |
dict |
Token 使用统计 |
response_metadata |
dict |
提供商原始响应信息 |
Token 使用量追踪
通过 usage_metadata 追踪每次调用的 Token 消耗:
python
response = model.invoke("你好")
usage = response.usage_metadata
print(f"输入 Token: {usage['input_tokens']}") # 输入消耗
print(f"输出 Token: {usage['output_tokens']}") # 输出消耗
print(f"总 Token: {usage['total_token_count']}") # 总计
工具调用(tool_calls)
当模型决定调用工具时,tool_calls 列表会包含调用信息:
python
response = model_with_tools.invoke("北京天气怎么样?")
for tc in response.tool_calls:
print(f"工具名: {tc['name']}") # "get_weather"
print(f"参数: {tc['args']}") # {"city": "北京"}
print(f"调用ID: {tc['id']}") # "call_abc123"
3.5 ToolMessage --- 工具执行结果
工具执行后返回的结果,必须匹配对应的 tool_call_id:
python
from langchain_core.messages import ToolMessage
# 工具执行结果
tool_result = ToolMessage(
content="北京:晴,25°C,湿度45%", # 工具返回的内容
tool_call_id="call_abc123", # 必须匹配 AIMessage 中的 tool_call id
name="get_weather", # 工具名称(可选)
artifact={"raw_data": {...}}, # 额外数据(不会发送给模型,仅程序使用)
)
| 属性 | 必填 | 说明 |
|---|---|---|
content |
✅ | 工具返回的内容(会发送给模型) |
tool_call_id |
✅ | 必须匹配 AIMessage.tool_calls 中的 id |
name |
❌ | 工具名称 |
artifact |
❌ | 额外数据,不会发送给模型,仅程序内部使用 |
3.6 三种输入格式
LangChain 支持三种方式传入消息,灵活适配不同场景:
python
# 格式1:纯文本字符串(自动包装为 HumanMessage)
result = model.invoke("你好")
# 格式2:消息对象列表(最完整,推荐)
from langchain_core.messages import SystemMessage, HumanMessage
result = model.invoke([
SystemMessage(content="你是助手"),
HumanMessage(content="你好"),
])
# 格式3:字典格式(简洁,适合序列化)
result = model.invoke([
{"role": "system", "content": "你是助手"},
{"role": "user", "content": "你好"},
])
3.7 流式消息块(AIMessageChunk)
流式输出时,每个 chunk 是 AIMessageChunk 对象,可以累加成完整的 AIMessage:
python
full = None
for chunk in model.stream("写一首诗"):
print(chunk.content, end="", flush=True)
if full is None:
full = chunk
else:
full += chunk # 累加
# full 现在是完整的 AIMessage
print(f"\n完整回复: {full.content}")
print(f"Token 用量: {full.usage_metadata}")
3.8 多模态内容详解
图片输入
支持三种图片传入方式:
python
# 方式1:URL
HumanMessage(content=[
{"type": "text", "text": "描述这张图片"},
{"type": "image_url", "image_url": {"url": "https://example.com/photo.jpg"}},
])
# 方式2:Base64 编码
import base64
with open("photo.jpg", "rb") as f:
b64 = base64.b64encode(f.read()).decode()
HumanMessage(content=[
{"type": "text", "text": "描述这张图片"},
{"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}},
])
# 方式3:File ID(部分提供商支持)
HumanMessage(content=[
{"type": "text", "text": "描述这张图片"},
{"type": "image_url", "image_url": {"url": "file-abc123"}},
])
PDF 输入
python
HumanMessage(content=[
{"type": "text", "text": "总结这份PDF文档"},
{"type": "file", "file": {"url": "data:application/pdf;base64,..."}},
])
音频/视频输入
python
# 音频
HumanMessage(content=[
{"type": "text", "text": "转录这段音频"},
{"type": "input_audio", "input_audio": {"data": base64_audio, "format": "mp3"}},
])
# 视频(部分模型支持)
HumanMessage(content=[
{"type": "text", "text": "描述这段视频"},
{"type": "video_url", "video_url": {"url": "https://example.com/video.mp4"}},
])
3.9 标准内容块类型
AIMessage.content_blocks 中的标准内容块类型:
| 内容块类型 | 说明 |
|---|---|
TextContentBlock |
普通文本内容 |
ReasoningContentBlock |
推理过程(推理模型如 o1/o3) |
ImageContentBlock |
图片内容 |
ToolCallContentBlock |
工具调用 |
AudioContentBlock |
音频内容 |
3.10 手动构建多轮对话
LLM 本身是无状态的,要让它"记住"上下文,必须把历史消息一起传入:
python
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
messages = [
SystemMessage(content="你是一位友好的助手。"),
HumanMessage(content="我叫小明,今年10岁。"), # 第1轮用户
AIMessage(content="你好小明!有什么问题吗?"), # 第1轮AI
HumanMessage(content="你还记得我叫什么吗?"), # 第2轮用户
]
response = model.invoke(messages) # AI 能记住"小明"
核心原理:这是所有 Memory 组件的底层原理 --- 维护一个消息列表,每次调用时传入完整历史。
四、Prompts 提示模板组件
4.1 为什么需要 Prompt Template
| 不用模板 | 用模板 |
|---|---|
| 每次手写相似 Prompt,容易出错 | 一次定义,多次复用 |
| 格式不统一 | 格式统一、修改方便 |
| 无法动态传参 | {变量名} 占位符 + format() 填充 |
| 难以维护 | 集中管理,一处修改全局生效 |
4.2 PromptTemplate 基础
python
from langchain_core.prompts import PromptTemplate
# 创建模板 --- {language} 和 {concept} 是占位符
prompt = PromptTemplate.from_template("请用{language}解释{concept}。")
# 填入实际值
formatted = prompt.format(language="小学生能懂的语言", concept="机器学习")
print(formatted) # "请用小学生能懂的语言解释机器学习。"
# 调用模型
response = model.invoke(formatted)
4.3 ChatPromptTemplate --- 对话提示模板
使用角色元组定义对话结构,更符合 ChatModel 的消息格式:
python
from langchain_core.prompts import ChatPromptTemplate
# from_messages() 接受角色元组列表
prompt = ChatPromptTemplate.from_messages([
("system", "你是一位{role},请用{style}的语气回答。"),
("human", "{question}"),
])
# 格式化
messages = prompt.format_messages(
role="幼儿园老师",
style="温柔耐心",
question="什么是太阳系?"
)
# → [SystemMessage("你是一位幼儿园老师..."), HumanMessage("什么是太阳系?")]
4.4 实用场景:翻译助手模板
python
from langchain_core.prompts import PromptTemplate
prompt = PromptTemplate.from_template("""
你是一位专业翻译,请将以下文本从{source_lang}翻译成{target_lang}。
只输出翻译结果,不要解释。
原文: {text}
译文:""")
# 英译中
r = model.invoke(prompt.format(
source_lang="英语", target_lang="中文",
text="Artificial intelligence will change the world."
))
# 中译英 --- 同一个模板,不同参数
r = model.invoke(prompt.format(
source_lang="中文", target_lang="英语",
text="今天天气真好,适合出去散步。"
))
4.5 Few-Shot Prompting(给 AI 几个例子)
核心思想:给例子 → 让 AI 模仿格式回答
手动 Few-Shot(最简单)
直接在 Prompt 中写示例:
python
prompt = PromptTemplate.from_template("""
请判断以下评论的情感倾向(正面/负面)。
例子:
评论: "这家餐厅的食物很好吃!" 情感: 正面
评论: "等了一个小时才上菜,味道还很咸。" 情感: 负面
评论: "{review}"
情感:""")
4.6 FewShotPromptTemplate(LangChain 封装)
当示例很多时,用 FewShotPromptTemplate 优雅管理,遵循三步流程:
python
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
# 第1步:准备示例列表
examples = [
{"question": "法国首都是哪里?", "answer": "巴黎"},
{"question": "美国首都是哪里?", "answer": "华盛顿"},
{"question": "英国首都是哪里?", "answer": "伦敦"},
]
# 第2步:定义每个示例的显示格式
example_prompt = PromptTemplate.from_template("问: {question}\n答: {answer}")
# 第3步:创建 FewShotPromptTemplate
few_shot = FewShotPromptTemplate(
examples=examples, # 示例列表
example_prompt=example_prompt, # 示例格式模板
prefix="请模仿下面的例子回答问题:", # 前缀(放在所有示例之前)
suffix="问题: {question}\n答:", # 后缀(放在所有示例之后,包含输入变量)
input_variables=["question"], # 输入变量列表
)
# 使用
result = few_shot.format(question="日本首都是哪里?")
print(result)
# 输出:
# 请模仿下面的例子回答问题:
# 问: 法国首都是哪里?
# 答: 巴黎
# 问: 美国首都是哪里?
# 答: 华盛顿
# 问: 英国首都是哪里?
# 答: 伦敦
# 问题: 日本首都是哪里?
# 答:
关键参数说明:
| 参数 | 说明 |
|---|---|
examples |
示例数据列表,每项是字典 |
example_prompt |
每个示例的格式模板 |
prefix |
前缀文本,放在所有示例之前 |
suffix |
后缀文本,放在所有示例之后,包含输入变量 |
input_variables |
后缀中需要填充的变量名列表 |
五、Output Parsers 输出解析组件
5.1 为什么需要 Parser
AI 的原始输出是 AIMessage 对象(带元数据),程序需要的是纯字符串或结构化数据。Parser 就是"翻译官",将 AIMessage 转换为程序可直接使用的数据格式。
AIMessage (原始输出) → Parser → 程序可用数据 (str / dict / Pydantic 对象)
5.2 StrOutputParser --- 纯字符串
最简单的解析器,提取 AIMessage.content 为纯字符串:
python
from langchain_core.output_parsers import StrOutputParser
# 不用 Parser: 返回 Message 对象,需要 .content 取文本
response = model.invoke("你好")
text = response.content # 手动取
# 用 Parser: 直接返回纯字符串
chain = model | StrOutputParser()
result = chain.invoke("你好") # result 已经是 str
# 在 Pipeline 中使用
chain = prompt | model | StrOutputParser()
result = chain.invoke({"concept": "人工智能"})
# result 是纯字符串 "人工智能是..."
5.3 JsonOutputParser --- 结构化 JSON
让 AI 输出 JSON 而非自由文本,程序直接得到 dict:
python
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate
parser = JsonOutputParser()
prompt = PromptTemplate.from_template("""
分析以下产品评论,输出 JSON。
评论: {review}
{format_instructions}
JSON:""")
# 注入格式指令 --- 告诉 AI 应该输出什么格式
prompt = prompt.partial(format_instructions=parser.get_format_instructions())
chain = prompt | model | parser
result = chain.invoke({"review": "这个产品很好用,就是价格有点贵。"})
# result 是 Python dict: {"sentiment": "positive", "rating": 4, ...}
get_format_instructions():自动生成格式说明文本,告诉模型应该输出什么结构的 JSON。
5.4 PydanticOutputParser --- 带类型验证
用 Pydantic 模型定义输出 Schema,自动进行类型验证和转换:
python
from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
# 定义数据模型 --- Field 的 description 会告诉 AI 每个字段的含义
class Person(BaseModel):
name: str = Field(description="姓名")
age: int = Field(description="年龄(整数)")
job: str = Field(description="职业")
parser = PydanticOutputParser(pydantic_object=Person)
prompt = PromptTemplate.from_template("""
从文本中提取人员信息。
文本: 小明今年25岁,是一名软件工程师。
{format_instructions}
JSON:""")
prompt = prompt.partial(format_instructions=parser.get_format_instructions())
chain = prompt | model | parser
result = chain.invoke({})
# result.name → "小明" (str)
# result.age → 25 (int) ← 自动类型转换+验证
# result.job → "软件工程师" (str)
5.5 三种 Parser 对比
| Parser | 返回类型 | 类型安全 | 适用场景 |
|---|---|---|---|
StrOutputParser |
str |
无 | 自由文本场景、聊天对话 |
JsonOutputParser |
dict |
无(age 可能是字符串"二十五") | 简单结构化提取 |
PydanticOutputParser |
Pydantic 对象 | 有(age 必须是 int,否则报错) | 需要类型验证的复杂结构 |
StrOutputParser → 纯字符串 → 适合自由文本场景
JsonOutputParser → Python dict → 适合简单结构化提取
PydanticOutputParser → Pydantic 对象 → 适合需要类型验证的复杂结构
5.6 Agent 的结构化输出
在 create_agent 中使用 response_format 参数,让 Agent 输出符合 Schema 的结构化数据:
python
from pydantic import BaseModel, Field
from langchain.agents import create_agent
class ResearchReport(BaseModel):
topic: str = Field(description="研究主题")
findings: list[str] = Field(description="关键发现列表")
conclusion: str = Field(description="结论")
agent = create_agent(
model=model,
tools=[search_tool],
system_prompt="你是一个研究助手。",
response_format=ResearchReport, # 结构化输出
)
result = agent.invoke({"messages": [HumanMessage(content="研究量子计算的发展")]})
# result 包含符合 ResearchReport Schema 的结构化输出
两种结构化输出策略
| 策略 | 方式 | 特点 |
|---|---|---|
response_format |
使用提供商原生结构化输出 | 更可靠,但不是所有提供商都支持 |
tool_calling |
通过工具调用实现结构化输出 | 通用性更好,兼容更多提供商 |
错误处理
结构化输出可能遇到的错误及处理策略:
| 错误类型 | 说明 |
|---|---|
MultipleStructuredOutputsError |
模型返回了多个结构化输出,但只期望一个 |
SchemaValidationError |
模型输出不符合 Schema 定义 |
错误处理策略:
python
# 策略1:重试(默认)
agent = create_agent(
model=model, tools=[...],
response_format=MySchema,
# 默认会在解析失败时重试
)
# 策略2:回退 --- 使用 tool_calling 策略
agent = create_agent(
model=model, tools=[...],
response_format=MySchema,
# 如果 response_format 策略失败,回退到 tool_calling
)
# 策略3:忽略 --- 不做结构化约束,接受原始输出
# 不设置 response_format 参数即可