LangChain框架完整指南:架构、生态与实践

417 阅读39分钟

本文撰写于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差异带来的集成难题。该模块包含三个关键组件:

  1. Prompt Templates:提示模板管理与格式化
  2. Models:语言模型的标准化接口
  3. 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 ModelsBaseChatModel消息列表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虽然强大,但面临三个根本性限制:

  1. 知识截止日期:模型只了解训练数据中的信息,无法获取最新知识
  2. 私有数据盲区:无法访问企业内部文档、数据库等专有信息
  3. 上下文窗口限制:即使最先进的模型,上下文窗口也是有限的(通常几万到几十万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模型对比

提供商模型维度优势
OpenAItext-embedding-3-large3072高质量、多语言支持
Cohereembed-english-v3.01024检索优化
Googletext-embedding-004768多模态支持
HuggingFacebge-large-zh-v1.51024中文优化、开源
Voyage AIvoyage-21024定制化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是一种声明式的组合语法,采用管道操作符(|)连接组件。它的核心优势:

  1. 声明式编程:描述"做什么"而非"怎么做",让LangChain负责执行优化
  2. 自动化能力:自动支持流式、批处理、异步等多种调用模式
  3. 无缝追踪:与LangSmith深度集成,自动记录每个步骤
  4. 生产部署:通过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系统包含:

  1. Agent:决策引擎,根据当前状态选择下一步行动
  2. Tools:Agent可以调用的函数集合
  3. AgentExecutor:执行循环的协调器
  4. 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 AgentTool 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最佳实践

  1. 错误处理:Callback内部错误不应中断主流程
  2. 性能考虑:避免在Callback中执行耗时操作
  3. 异步优先:高并发场景使用AsyncCallbackHandler
  4. 结构化日志:使用JSON格式便于后续分析
  5. 采样策略:生产环境可能需要采样降低开销
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 框架构建。

核心功能

  1. 追踪(Tracing)
    • 查看每一步的输入输出
    • Token 使用统计
    • 延迟分析
    • 错误追踪
  2. 评估(Evaluation)
    • LLM-as-Judge 评估器
    • 人工反馈收集
    • 数据集管理
    • A/B 测试
  3. 提示词管理(Prompt Hub)
    • 版本控制
    • 团队协作
    • Playground 测试
    • 提示词优化建议
  4. 监控(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框架的六大核心模块:

  1. Model I/O:通过Prompt Templates、统一的模型接口和Output Parsers,实现了与各种LLM的标准化交互
  2. Retrieval:完整的RAG流程,从Document Loaders、Text Splitters到Embeddings和Vector Stores,使LLM能够访问外部知识
  3. Chains:LCEL提供了声明式的组合语法,简化了复杂管道的构建,同时保持了高度的灵活性
  4. Memory:多种内存策略使LLM应用具备对话连续性,从简单的Buffer到智能的Summary
  5. Agents:赋予LLM自主决策能力,通过ReAct、Tool Calling等范式实现了真正的智能代理
  6. Callbacks:完善的事件系统确保了应用的可观测性,是生产部署的基础设施

10.2 技术趋势

LangChain生态正在几个方向上快速演进:

从LCEL到LangGraph:对于复杂的有状态应用,LangGraph正在成为新的标准。它提供了更强大的状态管理、条件分支和人机协作能力。

多模态支持:随着GPT-4V、Gemini等多模态模型的普及,LangChain正在扩展对图像、音频、视频等多模态数据的支持。

边缘部署:通过优化和量化,LangChain应用可以在边缘设备上运行,降低延迟和成本。

Agent自主性增强:未来的Agent将具备更强的规划、反思和自我修正能力,向通用人工智能(AGI)迈进。

10.3 最佳实践总结

基于生产实践,以下是关键建议:

  1. 优先使用LCEL:相比传统Chain,LCEL更灵活、可维护性更强
  2. 分离索引和检索:RAG应用中,索引是离线过程,检索是在线过程,应分别优化
  3. 重视可观测性:从一开始就集成LangSmith或其他追踪工具
  4. 渐进式复杂度:从简单的Chain开始,只在必要时引入Agent
  5. 充分测试:LLM输出的不确定性要求更全面的测试策略

参考文献