LangChain 知识总结详解(1)

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 参数即可