本文撰写于2025年10月,基于LangChain v0.3版本。随着框架的快速演进,部分API可能有所变化,请以官方文档为主:
引言
大型语言模型(LLM)的出现正在重塑软件开发的范式。然而,将LLM的强大能力整合到实际应用中并非易事——我们需要处理模型调用、数据检索、状态管理、工具集成等一系列复杂问题。LangChain作为业界领先的LLM应用开发框架,通过提供标准化接口和可组合的组件,极大地简化了这一过程。
LangChain由Harrison Chase于2022年10月创建,最初作为开源项目发布。短短几个月内,该框架就获得了业界的广泛认可。2023年4月,LangChain完成了由Sequoia Capital领投的超过2000万美元的融资,估值达到至少2亿美元。这一成功不仅反映了市场对LLM应用开发工具的强烈需求,也证明了LangChain架构设计的前瞻性。
截至2025年,LangChain生态系统已经发展成为一个完整的产品矩阵,包括核心框架、LangSmith(可观测性平台)、LangGraph(状态管理与编排)和LangServe(部署工具)。这些产品相互协作,为开发者提供了从原型设计到生产部署的完整解决方案。
本文将深入探讨LangChain框架的六大核心模块:Model I/O、Retrieval、Chains、Memory、Agents和Callbacks,并详细解析其底层实现机制、生态集成方式以及最佳实践。
内容概览
1. 架构层面
- 详细解析LangChain的模块化设计理念
- 说明包结构与依赖关系
- 深入讲解Runnable接口的核心抽象
2. 六大核心模块
Model I/O:
- Prompt Templates的工业化应用
- 统一的模型接口设计
- Output Parsers的结构化输出
- 包含ReAct等常用Agent模板
Retrieval:
- 完整的RAG流程架构
- Document Loaders支持的200+数据源
- 智能文档切分策略(RecursiveCharacterTextSplitter、SemanticChunker等)
- Embeddings原理与主流模型对比
- 50+向量数据库集成
- 高级Retriever模式
Chains:
- LCEL的设计哲学和组合模式
- RunnableSequence与RunnableParallel
- 底层实现机制与自动类型转换
- 与LangGraph的关系
Memory:
- 5种主要Memory类型的对比分析
- 从简单Buffer到智能Summary的演进
- 现代化的RunnableWithMessageHistory
- 持久化存储方案(SQL、Redis、MongoDB等)
Agents:
- ReAct、Tool Calling等Agent范式详解
- Tools定义与内置工具库
- LangGraph中的Agent实现
- Multi-Agent协作与Plan-and-Execute模式
- 调试与优化技巧
Callbacks:
- 完整的事件驱动系统
- 自定义Callback Handler实现
- 生产级集成(OpenTelemetry、Prometheus)
- 异步Callback与性能优化
3. 生态集成
- LangSmith、LangServe、LangGraph等内部生态
- 100+模型提供商集成(OpenAI、Anthropic、Google等)
- 50+向量数据库支持
- 企业数据源集成(Google Drive、Notion、Slack等)
- 与LlamaIndex、Haystack等框架的互操作
4. 实践指导
- 完整的RAG应用架构示例
- 性能优化策略(批处理、缓存、流式处理)
- 成本优化与错误处理
- 安全性考虑(Prompt注入防护、敏感信息过滤)
一、LangChain整体架构
1.1 架构设计理念
LangChain的架构基于三个核心设计原则:
模块化(Modularity):每个组件都是独立的、可替换的模块。开发者可以根据需求自由组合各种组件,而不必担心耦合问题。这种设计使得技术栈的演进变得轻松——当新的模型提供商或向量数据库出现时,只需实现标准接口即可无缝集成。
可组合性(Composability):组件之间通过标准化接口进行交互,可以像搭积木一样构建复杂应用。LangChain Expression Language(LCEL)进一步强化了这一特性,允许开发者用声明式语法快速构建处理管道。
生产就绪(Production-Ready):框架不仅关注原型开发,更注重生产环境的需求。内置的流式处理、异步支持、批处理能力,以及与LangSmith的深度集成,确保应用可以平滑地从实验阶段过渡到生产环境。
1.2 包结构与依赖关系
LangChain生态系统采用分层包结构,每个包都有明确的职责边界:
langchain-core ← 核心抽象和接口定义
↓
langchain-community ← 社区维护的集成
↓
langchain ← 高级组件(chains、agents等)
↓
langchain-{provider} ← 特定提供商的集成包
langchain-core:定义了整个生态系统的基础抽象,包括Runnable接口、消息类型、工具定义等。所有其他包都依赖于core包,确保了接口的一致性。
集成包(如langchain-openai、langchain-anthropic):每个主要的模型提供商和数据存储都有独立的集成包。这种设计使得版本管理更加灵活,开发者可以只安装需要的集成,减少依赖冲突。
langchain主包:包含预构建的chains、检索策略和agent实现。这些是跨提供商的通用组件,代表了应用的"认知架构"。
langgraph:专门用于构建有状态、多步骤的agent应用。它将LangChain组件组织成图结构,支持复杂的控制流和状态持久化。
1.3 核心抽象:Runnable接口
Runnable是LangChain中最基础的抽象,所有组件(模型、提示模板、检索器等)都实现了这一接口。Runnable定义了一组标准方法:
- invoke(): 同步执行,接收输入并返回输出
- ainvoke(): 异步执行版本
- batch(): 批量处理多个输入
- stream(): 流式输出结果
- astream_events(): 流式输出细粒度事件
这种统一的接口设计带来了巨大的灵活性。无论是简单的模型调用,还是复杂的multi-step agent,都可以用相同的方式调用和组合。
# 所有这些组件都实现了Runnable接口
model = ChatOpenAI()
prompt = ChatPromptTemplate.from_template("Translate {text} to {language}")
chain = prompt | model # 使用管道操作符组合
# 统一的调用方式
result = chain.invoke({"text": "Hello", "language": "Spanish"})
async_result = await chain.ainvoke({"text": "Hello", "language": "Spanish"})
stream = chain.stream({"text": "Hello", "language": "Spanish"})
二、Model I/O:标准化的模型交互
2.1 模块概述
Model I/O模块是LangChain与语言模型交互的核心层,它解决了不同模型提供商API差异带来的集成难题。该模块包含三个关键组件:
- Prompt Templates:提示模板管理与格式化
- Models:语言模型的标准化接口
- Output Parsers:结构化输出解析
这种三段式设计将输入准备、模型调用和输出处理清晰分离,使得每个环节都可以独立优化和替换。
2.2 Prompt Templates:提示工程的工业化
2.2.1 提示模板的必要性
在LLM应用中,提示(prompt)的质量直接决定了输出的质量。然而,手工拼接提示字符串既容易出错,又难以维护。Prompt Templates将提示转化为可参数化、可重用的组件。
LangChain提供了两类主要的提示模板:
PromptTemplate:用于传统的文本补全模型,接收字符串输入并生成格式化的提示字符串。
from langchain_core.prompts import PromptTemplate
template = PromptTemplate.from_template(
"将以下{style}风格的文本翻译成{language}:\n{text}"
)
# 模板自动推断变量
print(template.input_variables) # ['style', 'language', 'text']
# 格式化提示
formatted = template.format(
style="正式",
language="英语",
text="今天天气不错"
)
ChatPromptTemplate:专为对话模型设计,支持多轮对话和角色区分。
from langchain_core.prompts import ChatPromptTemplate
chat_template = ChatPromptTemplate.from_messages([
("system", "你是一个专业的{domain}专家助手"),
("human", "请解释{concept}"),
("ai", "让我为您详细解释..."),
("human", "{followup_question}")
])
2.2.2 高级模板功能
MessagesPlaceholder:动态插入消息列表,特别适合处理对话历史。
from langchain_core.prompts import MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
("system", "你是一个友好的助手"),
MessagesPlaceholder("chat_history"), # 动态插入历史消息
("human", "{input}")
])
# 使用时可以传入完整的消息历史
messages = template.invoke({
"chat_history": [
HumanMessage(content="我叫张三"),
AIMessage(content="你好张三!")
],
"input": "我叫什么名字?"
})
Few-shot Learning Templates:通过示例引导模型行为,实现上下文学习。
from langchain_core.prompts import FewShotPromptTemplate
examples = [
{"input": "开心", "output": "happy"},
{"input": "悲伤", "output": "sad"}
]
example_template = PromptTemplate(
input_variables=["input", "output"],
template="中文: {input}\n英文: {output}"
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_template,
prefix="请翻译以下中文词汇:",
suffix="中文: {input}\n英文:",
input_variables=["input"]
)
2.2.3 Agent中的常用模板
ReAct模板:结合推理(Reasoning)和行动(Acting),是构建agent的经典模板。
Answer the following questions as best you can. You have access to the following tools:
{tools}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can repeat N times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
这个模板引导模型以结构化方式思考问题,明确区分推理过程和工具调用,使得agent的决策过程变得可追踪、可调试。
2.3 Language Models:统一的模型接口
2.3.1 模型类型
LangChain支持两类主要的语言模型:
| 模型类型 | 接口 | 输入 | 输出 | 适用场景 |
|---|---|---|---|---|
| LLMs(遗留) | BaseLLM | 字符串 | 字符串 | 简单的文本补全任务 |
| Chat Models | BaseChatModel | 消息列表 | AI消息 | 对话、多轮交互、工具调用 |
现代应用主要使用Chat Models,因为它们:
- 支持角色区分(system、human、ai)
- 原生支持工具调用(function calling)
- 更好地理解对话上下文
2.3.2 模型初始化与配置
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
# OpenAI模型
openai_model = ChatOpenAI(
model="gpt-4",
temperature=0.7,
max_tokens=2000,
timeout=30,
max_retries=3
)
# Anthropic Claude
claude_model = ChatAnthropic(
model="claude-3-opus-20240229",
temperature=0.7,
max_tokens=4000
)
2.4 Output Parsers:结构化输出的关键
2.4.1 为什么需要Output Parser?
LLM的原始输出是非结构化文本,但实际应用往往需要结构化数据。Output Parser将文本转换为可编程处理的数据结构,如JSON、列表或Pydantic对象。
2.4.2 常用Parser类型
PydanticOutputParser:利用Pydantic进行类型验证和自动解析。
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
class Person(BaseModel):
name: str = Field(description="人物姓名")
age: int = Field(description="年龄")
occupation: str = Field(description="职业")
parser = PydanticOutputParser(pydantic_object=Person)
# Parser会生成格式说明
format_instructions = parser.get_format_instructions()
# 结合提示模板使用
prompt = PromptTemplate(
template="提取以下文本中的人物信息:\n{text}\n{format_instructions}",
input_variables=["text"],
partial_variables={"format_instructions": format_instructions}
)
StructuredOutputParser:为简单的结构化数据提供轻量级解析。
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
response_schemas = [
ResponseSchema(name="sentiment", description="文本的情感倾向"),
ResponseSchema(name="confidence", description="判断的置信度")
]
parser = StructuredOutputParser.from_response_schemas(response_schemas)
JsonOutputParser:专门处理JSON格式输出,支持流式解析。
from langchain.output_parsers import SimpleJsonOutputParser
json_parser = SimpleJsonOutputParser()
# 支持流式解析,逐步构建JSON对象
for chunk in chain.stream(query):
print(json_parser.parse_partial(chunk))
2.4.3 完整的Model I/O管道
# 构建完整的输入-处理-输出管道
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("讲一个关于{topic}的笑话")
model = s| output_parser
# 调用链
result = chain.invoke({"topic": "程序员"})
2.4.4 解析器类型对比
主要解析器类型:
| 解析器类型 | 功能描述 | 适用场景 |
|---|---|---|
| StrOutputParser | 提取 AIMessage 的文本内容 | 简单文本提取 |
| JsonOutputParser | 解析 JSON 格式输出 | 结构化数据提取 |
| PydanticOutputParser | 使用 Pydantic 模型验证输出 | 类型安全的数据提取 |
| CommaSeparatedListOutputParser | 解析逗号分隔列表 | 列表数据提取 |
| DatetimeOutputParser | 解析日期时间 | 时间相关数据 |
2.5 工具调用(Tool/Function Calling)
现代Chat Models支持工具调用能力,使LLM能够主动调用外部函数获取信息或执行操作。
from langchain.tools import tool
@tool
def search_weather(city: str) -> str:
"""查询指定城市的天气信息"""
# 实际的天气API调用
return f"{city}的天气:晴朗,温度25°C"
# 将工具绑定到模型
model_with_tools = model.bind_tools([search_weather])
# 模型会决定是否调用工具
response = model_with_tools.invoke("北京的天气怎么样?")
三、Retrieval:外部知识的桥梁
3.1 为什么需要Retrieval?
LLM虽然强大,但面临三个根本性限制:
- 知识截止日期:模型只了解训练数据中的信息,无法获取最新知识
- 私有数据盲区:无法访问企业内部文档、数据库等专有信息
- 上下文窗口限制:即使最先进的模型,上下文窗口也是有限的(通常几万到几十万token)
Retrieval模块通过检索增强生成(RAG)技术解决这些问题,使LLM能够访问外部知识库。
3.2 Retrieval流程架构
RAG系统的标准流程包括两个阶段:
离线索引阶段:
原始文档 → 加载 → 切分 → 向量化 → 存储到向量数据库
在线检索阶段:
用户查询 → 向量化 → 相似度搜索 → 检索相关文档 → 增强提示 → LLM生成
3.3 Document Loaders:数据摄取
3.3.1 支持的数据源
LangChain提供了200多种文档加载器,涵盖几乎所有常见数据源:
| 类别 | 示例加载器 | 用途 |
|---|---|---|
| 文件系统 | TextLoader, PyPDFLoader, UnstructuredFileLoader | 本地文档处理 |
| 网络资源 | WebBaseLoader, WikipediaLoader | 网页爬取 |
| 数据库 | SQLDatabaseLoader | 结构化数据导入 |
| 云存储 | S3Loader, GoogleDriveLoader | 云端文档同步 |
| API服务 | ArxivLoader, PubMedLoader | 学术文献检索 |
3.3.2 实际使用示例
from langchain_community.document_loaders import (
PyPDFLoader,
WebBaseLoader,
WikipediaLoader
)
# PDF文档加载
pdf_loader = PyPDFLoader("technical_report.pdf")
pdf_docs = pdf_loader.load()
# 每页PDF生成一个Document对象
for doc in pdf_docs:
print(doc.page_content[:100]) # 文本内容
print(doc.metadata) # 元数据:{'source': '...', 'page': 0}
# 网页内容加载
web_loader = WebBaseLoader("https://blog.langchain.dev/")
web_docs = web_loader.load()
# Wikipedia条目加载
wiki_loader = WikipediaLoader(query="Artificial Intelligence", load_max_docs=5)
wiki_docs = wiki_loader.load()
3.4 Text Splitters:智能文档切分
3.4.1 为什么需要切分?
大文档直接喂给LLM存在多个问题:
- 超出模型的上下文窗口限制
- 降低检索精度(粗粒度的文档难以精确匹配查询)
- 增加推理成本(处理更多不必要的token)
文档切分将长文本分割成语义相关的小块(chunks),每块可以独立索引和检索。
3.4.2 切分策略
RecursiveCharacterTextSplitter:最通用的切分器,按层级分隔符递归切分。
from langchain.text_splitters import RecursiveCharacterTextSplitter
# 默认分隔符顺序:\n\n → \n → 空格 → 字符
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每块目标大小
chunk_overlap=200, # 块间重叠,保持上下文连续性
length_function=len, # 长度计算函数
separators=["\n\n", "\n", " ", ""] # 自定义分隔符优先级
)
chunks = splitter.split_documents(documents)
语言专用Splitters:根据编程语言语法智能切分代码。
from langchain.text_splitters import (
PythonCodeTextSplitter,
MarkdownHeaderTextSplitter
)
# Python代码切分
python_splitter = PythonCodeTextSplitter(chunk_size=500)
code_chunks = python_splitter.split_text(python_code)
# Markdown按标题层级切分
markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
)
SemanticChunker:基于语义相似度的智能切分,而非固定字符数。
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai import OpenAIEmbeddings
# 当相邻句子语义差异超过阈值时切分
semantic_chunker = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile"
)
3.4.3 Token级切分
对于需要精确控制token数量的场景,可以使用基于tokenizer的切分器:
from langchain.text_splitters import CharacterTextSplitter
# 使用tiktoken(OpenAI的tokenizer)
token_splitter = CharacterTextSplitter.from_tiktoken_encoder(
encoding_name="cl100k_base", # GPT-4的编码器
chunk_size=400,
chunk_overlap=50
)
3.5 Embeddings:语义向量化
3.5.1 Embedding原理
Embedding模型将文本转换为高维向量空间中的点。语义相似的文本在向量空间中距离较近,这使得我们可以通过数学计算(如余弦相似度)量化文本相似性。
from langchain_openai import OpenAIEmbeddings
from langchain_huggingface import HuggingFaceEmbeddings
# OpenAI Embedding
openai_embeddings = OpenAIEmbeddings(
model="text-embedding-3-large" # 3072维向量
)
# HuggingFace开源模型
hf_embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-large-en-v1.5", # 1024维
encode_kwargs={'normalize_embeddings': True}
)
# 生成embedding
text = "LangChain simplifies LLM application development"
vector = openai_embeddings.embed_query(text)
print(f"向量维度: {len(vector)}") # 3072
3.5.2 主流Embedding模型对比
| 提供商 | 模型 | 维度 | 优势 |
|---|---|---|---|
| OpenAI | text-embedding-3-large | 3072 | 高质量、多语言支持 |
| Cohere | embed-english-v3.0 | 1024 | 检索优化 |
| text-embedding-004 | 768 | 多模态支持 | |
| HuggingFace | bge-large-zh-v1.5 | 1024 | 中文优化、开源 |
| Voyage AI | voyage-2 | 1024 | 定制化fine-tuning |
3.6 Vector Stores:高效向量存储与检索
3.6.1 向量数据库的作用
向量数据库专门优化了高维向量的存储和相似度搜索。相比传统数据库,它们使用专门的索引结构(如HNSW、IVF)实现毫秒级的最近邻搜索。
3.6.2 集成的向量数据库
LangChain支持50+向量数据库,主要分为几类:
内存型(适合原型开发):
from langchain_community.vectorstores import FAISS, Chroma
# FAISS:Facebook开发的高效向量检索库
vector_store = FAISS.from_documents(
documents=chunks,
embedding=embeddings
)
# 本地持久化
vector_store.save_local("faiss_index")
# 加载已保存的索引
loaded_store = FAISS.load_local("faiss_index", embeddings)
独立部署型(生产环境推荐):
from langchain_community.vectorstores import (
Qdrant,
Weaviate,
Milvus,
Pinecone
)
# Qdrant示例
from qdrant_client import QdrantClient
client = QdrantClient(host="localhost", port=6333)
qdrant_store = Qdrant(
client=client,
collection_name="my_documents",
embeddings=embeddings
)
云托管型:
from langchain_pinecone import PineconeVectorStore
# Pinecone云服务
pinecone_store = PineconeVectorStore(
index_name="langchain-index",
embedding=embeddings
)
3.6.3 相似度搜索策略
基本相似度搜索:
# 返回最相似的k个文档
results = vector_store.similarity_search(
query="What is LangChain?",
k=4
)
# 带相似度分数
results_with_scores = vector_store.similarity_search_with_score(
query="What is LangChain?",
k=4
)
MMR(Maximal Marginal Relevance)搜索:平衡相关性和多样性。
# 避免检索到的文档过于相似
mmr_results = vector_store.max_marginal_relevance_search(
query="What is LangChain?",
k=4,
fetch_k=20, # 先获取20个候选
lambda_mult=0.5 # 平衡相关性(1.0)和多样性(0.0)
)
元数据过滤:结合向量相似度和结构化过滤。
# 只在特定来源中搜索
filtered_results = vector_store.similarity_search(
query="pricing information",
k=4,
filter={"source": "product_docs", "version": "2024"}
)
3.7 Retrievers:抽象检索接口
Retriever是对向量存储的进一步抽象,提供了统一的检索接口。
# 将向量存储转换为Retriever
retriever = vector_store.as_retriever(
search_type="mmr",
search_kwargs={"k": 6, "fetch_k": 20}
)
# Retriever可以直接用于链中
from langchain_core.runnables import RunnablePassthrough
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
)
3.7.1 高级Retriever类型
MultiQueryRetriever:生成多个查询变体,综合检索结果。
from langchain.retrievers.multi_query import MultiQueryRetriever
multi_query_retriever = MultiQueryRetriever.from_llm(
retriever=vector_store.as_retriever(),
llm=ChatOpenAI(temperature=0)
)
# 自动生成3-5个查询变体,合并去重结果
results = multi_query_retriever.invoke("How does RAG work?")
ContextualCompressionRetriever:对检索结果进行压缩和重排序。
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
compressor = LLMChainExtractor.from_llm(llm)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=vector_store.as_retriever()
)
# 只返回文档中与查询最相关的部分
compressed_docs = compression_retriever.invoke("What is LangChain?")
ParentDocumentRetriever:检索小块,返回大块文档。
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
# 用小块进行精确匹配,返回完整上下文
parent_retriever = ParentDocumentRetriever(
vectorstore=vector_store,
docstore=InMemoryStore(),
child_splitter=small_chunk_splitter,
parent_splitter=large_chunk_splitter,
)
3.8 与生态系统的集成
3.8.1 与LlamaIndex的协作
LlamaIndex专注于数据索引和检索,与LangChain形成互补关系:
from llama_index import VectorStoreIndex, ServiceContext
from langchain.llms import OpenAI
# LlamaIndex构建索引
index = VectorStoreIndex.from_documents(documents)
# 作为LangChain retriever使用
langchain_retriever = index.as_retriever()
3.8.2 图数据库集成
对于需要结构化知识的场景,可以集成图数据库:
from langchain_community.graphs import Neo4jGraph
from langchain.chains import GraphCypherQAChain
# 连接Neo4j
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="neo4j",
password="password"
)
# 自动生成Cypher查询
qa_chain = GraphCypherQAChain.from_llm(
llm=ChatOpenAI(),
graph=graph,
verbose=True
)
四、Chains:可组合的处理管道
4.1 Chains的演进
LangChain的命名来源于"链"(Chain)这一核心概念。早期版本提供了LLMChain、SequentialChain等预构建链,但这些链存在灵活性不足、难以定制等问题。
2023年第三季度,LangChain引入了LangChain Expression Language(LCEL),彻底改变了链的构建方式。
4.2 LangChain Expression Language(LCEL)
4.2.1 LCEL的设计哲学
LCEL是一种声明式的组合语法,采用管道操作符(|)连接组件。它的核心优势:
- 声明式编程:描述"做什么"而非"怎么做",让LangChain负责执行优化
- 自动化能力:自动支持流式、批处理、异步等多种调用模式
- 无缝追踪:与LangSmith深度集成,自动记录每个步骤
- 生产部署:通过LangServe一键部署为REST API
4.2.2 基本组合模式
顺序链(RunnableSequence):
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
# 使用管道操作符自动创建RunnableSequence
prompt = ChatPromptTemplate.from_template("讲一个关于{topic}的笑话")
model = ChatOpenAI()
output_parser = StrOutputParser()
chain = prompt | model | output_parser
# 一次调用,多种模式
result = chain.invoke({"topic": "AI"}) # 同步
async_result = await chain.ainvoke({"topic": "AI"}) # 异步
stream = chain.stream({"topic": "AI"}) # 流式
# 批量处理
batch_results = chain.batch([
{"topic": "AI"},
{"topic": "blockchain"},
{"topic": "quantum computing"}
])
并行执行(RunnableParallel):
from langchain_core.runnables import RunnableParallel
# 同时执行多个操作
parallel_chain = RunnableParallel(
joke=prompt | model,
poem=poem_prompt | model,
story=story_prompt | model
)
# 返回字典,包含所有结果
results = parallel_chain.invoke({"topic": "programming"})
# {'joke': '...', 'poem': '...', 'story': '...'}
4.2.3 高级组合模式
RunnablePassthrough:传递输入数据。
from langchain_core.runnables import RunnablePassthrough
chain = (
RunnablePassthrough.assign(
context=retriever, # 添加检索到的上下文
question=lambda x: x["question"] # 保留原始问题
)
| prompt
| model
)
RunnableLambda:嵌入自定义函数。
from langchain_core.runnables import RunnableLambda
def extract_keywords(text):
# 自定义逻辑
return text.split()[:5]
chain = (
prompt
| model
| RunnableLambda(extract_keywords) # 包装Python函数
)
条件分支(RunnableBranch):
from langchain_core.runnables import RunnableBranch
branch = RunnableBranch(
(lambda x: len(x["question"]) < 10, short_answer_chain),
(lambda x: "code" in x["question"], code_chain),
default_chain # 默认分支
)
4.2.4 错误处理与重试
Fallback机制:
# 主模型失败时自动切换备用模型
chain_with_fallback = (
prompt
| model.with_fallbacks([backup_model_1, backup_model_2])
| output_parser
)
自动重试:
# 出错时自动重试,指数退避
chain_with_retry = (
prompt
| model.with_retry(
stop_after_attempt=3,
wait_exponential_multiplier=1,
wait_exponential_max=10
)
| output_parser
)
4.3 底层实现机制
4.3.1 Runnable接口的实现
所有LCEL组件都实现了Runnable协议:
class Runnable(ABC):
def invoke(self, input: Input, config: Optional[RunnableConfig] = None) -> Output:
"""同步调用"""
async def ainvoke(self, input: Input, config: Optional[RunnableConfig] = None) -> Output:
"""异步调用"""
def stream(self, input: Input, config: Optional[RunnableConfig] = None) -> Iterator[Output]:
"""流式输出"""
def batch(self, inputs: List[Input], config: Optional[RunnableConfig] = None) -> List[Output]:
"""批量处理"""
def __or__(self, other: Runnable) -> RunnableSequence:
"""重载 | 操作符,创建顺序链"""
return RunnableSequence(first=self, last=other)
4.3.2 自动类型转换
LCEL在运行时自动进行类型转换:
- 字典 → RunnableParallel:
{"a": runnable1, "b": runnable2}自动转换为并行执行 - 函数 → RunnableLambda:普通Python函数自动包装为Runnable
- 列表 → RunnableSequence:
[runnable1, runnable2, runnable3]转换为顺序链
这种设计使得语法更加简洁:
# 简洁语法
chain = {
"context": retriever,
"question": lambda x: x
} | prompt | model
# 等价的显式语法
chain = RunnableParallel({
"context": retriever,
"question": RunnableLambda(lambda x: x)
}) | prompt | model
4.3.3 流式处理的优化
LCEL自动优化流式处理路径,最小化Time-to-First-Token(首次输出时间):
# 流式处理每个组件的输出
async for chunk in chain.astream(input):
print(chunk, end="", flush=True)
# 更细粒度的事件流
async for event in chain.astream_events(input, version="v2"):
if event["event"] == "on_chat_model_stream":
print(event["data"]["chunk"].content, end="")
4.4 预构建链与模板
尽管LCEL是主流,LangChain仍提供一些常用的预构建链:
RetrievalQA:简单的问答链。
ConversationalRetrievalChain:带对话历史的问答。
LLMChain:基本的提示-模型-解析链(已废弃,建议使用LCEL)。
这些预构建链适合快速原型开发,但LCEL提供了更好的灵活性和可维护性。
4.5 与LangGraph的关系
对于复杂的有状态应用(如多步骤agent),LangGraph提供了更强大的编排能力:
from langgraph.graph import StateGraph, END
# 定义状态
class AgentState(TypedDict):
messages: List[BaseMessage]
next_step: str
# 构建图
workflow = StateGraph(AgentState)
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", "agent")
workflow.set_entry_point("agent")
app = workflow.compile()
LangGraph将状态管理、条件分支、循环等复杂逻辑显式化,适合构建生产级agent应用。
五、Memory:对话的连续性
5.1 为什么需要Memory?
LLM本质上是无状态的——每次调用都是独立的,模型不会记住之前的对话。但实际应用(如聊天机器人、客服系统)需要维护对话上下文。Memory模块解决了这个问题。
5.2 Memory的类型与权衡
| Memory类型 | 存储内容 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| ConversationBufferMemory | 完整对话历史 | 信息完整 | Token消耗大 | 短对话 |
| ConversationBufferWindowMemory | 最近K轮对话 | Token可控 | 遗忘早期内容 | 中等长度对话 |
| ConversationSummaryMemory | 对话摘要 | 压缩率高 | 信息损失 | 长时对话 |
| ConversationSummaryBufferMemory | 摘要+最近对话 | 平衡完整性和效率 | 实现复杂 | 生产环境推荐 |
| ConversationEntityMemory | 实体知识图谱 | 结构化记忆 | 需要NER | 知识密集型对话 |
5.3 Memory实现详解
5.3.1 ConversationBufferMemory
最简单的内存实现,直接存储所有消息:
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(return_messages=True)
# 手动添加对话
memory.save_context(
{"input": "你好,我叫李明"},
{"output": "你好李明!很高兴认识你"}
)
# 加载历史
history = memory.load_memory_variables({})
print(history['history'])
# [HumanMessage(content='你好,我叫李明'),
# AIMessage(content='你好李明!很高兴认识你')]
5.3.2 ConversationBufferWindowMemory
滑动窗口机制,只保留最近K次交互:
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(
k=3, # 只保留最近3轮对话
return_messages=True
)
# 随着对话进行,旧消息自动移出窗口
for i in range(10):
memory.save_context({"input": f"问题{i}"}, {"output": f"回答{i}"})
# 只能看到最后3轮
history = memory.load_memory_variables({})
print(len(history['history'])) # 6条消息(3轮 * 2条/轮)
5.3.3 ConversationSummaryMemory
使用LLM定期总结对话内容:
from langchain.memory import ConversationSummaryMemory
memory = ConversationSummaryMemory(
llm=ChatOpenAI(temperature=0),
return_messages=True
)
# 对话会自动被总结
memory.save_context(
{"input": "我在寻找一款适合编程的笔记本电脑,预算在8000-10000元"},
{"output": "根据您的需求,我推荐几款..."}
)
memory.save_context(
{"input": "我主要用Python和JavaScript开发"},
{"output": "那么我建议选择内存至少16GB的型号..."}
)
# 查看总结
print(memory.buffer)
# "用户在寻找编程笔记本电脑,预算8000-10000元,主要使用Python和JavaScript..."
5.3.4 ConversationSummaryBufferMemory
结合摘要和缓冲,是生产环境的最佳选择:
from langchain.memory import ConversationSummaryBufferMemory
memory = ConversationSummaryBufferMemory(
llm=ChatOpenAI(),
max_token_limit=500, # token上限
return_messages=True
)
# 当历史超过上限时:
# 1. 保留最近的对话(buffer)
# 2. 将更早的对话总结(summary)
# 3. 最终提示:summary + recent_messages
5.4 现代化的Memory管理(v0.3+)
从LangChain 0.3开始,推荐使用RunnableWithMessageHistory替代传统Memory类:
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_community.chat_message_histories import ChatMessageHistory
# 内存存储
store = {}
def get_session_history(session_id: str) -> ChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]
# 包装链以支持历史
conversational_chain = RunnableWithMessageHistory(
runnable=chain,
get_session_history=get_session_history,
input_messages_key="question",
history_messages_key="chat_history"
)
# 调用时指定session_id
response = conversational_chain.invoke(
{"question": "我叫什么名字?"},
config={"configurable": {"session_id": "user_123"}}
)
5.5 持久化存储
5.5.1 内存持久化选项
文件系统:
from langchain_community.chat_message_histories import FileChatMessageHistory
history = FileChatMessageHistory("conversation_history.json")
数据库存储:
from langchain_community.chat_message_histories import (
SQLChatMessageHistory,
RedisChatMessageHistory,
MongoDBChatMessageHistory
)
# PostgreSQL
sql_history = SQLChatMessageHistory(
connection_string="postgresql://user:pass@localhost/dbname",
session_id="session_123"
)
# Redis(适合高并发场景)
redis_history = RedisChatMessageHistory(
session_id="session_123",
url="redis://localhost:6379"
)
5.5.2 LangGraph的持久化方案
LangGraph提供了内置的持久化层:
from langgraph.checkpoint.memory import MemorySaver
from langgraph.checkpoint.sqlite import SqliteSaver
# 内存checkpointer(开发用)
memory_checkpointer = MemorySaver()
# SQLite持久化(生产用)
sqlite_checkpointer = SqliteSaver.from_conn_string("checkpoints.db")
# 编译时指定checkpointer
app = workflow.compile(checkpointer=sqlite_checkpointer)
5.6 实现自定义Memory
对于特殊需求,可以实现自定义内存类:
from langchain_core.chat_history import BaseChatMessageHistory
from pydantic import BaseModel, Field
class CustomMemory(BaseChatMessageHistory, BaseModel):
messages: List[BaseMessage] = Field(default_factory=list)
user_profile: dict = Field(default_factory=dict)
def add_messages(self, messages: List[BaseMessage]) -> None:
"""添加消息并更新用户画像"""
self.messages.extend(messages)
self._update_profile(messages)
def _update_profile(self, messages):
"""从对话中提取用户信息"""
# 自定义逻辑:使用NER提取实体
pass
def clear(self) -> None:
self.messages = []
六、Agents:自主决策的智能体
6.1 什么是Agent?
Agent是LangChain中最具智能的组件,它赋予LLM"代理能力"——根据任务自主决策使用哪些工具、按什么顺序执行。
与静态的Chain不同,Agent在运行时动态规划行动路径。
Agent的核心循环:
观察(Observation) → 思考(Thought) → 行动(Action) → 观察...
这个循环持续到Agent认为任务完成。
6.2 Agent的架构组件
一个完整的Agent系统包含:
- Agent:决策引擎,根据当前状态选择下一步行动
- Tools:Agent可以调用的函数集合
- AgentExecutor:执行循环的协调器
- Memory:维护对话历史(可选)
6.3 Tools:Agent的工具箱
6.3.1 定义Tool
使用@tool装饰器快速创建工具:
from langchain.tools import tool
@tool
def search_wikipedia(query: str) -> str:
"""在Wikipedia上搜索信息。
Args:
query: 搜索关键词
Returns:
搜索结果摘要
"""
# 实际的Wikipedia API调用
from wikipedia import summary
return summary(query, sentences=3)
@tool
def calculate(expression: str) -> float:
"""计算数学表达式。
Args:
expression: 数学表达式,如 "2 + 3 * 4"
Returns:
计算结果
"""
return eval(expression)
tools = [search_wikipedia, calculate]
6.3.2 Tool的元数据
Tool的描述至关重要——Agent通过描述决定何时使用该工具。好的描述应该:
- 清晰说明工具的功能
- 明确参数的含义
- 说明返回值的格式
- 提供使用示例(通过Few-shot)
6.3.3 内置Tools
LangChain提供了丰富的预构建工具:
from langchain_community.tools import (
DuckDuckGoSearchRun, # 网络搜索
WikipediaQueryRun, # Wikipedia
PythonREPLTool, # Python代码执行
ShellTool, # Shell命令
FileManagementToolkit, # 文件操作
SQLDatabaseToolkit, # 数据库查询
)
# 使用Tavily搜索(推荐)
from langchain_community.tools.tavily_search import TavilySearchResults
search = TavilySearchResults(max_results=2)
6.4 Agent类型详解
6.4.1 ReAct Agent
基于"Reasoning and Acting"论文,是最经典的Agent范式。
ReAct的提示模板:
You have access to the following tools:
- search: Search the web for information
- calculator: Perform mathematical calculations
Use the following format:
Question: the input question
Thought: think about what to do
Action: the tool to use, must be one of [search, calculator]
Action Input: the input to the tool
Observation: the result of the action
... (repeat Thought/Action/Action Input/Observation N times)
Thought: I now know the final answer
Final Answer: the final answer to the question
实现方式:
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub
# 使用LangChain Hub中的标准ReAct提示
react_prompt = hub.pull("hwchase17/react")
# 创建Agent
agent = create_react_agent(
llm=ChatOpenAI(temperature=0),
tools=tools,
prompt=react_prompt
)
# 包装为Executor
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
verbose=True, # 显示中间步骤
max_iterations=5, # 防止死循环
handle_parsing_errors=True # 容错处理
)
# 执行
response = agent_executor.invoke({
"input": "巴黎的人口是多少?如果增加20%是多少?"
})
执行过程示例:
Thought: 我需要先查找巴黎的当前人口
Action: search
Action Input: Paris population 2024
Observation: Paris has approximately 2.2 million people in the city proper...
Thought: 现在我知道人口是220万,需要计算增加20%后的数字
Action: calculator
Action Input: 2.2 * 1.2
Observation: 2.64
Thought: 我现在知道最终答案
Final Answer: 巴黎目前人口约220万,增加20%后约为264万。
6.4.2 Tool Calling Agent
利用模型原生的工具调用(Function Calling)能力,更高效、更可靠。
from langchain.agents import create_tool_calling_agent
# 创建支持工具调用的Agent
tool_calling_agent = create_tool_calling_agent(
llm=ChatOpenAI(model="gpt-4"), # 需要支持function calling
tools=tools,
prompt=prompt
)
agent_executor = AgentExecutor(
agent=tool_calling_agent,
tools=tools,
verbose=True
)
对比ReAct Agent:
| 特性 | ReAct Agent | Tool Calling Agent |
|---|---|---|
| 实现方式 | 文本提示 + 解析 | 原生API调用 |
| 可靠性 | 依赖解析准确性 | 结构化输出,更可靠 |
| 效率 | 可能需要多轮修正 | 一次调用返回工具选择 |
| 可解释性 | 有明确的Thought | 缺少推理过程 |
| 适用模型 | 所有文本模型 | 需要支持function calling |
6.4.3 OpenAI Functions Agent
专为OpenAI的function calling优化:
from langchain.agents import create_openai_functions_agent
openai_agent = create_openai_functions_agent(
llm=ChatOpenAI(model="gpt-4"),
tools=tools,
prompt=ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant"),
("placeholder", "{chat_history}"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
)
6.4.4 Structured Chat Agent
适合复杂的多工具场景,支持工具的输入输出都是结构化数据:
from langchain.agents import create_structured_chat_agent
structured_agent = create_structured_chat_agent(
llm=ChatOpenAI(),
tools=tools,
prompt=structured_prompt
)
6.5 LangGraph中的Agent实现
对于生产环境,推荐使用LangGraph构建Agent:
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver
# 一行代码创建Agent
memory = MemorySaver()
agent = create_react_agent(
model=ChatOpenAI(model="gpt-4"),
tools=tools,
checkpointer=memory # 自动持久化
)
# 支持中断和恢复
config = {"configurable": {"thread_id": "conversation_123"}}
# 流式输出
for chunk in agent.stream(
{"messages": [("human", "解释量子计算")]},
config=config
):
print(chunk)
LangGraph Agent的优势:
- 内置状态管理
- 支持Human-in-the-loop
- 时间旅行(回溯到任意状态)
- 更细粒度的控制流
6.6 高级Agent模式
6.6.1 Multi-Agent协作
多个专业化Agent协同工作:
# 研究员Agent
researcher = create_react_agent(
model=ChatOpenAI(),
tools=[search_tool, wikipedia_tool]
)
# 作家Agent
writer = create_react_agent(
model=ChatOpenAI(),
tools=[grammar_check_tool]
)
# 协调器
workflow = StateGraph(...)
workflow.add_node("research", researcher)
workflow.add_node("write", writer)
workflow.add_edge("research", "write")
6.6.2 Plan-and-Execute Agent
先规划整体策略,再逐步执行:
from langchain.agents import Plan, PlanExecutor
plan_agent = Plan(
planner=planner_llm,
executor=executor_agent,
tools=tools
)
# 自动分解复杂任务
response = plan_agent.run("写一份关于AI伦理的10页报告")
6.7 Agent的调试与优化
6.7.1 使用LangSmith追踪
LangSmith自动记录Agent的每个决策步骤:
import os
os.environ["LANGSMITH_TRACING"] = "true"
os.environ["LANGSMITH_PROJECT"] = "my-agent-project"
# 所有执行自动上传到LangSmith
agent_executor.invoke({"input": "..."})
在LangSmith界面可以看到:
- 每次工具调用的输入输出
- LLM的推理过程
- 执行时间和token消耗
- 错误堆栈
6.7.2 常见问题与解决
问题1:Agent陷入循环
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=10, # 限制最大迭代次数
early_stopping_method="force" # 超时强制结束
)
问题2:工具选择不准确
- 改进工具描述,添加具体例子
- 使用Few-shot示例
- 考虑使用function calling而非文本解析
问题3:性能问题
# 使用缓存减少重复调用
from langchain.cache import InMemoryCache
set_llm_cache(InMemoryCache())
# 并行执行独立工具
from langchain.agents import ParallelExecutor
七、Callbacks:可观测性的基石
7.1 Callbacks系统概述
Callbacks是LangChain的事件驱动系统,允许在应用执行的各个阶段插入自定义逻辑。它是实现日志记录、监控、流式输出和调试的关键机制。
7.2 Callback事件类型
Callbacks定义了一系列标准事件,涵盖LLM调用、Chain执行、工具使用等全生命周期:
LLM相关事件:
on_llm_start: LLM调用开始on_llm_new_token: 生成新token(流式输出)on_llm_end: LLM调用结束on_llm_error: LLM调用出错
Chain相关事件:
on_chain_start: Chain执行开始on_chain_end: Chain执行结束on_chain_error: Chain执行出错
Tool相关事件:
on_tool_start: 工具调用开始on_tool_end: 工具调用结束on_tool_error: 工具调用出错
Agent相关事件:
on_agent_action: Agent决定执行某个动作on_agent_finish: Agent完成任务
7.3 实现自定义Callback Handler
7.3.1 基本实现
from langchain.callbacks.base import BaseCallbackHandler
from typing import Any, Dict, List
class MyCustomHandler(BaseCallbackHandler):
"""自定义回调处理器"""
def on_llm_start(
self,
serialized: Dict[str, Any],
prompts: List[str],
**kwargs: Any
) -> None:
"""LLM开始调用"""
print(f"[LLM START] 提示: {prompts[0][:100]}...")
self.start_time = time.time()
def on_llm_end(
self,
response: Any,
**kwargs: Any
) -> None:
"""LLM调用结束"""
elapsed = time.time() - self.start_time
print(f"[LLM END] 耗时: {elapsed:.2f}秒")
def on_llm_new_token(
self,
token: str,
**kwargs: Any
) -> None:
"""流式输出新token"""
print(token, end="", flush=True)
def on_chain_start(
self,
serialized: Dict[str, Any],
inputs: Dict[str, Any],
**kwargs: Any
) -> None:
"""Chain开始执行"""
print(f"[CHAIN START] 输入: {inputs}")
def on_tool_start(
self,
serialized: Dict[str, Any],
input_str: str,
**kwargs: Any
) -> None:
"""工具调用开始"""
tool_name = serialized.get("name", "unknown")
print(f"[TOOL START] {tool_name}: {input_str}")
7.3.2 使用Callback
Callbacks可以在两个层级使用:
请求级别(仅对当前调用生效):
# 传递给invoke/ainvoke
response = chain.invoke(
{"input": "question"},
config={"callbacks": [MyCustomHandler()]}
)
# 传递给AgentExecutor
agent_executor.invoke(
{"input": "question"},
callbacks=[MyCustomHandler()]
)
构造级别(对所有调用生效):
# 在创建组件时绑定
llm = ChatOpenAI(callbacks=[MyCustomHandler()])
chain = LLMChain(llm=llm, callbacks=[MyCustomHandler()])
7.4 内置Callback Handlers
7.4.1 StreamingStdOutCallbackHandler
实时输出LLM生成的内容:
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
llm = ChatOpenAI(
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()]
)
# 输出会实时打印到标准输出
llm.invoke("写一首诗")
7.4.2 FileCallbackHandler
将日志写入文件:
from langchain.callbacks.file import FileCallbackHandler
file_handler = FileCallbackHandler("langchain_logs.jsonl")
chain = LLMChain(llm=llm, callbacks=[file_handler])
7.4.3 LangChainTracer
集成LangSmith进行追踪:
from langchain.callbacks.tracers import LangChainTracer
tracer = LangChainTracer(project_name="my-project")
chain.invoke({"input": "..."}, callbacks=[tracer])
7.5 生产级Callback实现
7.5.1 集成OpenTelemetry
from opentelemetry import trace
from langchain.callbacks.base import BaseCallbackHandler
class OpenTelemetryHandler(BaseCallbackHandler):
"""集成OpenTelemetry的Callback"""
def __init__(self):
self.tracer = trace.get_tracer(__name__)
self.spans = {}
def on_chain_start(self, serialized, inputs, run_id, **kwargs):
span = self.tracer.start_span(
name=f"chain_{serialized.get('name')}",
attributes={
"chain.inputs": str(inputs),
"run.id": str(run_id)
}
)
self.spans[run_id] = span
def on_chain_end(self, outputs, run_id, **kwargs):
span = self.spans.pop(run_id, None)
if span:
span.set_attribute("chain.outputs", str(outputs))
span.end()
def on_llm_start(self, serialized, prompts, run_id, **kwargs):
span = self.tracer.start_span(
name="llm_call",
attributes={
"llm.prompts": prompts,
"llm.model": serialized.get("name")
}
)
self.spans[run_id] = span
7.5.2 集成Prometheus监控
from prometheus_client import Counter, Histogram
class PrometheusHandler(BaseCallbackHandler):
"""Prometheus指标收集"""
def __init__(self):
self.llm_calls = Counter(
'langchain_llm_calls_total',
'Total number of LLM calls',
['model', 'status']
)
self.llm_latency = Histogram(
'langchain_llm_latency_seconds',
'LLM call latency',
['model']
)
def on_llm_start(self, serialized, prompts, run_id, **kwargs):
self.start_times[run_id] = time.time()
def on_llm_end(self, response, run_id, **kwargs):
latency = time.time() - self.start_times.pop(run_id)
model = response.llm_output.get("model_name", "unknown")
self.llm_calls.labels(model=model, status="success").inc()
self.llm_latency.labels(model=model).observe(latency)
7.6 异步Callback
对于高并发场景,实现异步Callback避免阻塞:
from langchain.callbacks.base import AsyncCallbackHandler
class AsyncLoggingHandler(AsyncCallbackHandler):
"""异步日志记录"""
async def on_llm_start(self, serialized, prompts, **kwargs):
# 异步写入数据库
await self.db.log_llm_start(prompts)
async def on_llm_end(self, response, **kwargs):
await self.db.log_llm_end(response)
7.7 Callback的底层实现
7.7.1 CallbackManager
LangChain内部使用CallbackManager协调多个Callback:
# LangChain内部实现
class CallbackManager:
def __init__(self, handlers: List[BaseCallbackHandler]):
self.handlers = handlers
def on_llm_start(self, *args, **kwargs):
# 按顺序调用所有handler
for handler in self.handlers:
try:
handler.on_llm_start(*args, **kwargs)
except Exception as e:
logger.error(f"Callback error: {e}")
# 错误不应该中断主流程
7.7.2 配置传播
RunnableConfig自动在组件间传播Callback配置:
config = RunnableConfig(callbacks=[my_handler])
# config会自动传递给所有子组件
result = (prompt | model | parser).invoke(input, config=config)
7.8 Callbacks最佳实践
- 错误处理:Callback内部错误不应中断主流程
- 性能考虑:避免在Callback中执行耗时操作
- 异步优先:高并发场景使用AsyncCallbackHandler
- 结构化日志:使用JSON格式便于后续分析
- 采样策略:生产环境可能需要采样降低开销
class SamplingHandler(BaseCallbackHandler):
def __init__(self, sample_rate=0.1):
self.sample_rate = sample_rate
def on_llm_start(self, *args, **kwargs):
if random.random() < self.sample_rate:
# 只记录10%的调用
self._log(*args, **kwargs)
八、生态集成与扩展
8.1 集成生态总览
开发阶段:
LangChain (开发) → LangSmith (测试/评估) → LangServe (部署) → LangSmith (监控)
↓
LangGraph (复杂编排)
与第三方工具集成:
| 类别 | 集成工具 |
|---|---|
| 向量数据库 | Pinecone, Milvus, Weaviate, Chroma, FAISS, Qdrant |
| LLM 提供商 | OpenAI, Anthropic, Google, Cohere, Hugging Face, AWS Bedrock |
| 监控平台 | LangSmith, W&B, MLflow, Arize |
| 数据源 | Google Drive, Notion, Confluence, GitHub, Slack |
| 文档处理 | Unstructured, PyPDF, Docling |
8.2 模型提供商集成
LangChain支持超过100个模型提供商,主要分为几类:
8.1.1 商业API
# OpenAI
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
chat = ChatOpenAI(model="gpt-4")
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
# Anthropic Claude
from langchain_anthropic import ChatAnthropic
claude = ChatAnthropic(model="claude-3-opus-20240229")
# Google Gemini
from langchain_google_genai import ChatGoogleGenerativeAI
gemini = ChatGoogleGenerativeAI(model="gemini-pro")
# Cohere
from langchain_cohere import ChatCohere
cohere = ChatCohere(model="command-r-plus")
8.1.2 开源模型
# HuggingFace模型
from langchain_huggingface import HuggingFacePipeline
hf_model = HuggingFacePipeline.from_model_id(
model_id="meta-llama/Llama-2-7b-chat-hf",
task="text-generation",
device=0 # GPU设备号
)
# Ollama本地部署
from langchain_community.llms import Ollama
ollama = Ollama(model="llama2")
8.1.3 云平台集成
# AWS Bedrock
from langchain_aws import ChatBedrock
bedrock = ChatBedrock(
model_id="anthropic.claude-3-sonnet-20240229-v1:0",
region_name="us-east-1"
)
# Google Vertex AI
from langchain_google_vertexai import ChatVertexAI
vertex = ChatVertexAI(model_name="gemini-pro")
# Azure OpenAI
from langchain_openai import AzureChatOpenAI
azure = AzureChatOpenAI(
azure_deployment="gpt-4-deployment",
api_version="2024-02-15-preview"
)
8.3 向量数据库集成
8.3.1 开源方案
# Chroma
from langchain_community.vectorstores import Chroma
chroma = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
# Qdrant
from langchain_qdrant import QdrantVectorStore
qdrant = QdrantVectorStore.from_documents(
documents,
embeddings,
url="http://localhost:6333",
collection_name="my_collection"
)
# Weaviate
from langchain_weaviate import WeaviateVectorStore
weaviate = WeaviateVectorStore(
url="http://localhost:8080",
index_name="LangChain",
embedding=embeddings
)
8.3.2 云托管方案
# Pinecone
from langchain_pinecone import PineconeVectorStore
pinecone = PineconeVectorStore.from_documents(
documents,
embeddings,
index_name="langchain-index"
)
# Zilliz Cloud (Milvus托管版)
from langchain_milvus import Milvus
milvus = Milvus(
embedding_function=embeddings,
connection_args={
"uri": "https://xxx.zillizcloud.com",
"token": "your-token"
}
)
8.4 数据源集成
8.4.1 文档处理
# Unstructured (处理多种格式)
from langchain_community.document_loaders import UnstructuredFileLoader
loader = UnstructuredFileLoader(
"document.docx",
mode="elements" # 保留文档结构
)
# PDF高级处理
from langchain_community.document_loaders import PyMuPDFLoader
loader = PyMuPDFLoader("document.pdf")
docs = loader.load()
8.4.2 企业数据源
# Google Drive
from langchain_community.document_loaders import GoogleDriveLoader
gdrive_loader = GoogleDriveLoader(
folder_id="folder_id",
recursive=True
)
# Notion
from langchain_community.document_loaders import NotionDBLoader
notion_loader = NotionDBLoader(
integration_token="token",
database_id="database_id"
)
# Slack
from langchain_community.document_loaders import SlackDirectoryLoader
slack_loader = SlackDirectoryLoader("slack_export/")
8.5 框架互操作性
8.5.1 与LlamaIndex协作
from llama_index import VectorStoreIndex
from langchain_openai import OpenAI
# LlamaIndex构建索引
index = VectorStoreIndex.from_documents(documents)
# 转换为LangChain retriever
retriever = index.as_retriever()
# 在LangChain中使用
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
)
8.5.2 与Haystack集成
from haystack.document_stores import FAISSDocumentStore
from langchain.retrievers import HaystackRetriever
# Haystack文档存储
doc_store = FAISSDocumentStore(...)
# 包装为LangChain retriever
retriever = HaystackRetriever(document_store=doc_store)
8.6 LangSmith:可观测性与评估平台
LangSmith 是 LangChain 的统一可观测性和评估平台,无论是否使用 LangChain 框架构建。
核心功能:
- 追踪(Tracing)
- 查看每一步的输入输出
- Token 使用统计
- 延迟分析
- 错误追踪
- 评估(Evaluation)
- LLM-as-Judge 评估器
- 人工反馈收集
- 数据集管理
- A/B 测试
- 提示词管理(Prompt Hub)
- 版本控制
- 团队协作
- Playground 测试
- 提示词优化建议
- 监控(Monitoring)
- 实时仪表板
- 成本追踪
- 质量指标
- 告警系统
使用示例:
import os
# 启用追踪
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls__..."
os.environ["LANGCHAIN_PROJECT"] = "production-app"
# 执行会自动记录到 LangSmith
result = chain.invoke(input)
评估示例:
from langsmith import Client
from langsmith.evaluation import evaluate
client = Client()
# 定义评估器
def relevance_evaluator(run, example):
# 评估响应相关性
score = llm_judge.invoke({
"question": example.inputs["question"],
"answer": run.outputs["answer"]
})
return {"score": score}
# 运行评估
results = evaluate(
lambda x: chain.invoke(x),
data=client.list_examples(dataset_name="qa-test-set"),
evaluators=[relevance_evaluator]
)
8.7 LangServe:API 部署
LangServe 将 LangChain Runnable 部署为 REST API。
特性:
- 基于 FastAPI
- 自动生成 OpenAPI 文档
- 支持流式响应
- 内置 Playground UI
- LangSmith 集成
部署示例:
from langserve import add_routes
from fastapi import FastAPI
app = FastAPI(
title="LangChain Server",
version="1.0",
description="A simple API server using LangChain"
)
# 添加路由
add_routes(
app,
chain,
path="/chat",
enable_feedback_endpoint=True, # 反馈端点
enable_public_trace_link_endpoint=True # 公开追踪链接
)
# 运行:uvicorn main:app --reload
客户端调用:
from langserve import RemoteRunnable
# 连接远程 Runnable
remote_chain = RemoteRunnable("http://localhost:8000/chat/")
# 调用
result = remote_chain.invoke({"input": "Hello"})
# 流式调用
for chunk in remote_chain.stream({"input": "Hello"}):
print(chunk, end="")
8.8 LangGraph:高级编排框架
LangGraph 是 LangChain 的下一代编排框架,专为复杂的、有状态的应用设计。
核心概念:
- 状态图(StateGraph):应用定义为状态机
- 节点(Nodes):执行特定逻辑的函数
- 边(Edges):定义节点间的转换
- 持久化(Checkpointer):自动保存状态
示例:多步骤工作流:
from langgraph.graph import StateGraph, END
from typing import TypedDict, Annotated, Sequence
from langchain_core.messages import BaseMessage
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], "The messages"]
next_step: str
# 定义节点
def research_node(state):
# 研究节点
result = researcher.invoke(state["messages"])
return {
"messages": state["messages"] + [result],
"next_step": "analyze"
}
def analyze_node(state):
# 分析节点
result = analyzer.invoke(state["messages"])
return {
"messages": state["messages"] + [result],
"next_step": "report"
}
def report_node(state):
# 报告节点
result = reporter.invoke(state["messages"])
return {"messages": state["messages"] + [result]}
# 构建图
workflow = StateGraph(AgentState)
workflow.add_node("research", research_node)
workflow.add_node("analyze", analyze_node)
workflow.add_node("report", report_node)
workflow.add_edge("research", "analyze")
workflow.add_edge("analyze", "report")
workflow.add_edge("report", END)
workflow.set_entry_point("research")
# 编译
app = workflow.compile(checkpointer=MemorySaver())
# 执行
config = {"configurable": {"thread_id": "task-1"}}
result = app.invoke(
{"messages": [HumanMessage(content="研究量子计算")]},
config
)
LangGraph Studio:
- 可视化调试
- 实时状态查看
- 交互式执行
- 时间旅行(回溯到任意状态)
8.9 完整的RAG应用架构
结合所有组件构建生产级RAG系统:
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitters import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import DirectoryLoader
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
# 1. 加载文档
loader = DirectoryLoader("./docs", glob="**/*.md")
docs = loader.load()
# 2. 切分
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
splits = text_splitter.split_documents(docs)
# 3. 向量化并存储
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(
documents=splits,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 4. 创建检索器
retriever = vectorstore.as_retriever(
search_type="mmr",
search_kwargs={"k": 6, "fetch_k": 20}
)
# 5. 定义提示模板
template = """基于以下上下文回答问题。如果无法从上下文中找到答案,请说"我不知道"。
上下文:
{context}
问题:{question}
答案:"""
prompt = ChatPromptTemplate.from_template(template)
# 6. 构建RAG链
model = ChatOpenAI(model="gpt-4", temperature=0)
rag_chain = (
{
"context": retriever | (lambda docs: "\n\n".join(d.page_content for d in docs)),
"question": RunnablePassthrough()
}
| prompt
| model
| StrOutputParser()
)
# 7. 使用
response = rag_chain.invoke("What is LangChain?")
九、性能优化与最佳实践
9.1 性能优化策略
9.1.1 批处理
# 批量处理多个请求
questions = ["question 1", "question 2", "question 3"]
results = chain.batch(questions)
# 控制并发数
results = chain.batch(
questions,
config={"max_concurrency": 5}
)
9.1.2 缓存机制
from langchain.cache import InMemoryCache, SQLiteCache
# 内存缓存
set_llm_cache(InMemoryCache())
# 持久化缓存
set_llm_cache(SQLiteCache(database_path=".langchain.db"))
# 自定义缓存key
from langchain.cache import RETURN_VAL_TYPE
def custom_cache_key(prompt: str, llm_string: str) -> str:
return hashlib.md5(f"{prompt}:{llm_string}".encode()).hexdigest()
9.1.3 流式处理
# 降低感知延迟
for chunk in chain.stream(input):
print(chunk, end="", flush=True)
# 异步流式
async for chunk in chain.astream(input):
await websocket.send(chunk)
9.2 成本优化
9.2.1 Token计数与管理
from langchain.callbacks import get_openai_callback
with get_openai_callback() as cb:
response = chain.invoke(input)
print(f"Total Tokens: {cb.total_tokens}")
print(f"Total Cost (USD): ${cb.total_cost}")
9.2.2 模型选择策略
# 简单任务用小模型
routing_chain = RunnableBranch(
(lambda x: len(x["input"]) < 50, cheap_model_chain),
(lambda x: "code" in x["input"], specialized_model_chain),
default_expensive_model_chain
)
9.3 错误处理与容错
from langchain.schema.output_parser import OutputParserException
try:
result = chain.invoke(input)
except OutputParserException as e:
# 解析失败,重试或使用默认值
result = fallback_handler(e)
except Exception as e:
# 记录错误,返回友好消息
logger.error(f"Chain execution failed: {e}")
result = "抱歉,处理请求时出现了问题。"
9.4 安全性考虑
9.4.1 Prompt注入防护
from langchain_experimental.guardrails import PromptGuard
guard = PromptGuard()
# 检查用户输入
is_safe, reason = guard.check(user_input)
if not is_safe:
return f"输入被拒绝:{reason}"
9.4.2 敏感信息过滤
from presidio_analyzer import AnalyzerEngine
from presidio_anonymizer import AnonymizerEngine
analyzer = AnalyzerEngine()
anonymizer = AnonymizerEngine()
# 检测并匿名化PII
results = analyzer.analyze(text=user_input, language='en')
anonymized = anonymizer.anonymize(text=user_input, analyzer_results=results)
十、总结与展望
10.1 核心模块回顾
本文深入探讨了LangChain框架的六大核心模块:
- Model I/O:通过Prompt Templates、统一的模型接口和Output Parsers,实现了与各种LLM的标准化交互
- Retrieval:完整的RAG流程,从Document Loaders、Text Splitters到Embeddings和Vector Stores,使LLM能够访问外部知识
- Chains:LCEL提供了声明式的组合语法,简化了复杂管道的构建,同时保持了高度的灵活性
- Memory:多种内存策略使LLM应用具备对话连续性,从简单的Buffer到智能的Summary
- Agents:赋予LLM自主决策能力,通过ReAct、Tool Calling等范式实现了真正的智能代理
- Callbacks:完善的事件系统确保了应用的可观测性,是生产部署的基础设施
10.2 技术趋势
LangChain生态正在几个方向上快速演进:
从LCEL到LangGraph:对于复杂的有状态应用,LangGraph正在成为新的标准。它提供了更强大的状态管理、条件分支和人机协作能力。
多模态支持:随着GPT-4V、Gemini等多模态模型的普及,LangChain正在扩展对图像、音频、视频等多模态数据的支持。
边缘部署:通过优化和量化,LangChain应用可以在边缘设备上运行,降低延迟和成本。
Agent自主性增强:未来的Agent将具备更强的规划、反思和自我修正能力,向通用人工智能(AGI)迈进。
10.3 最佳实践总结
基于生产实践,以下是关键建议:
- 优先使用LCEL:相比传统Chain,LCEL更灵活、可维护性更强
- 分离索引和检索:RAG应用中,索引是离线过程,检索是在线过程,应分别优化
- 重视可观测性:从一开始就集成LangSmith或其他追踪工具
- 渐进式复杂度:从简单的Chain开始,只在必要时引入Agent
- 充分测试:LLM输出的不确定性要求更全面的测试策略
参考文献
- LangChain官方文档 - 完整的API参考和概念指南
python.langchain.com/docs/introd… - LangChain GitHub仓库 - 开源代码和社区贡献
github.com/langchain-a… - LangChain Expression Language (LCEL)详解 - 声明式组合语法介绍
python.langchain.com/docs/concep… - LangGraph官方文档 - 状态管理和Agent编排
langchain-ai.github.io/langgraph/ - LangSmith平台 - 可观测性和评估工具
www.langchain.com/langsmith - Retrieval Augmented Generation教程 - RAG完整实现指南
python.langchain.com/docs/tutori… - Agent开发指南 - 构建智能代理的最佳实践
python.langchain.com/docs/tutori… - ReAct论文 - Reasoning and Acting的原始研究
Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models" (2022)
arxiv.org/abs/2210.03… - Pinecone LangChain集成 - 向量数据库实践
www.pinecone.io/learn/serie… - Anthropic Claude集成文档 - Claude模型在LangChain中的使用
python.langchain.com/docs/integr… - OpenAI Function Calling指南 - 工具调用的原生支持
platform.openai.com/docs/guides… - Neo4j与LangChain集成 - 图数据库在RAG中的应用
neo4j.com/labs/genai-… - Weaviate向量数据库教程 - 企业级向量搜索方案
weaviate.io/developers/… - LangChain Wikipedia - 框架历史和发展里程碑
en.wikipedia.org/wiki/LangCh… - Langfuse追踪集成 - 开源可观测性方案
langfuse.com/docs/integr… - HuggingFace Embeddings - 开源Embedding模型
huggingface.co/blog/gettin… - Chroma向量数据库 - 轻量级向量存储方案
docs.trychroma.com/ - LangChain Conversational Memory - 对话记忆最佳实践
www.pinecone.io/learn/serie… - Prompt Engineering指南 - 提示工程技巧
docs.claude.com/en/docs/bui… - LangChain安全性考虑 - 生产环境安全指南
python.langchain.com/docs/securi…