系列:深入 LangChain ------ 从核心原理到生产实践
学习目标
- 理解
langchain-classic(libs/langchain/)与新版langchain(libs/langchain_v1/)的本质区别 - 掌握 Classic 中 Chain、Agent、Memory 三大核心模式的设计思想与局限性
- 理解新版以
create_agent()+init_chat_model()+ Middleware 为核心的精简架构 - 建立从 Classic 到新版的完整迁移映射
- 深入分析 Classic
__init__.py兼容层的延迟导入与废弃警告机制
3.1 两个时代:为什么会有两个 LangChain?
打开 LangChain 的 monorepo,你会发现一个有趣的现象:在 libs/ 目录下同时存在 langchain/(Classic)和 langchain_v1/(新版)两个独立的包。这并非代码组织的混乱,而是 LangChain 架构演进的自然产物。
libs/
├── langchain/ # langchain-classic v1.0.3 ------ 遗留版本
│ └── langchain_classic/ # 40+ 子模块,Chain/Agent/Memory 为核心
├── langchain_v1/ # langchain v1.2.15 ------ 活跃开发版本
│ └── langchain/ # 7 个精简模块,LangGraph 为编排核心
└── core/ # langchain-core v1.2.27 ------ 共享基础层
└── langchain_core/ # 两个版本共同依赖的基础抽象
3.1.1 包命名与版本对照
| 维度 | Classic(langchain-classic) |
新版(langchain) |
|---|---|---|
| PyPI 包名 | langchain-classic |
langchain |
| 版本 | v1.0.3(稳定维护) | v1.2.15(活跃开发) |
| 源码位置 | libs/langchain/langchain_classic/ |
libs/langchain_v1/langchain/ |
| Python 模块名 | langchain_classic |
langchain |
| 状态 | 仅修复关键 Bug,不再添加新功能 | 主力版本,持续迭代 |
理解这两个版本的关系是深入 LangChain 生态的关键。Classic 代表了 LangChain 的"第一代架构思想"------功能全包、Chain 为核心;新版代表了"第二代架构思想"------精简入口、图为核心。
3.1.2 依赖关系的根本差异
两个版本的依赖声明清晰地反映了架构理念的转变:
Classic 的 pyproject.toml (来源:libs/langchain/pyproject.toml):
toml
dependencies = [
"langchain-core>=1.2.19,<2.0.0",
"langchain-text-splitters>=1.1.1,<2.0.0",
"langsmith>=0.1.17,<1.0.0",
"pydantic>=2.7.4,<3.0.0",
"SQLAlchemy>=1.4.0,<3.0.0", # ORM 框架 ------ 用于数据库交互
"requests>=2.0.0,<3.0.0", # HTTP 客户端 ------ 用于 API 调用
"PyYAML>=5.3.0,<7.0.0", # YAML 解析 ------ 用于配置和序列化
"async-timeout>=4.0.0,<5.0.0; python_version < '3.11'",
]
新版的 pyproject.toml (来源:libs/langchain_v1/pyproject.toml):
toml
dependencies = [
"langchain-core>=1.2.10,<2.0.0",
"langgraph>=1.1.5,<1.2.0", # 图编排引擎 ------ 核心运行时
"pydantic>=2.7.4,<3.0.0",
]
对比非常鲜明:
- Classic 携带 7 个核心依赖(SQLAlchemy、requests、PyYAML 等),因为它试图在一个包内覆盖数据库交互、HTTP 请求、配置管理等全部能力
- 新版仅 3 个核心依赖,将编排能力完全委托给
langgraph,其余功能下沉到 Partner 包
3.1.3 模块规模的量级差异
langchain_classic/ langchain/(新版)
├── agents/ (164 导出) ├── agents/ (2 导出: create_agent, AgentState)
├── chains/ (87 导出) ├── chat_models/ (2 导出: BaseChatModel, init_chat_model)
├── memory/ (26 导出) ├── embeddings/ (2 导出: Embeddings, init_embeddings)
├── document_loaders/ (171 导出) ├── messages/ (消息类型重导出)
├── callbacks/ ├── tools/ (工具类型重导出)
├── text_splitter/ └── rate_limiters/(2 导出)
├── embeddings/
├── llms/
├── output_parsers/
├── retrievers/
├── vectorstores/
├── utilities/
└── ... (40+ 子模块) 共计 7 个模块
Classic 有 40+ 个子模块,导出数百个类和函数;新版只有 7 个模块,总共不到 20 个公开 API。这不是"功能缺失",而是设计哲学的根本转变 ------新版将自己定位为"精简入口层",把具体的功能实现分别交给 langchain-core(基础抽象)、langgraph(编排运行时)和 Partner 包(模型集成)。
3.2 Classic 核心模式深度解析
要理解为什么需要迁移,首先需要理解 Classic 的核心设计模式------Chain、Agent、Memory 三位一体的架构。
3.2.1 Chain 模式:线性组合的黄金年代
Chain 是 Classic 中最核心的抽象。它的设计思想很直观:将一系列操作串联起来,形成一条"链"。
Chain 基类 (来源:base.py):
python
class Chain(RunnableSerializable[dict[str, Any], dict[str, Any]], ABC):
"""Abstract base class for creating structured sequences of calls to components."""
memory: BaseMemory | None = None
"""Optional memory object."""
callbacks: Callbacks = Field(default=None, exclude=True)
verbose: bool = Field(default_factory=_get_verbosity)
tags: list[str] | None = None
metadata: dict[str, Any] | None = None
@property
@abstractmethod
def input_keys(self) -> list[str]:
"""输入键列表"""
@property
@abstractmethod
def output_keys(self) -> list[str]:
"""输出键列表"""
@abstractmethod
def _call(
self,
inputs: dict[str, Any],
run_manager: CallbackManagerForChainRun | None = None,
) -> dict[str, str]:
"""执行链的核心逻辑"""
Chain 基类的设计有几个值得注意的特征:
- 继承
RunnableSerializable:虽然 Chain 是 Classic 的概念,但它已经桥接到了 Runnable 协议 - 字典驱动的 I/O :输入和输出都是
dict[str, Any],通过input_keys和output_keys声明接口 - 内置 Memory 支持 :
memory字段直接嵌入基类,Chain 执行前后自动加载/保存记忆 - 回调系统集成:每次执行都会触发回调生命周期
LLMChain------最经典的 Chain 实现 (来源:llm.py):
python
@deprecated(
since="0.1.17",
alternative="RunnableSequence, e.g., `prompt | llm`",
removal="1.0",
)
class LLMChain(Chain):
"""Chain to run queries against LLMs."""
prompt: BasePromptTemplate
llm: Runnable[LanguageModelInput, str] | Runnable[LanguageModelInput, BaseMessage]
output_key: str = "text"
output_parser: BaseLLMOutputParser = Field(default_factory=StrOutputParser)
return_final_only: bool = True
llm_kwargs: dict = Field(default_factory=dict)
@property
def input_keys(self) -> list[str]:
return self.prompt.input_variables
LLMChain 的核心工作流程是 Prompt → LLM → OutputParser------这正是后来 LCEL 管道 prompt | llm | parser 的前身。
Classic 的 Chain 生态------87 种 Chain 类型
Classic 的 chains/__init__.py 通过 _module_lookup 字典映射了 87 个 Chain 类型,涵盖了几乎所有常见的 LLM 应用模式:
| 类别 | 代表 Chain | 数量 | 说明 |
|---|---|---|---|
| 文档组合 | StuffDocumentsChain、MapReduceDocumentsChain、RefineDocumentsChain |
6 | 处理长文档的四种策略 |
| 对话 | ConversationChain、ConversationalRetrievalChain |
2 | 带记忆的对话管理 |
| LLM 应用 | LLMChain、LLMCheckerChain、LLMMathChain |
4 | 基础 LLM 调用链 |
| 检索问答 | RetrievalQA、QAWithSourcesChain |
4 | RAG 模式实现 |
| 路由 | RouterChain、MultiPromptChain、MultiRetrievalQAChain |
4 | 动态路由分发 |
| 序列 | SequentialChain、SimpleSequentialChain、TransformChain |
3 | 链式串联 |
| 图数据库 | GraphQAChain、GraphCypherQAChain |
12+ | 图数据库查询(重定向到 community) |
| 其他 | ConstitutionalChain、HypotheticalDocumentEmbedder |
10+ | 特殊应用链 |
这种"为每个场景预制一个 Chain"的策略在早期很有效------开发者不需要理解底层原理,直接使用对应的 Chain 就能快速实现功能。但随着应用复杂度的提升,问题逐渐显现:
- 组合爆炸:需要的 Chain 类型越来越多,维护成本指数增长
- 灵活性不足:每个 Chain 的行为都是预设的,自定义需求需要继承重写
- 字典接口的脆弱性 :
input_keys/output_keys缺乏类型安全,运行时才能发现键名错误
3.2.2 Agent 模式:决策循环的第一代实现
Classic 中的 Agent 系统由三个核心组件构成:AgentType(类型枚举)、Agent(决策逻辑)、AgentExecutor(执行循环)。
AgentType 枚举------9 种预定义 Agent 类型 (来源:agent_types.py):
python
@deprecated(
"0.1.0",
message=AGENT_DEPRECATION_WARNING,
removal="1.0",
)
class AgentType(str, Enum):
"""An enum for agent types."""
ZERO_SHOT_REACT_DESCRIPTION = "zero-shot-react-description"
"""ReAct 模式:思考-操作-观察循环"""
REACT_DOCSTORE = "react-docstore"
"""文档库 ReAct:Lookup + Search 双工具"""
SELF_ASK_WITH_SEARCH = "self-ask-with-search"
"""自问自答:分解复杂问题为子问题"""
CONVERSATIONAL_REACT_DESCRIPTION = "conversational-react-description"
"""对话式 ReAct"""
CHAT_ZERO_SHOT_REACT_DESCRIPTION = "chat-zero-shot-react-description"
"""Chat 模型的零样本 ReAct"""
CHAT_CONVERSATIONAL_REACT_DESCRIPTION = "chat-conversational-react-description"
"""Chat 模型的对话式 ReAct"""
STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION = (
"structured-chat-zero-shot-react-description"
)
"""结构化 Chat:支持多输入工具"""
OPENAI_FUNCTIONS = "openai-functions"
"""OpenAI 函数调用:利用原生 function calling"""
OPENAI_MULTI_FUNCTIONS = "openai-multi-functions"
"""OpenAI 多函数调用"""
这 9 种类型涵盖了当时主流的 Agent 策略。但注意,整个枚举类都被标记为 @deprecated("0.1.0"),推荐迁移至 LangGraph。
AgentExecutor------Agent 执行引擎 (来源:agent.py):
python
class AgentExecutor(Chain):
"""Agent that is using tools."""
agent: BaseSingleActionAgent | BaseMultiActionAgent | Runnable
tools: Sequence[BaseTool]
return_intermediate_steps: bool = False
max_iterations: int | None = 15
max_execution_time: float | None = None
early_stopping_method: str = "force" # "force" 或 "generate"
handle_parsing_errors: bool | str | Callable = False
trim_intermediate_steps: int | Callable = -1
AgentExecutor 是 Classic Agent 系统的核心引擎。它的执行循环可以简化为:
┌─────────────────────────────────────────────────┐
│ AgentExecutor 执行循环 │
│ │
│ 1. Agent.plan(inputs) → AgentAction | AgentFinish │
│ │ │
│ ├── AgentFinish → 返回最终结果 │
│ │ │
│ └── AgentAction → 执行工具 │
│ │ │
│ ├── tool.run(action.tool_input) │
│ │ │
│ └── observation → 加入历史,回到步骤 1 │
│ │
│ 循环终止条件: │
│ - AgentFinish │
│ - 达到 max_iterations (15) │
│ - 超过 max_execution_time │
└─────────────────────────────────────────────────┘
AgentExecutor 的设计在当时是合理的,但也有明确的局限性:
- 单一循环模式:只支持"思考-行动-观察"这一种循环结构,无法实现条件分支、并行执行
- 状态管理原始 :中间步骤以
list[tuple[AgentAction, str]]存储,无法做细粒度的状态控制 - 错误恢复有限 :
handle_parsing_errors只处理输出解析错误,不支持工具执行失败的自动重试 - 缺乏持久化:循环状态仅在内存中,无法跨请求保持
Classic Agent 的使用模式
典型的 Classic Agent 创建流程:
python
# Classic 方式一:通过 initialize_agent 工厂函数
from langchain_classic.agents import initialize_agent, AgentType
from langchain_classic.tools import Tool
agent_executor = initialize_agent(
tools=[search_tool, calculator_tool],
llm=llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
max_iterations=10,
)
result = agent_executor.invoke({"input": "What is the weather in Beijing?"})
# Classic 方式二:手动组装
from langchain_classic.agents import ZeroShotAgent, AgentExecutor
prompt = ZeroShotAgent.create_prompt(tools=tools)
llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools)
executor = AgentExecutor(agent=agent, tools=tools)
Classic Agent 工具包------30+ Toolkits
Classic 还提供了大量预制的 Agent Toolkits,帮助开发者快速创建特定领域的 Agent:
| 类别 | 工具包 | 用途 |
|---|---|---|
| 数据分析 | SQL Toolkit、Pandas Toolkit | 数据库查询、数据框操作 |
| 开发工具 | GitHub Toolkit、GitLab Toolkit | 代码仓库操作 |
| 项目管理 | Jira Toolkit、Slack Toolkit | 工作流集成 |
| 通讯 | Gmail Toolkit | 邮件处理 |
| API | OpenAPI Toolkit | 通用 API 交互 |
| 文件 | File Management Toolkit | 文件操作 |
| 搜索 | VectorStore Toolkit | 向量搜索 |
部分工具包后来被迁移到 langchain_experimental(如 create_csv_agent、create_pandas_dataframe_agent),部分迁移到 langchain_community(如 create_sql_agent)。
3.2.3 Memory 模式:对话状态管理的探索
Classic 的 Memory 系统是一个独立的对话记忆管理框架,直接嵌入在 Chain 基类中。
BaseMemory 抽象:
python
@deprecated(
since="0.3.3",
removal="1.0.0",
message="Please see the migration guide at: ..."
)
class BaseMemory(Serializable, ABC):
"""Memory 抽象基类"""
@property
@abstractmethod
def memory_variables(self) -> list[str]:
"""返回内存提供的变量键"""
@abstractmethod
def load_memory_variables(self, inputs: dict[str, Any]) -> dict[str, Any]:
"""从内存加载变量"""
@abstractmethod
def save_context(self, inputs: dict[str, Any], outputs: dict[str, str]) -> None:
"""保存执行上下文到内存"""
@abstractmethod
def clear(self) -> None:
"""清空内存"""
Classic 提供了 26 种 Memory 实现,涵盖多种记忆策略:
| 策略 | 实现类 | 原理 |
|---|---|---|
| 全量缓冲 | ConversationBufferMemory |
存储完整对话历史 |
| 滑动窗口 | ConversationBufferWindowMemory |
只保留最近 k 轮对话 |
| 自动总结 | ConversationSummaryMemory |
用 LLM 总结旧对话 |
| 混合策略 | ConversationSummaryBufferMemory |
近期全量 + 远期总结 |
| Token 计数 | ConversationTokenBufferMemory |
基于 Token 数量截断 |
| 实体追踪 | ConversationEntityMemory |
维护实体信息 |
| 向量检索 | VectorStoreRetrieverMemory |
通过语义相似度检索相关记忆 |
| 知识图谱 | ConversationKGMemory |
构建和查询知识图谱 |
Memory 系统的设计理念很超前,但在实践中暴露了几个问题:
- 与 Chain 的紧耦合:Memory 嵌入 Chain 基类,在非 Chain 场景(如 LangGraph)中难以复用
- 同步/异步割裂 :
load_memory_variables和aload_memory_variables需要分别实现 - 状态管理局限:只能在 Chain 执行前后加载/保存,不支持中间步骤的细粒度状态管理
3.2.4 Document Loaders------171+ 种加载器
Classic 集成了 171+ 种文档加载器,覆盖了几乎所有数据源。但这些加载器全部通过 __getattr__ 动态重定向到 langchain_community:
python
# libs/langchain/langchain_classic/document_loaders/__init__.py
# 所有 loader 都从 langchain_community 动态导入
# 包括:WebBaseLoader, PyPDFLoader, CSVLoader, S3FileLoader,
# GoogleDriveLoader, TelegramChatLoader, ArxivLoader 等 171+ 种
这反映了 Classic 时代"大而全"的设计思路------一个包打天下。
3.3 新版架构:精简、专注、可组合
新版 LangChain(v1.2.15)的 __init__.py 只有一行有效代码:
python
"""Main entrypoint into LangChain."""
__version__ = "1.2.15"
对比 Classic 那长达数百行的 __getattr__ 兼容层,新版的极简入口鲜明地体现了"精简入口"的设计理念。新版的全部公开 API 集中在 7 个模块中,核心能力可以用三个关键函数概括:
3.3.1 create_agent() ------ 一个函数取代整个 Agent 体系
新版用一个工厂函数 create_agent() 取代了 Classic 中 AgentType(9 种枚举)+ Agent(多种子类)+ AgentExecutor(执行引擎)+ initialize_agent()(工厂函数)的整套体系。
函数签名 (来源:factory.py):
python
def create_agent(
model: str | BaseChatModel,
tools: Sequence[BaseTool | Callable[..., Any] | dict[str, Any]] | None = None,
*,
system_prompt: str | SystemMessage | None = None,
middleware: Sequence[AgentMiddleware[StateT_co, ContextT]] = (),
response_format: ResponseFormat[ResponseT] | type[ResponseT] | dict[str, Any] | None = None,
state_schema: type[AgentState[ResponseT]] | None = None,
context_schema: type[ContextT] | None = None,
checkpointer: Checkpointer | None = None,
store: BaseStore | None = None,
interrupt_before: list[str] | None = None,
interrupt_after: list[str] | None = None,
debug: bool = False,
name: str | None = None,
cache: BaseCache[Any] | None = None,
) -> CompiledStateGraph[
AgentState[ResponseT], ContextT, _InputAgentState, _OutputAgentState[ResponseT]
]:
关键设计决策:
-
返回值是 LangGraph
CompiledStateGraph:Agent 不再是一个独立的"执行引擎",而是一个编译后的状态图。这意味着你可以直接使用 LangGraph 的全部能力------中断、持久化、子图嵌套、流式执行。 -
model支持字符串格式 :"openai:gpt-4o"内部会通过init_chat_model()自动解析为对应的 ChatModel 实例,省去了手动导入和实例化的步骤。 -
middleware替代了硬编码的 Agent 行为:Classic 中需要通过继承 Agent 子类来自定义行为,新版通过中间件组合实现,更加灵活。 -
response_format内置结构化输出 :支持 Pydantic 模型、ToolStrategy、ProviderStrategy三种策略,无需额外包装。 -
原生持久化支持 :
checkpointer和store参数直接集成 LangGraph 的持久化能力,替代了 Classic 中笨重的 Memory 系统。
新版 Agent 的使用方式:
python
from langchain.agents import create_agent
from langchain.chat_models import init_chat_model
# 最简用法------3 行代码创建一个 Agent
model = init_chat_model("openai:gpt-4o")
agent = create_agent(model, tools=[search_tool, calculator_tool])
result = agent.invoke({"messages": [("human", "What is the weather?")]})
# 带中间件、持久化和结构化输出
from langchain.agents.middleware import (
ModelFallbackMiddleware,
SummarizationMiddleware,
HumanInTheLoopMiddleware,
)
from pydantic import BaseModel
class WeatherReport(BaseModel):
city: str
temperature: float
description: str
agent = create_agent(
"anthropic:claude-sonnet-4-20250514",
tools=[weather_tool],
system_prompt="You are a weather assistant.",
middleware=[
ModelFallbackMiddleware(fallback_models=["openai:gpt-4o"]),
SummarizationMiddleware(),
],
response_format=WeatherReport,
checkpointer=memory_saver,
)
3.3.2 init_chat_model() ------ 统一的模型初始化入口
新版的另一个核心函数是 init_chat_model(),它统一了所有 LLM 提供商的初始化方式。
内置提供商注册表 (来源:base.py):
python
_BUILTIN_PROVIDERS: dict[str, tuple[str, str, Callable[..., BaseChatModel]]] = {
"openai": ("langchain_openai", "ChatOpenAI", _call),
"anthropic": ("langchain_anthropic", "ChatAnthropic", _call),
"deepseek": ("langchain_deepseek", "ChatDeepSeek", _call),
"google_vertexai": ("langchain_google_vertexai", "ChatVertexAI", _call),
"google_genai": ("langchain_google_genai", "ChatGoogleGenerativeAI", _call),
"cohere": ("langchain_cohere", "ChatCohere", _call),
"fireworks": ("langchain_fireworks", "ChatFireworks", _call),
"together": ("langchain_together", "ChatTogether", _call),
"mistralai": ("langchain_mistralai", "ChatMistralAI", _call),
"huggingface": ("langchain_huggingface", "ChatHuggingFace", _call),
"groq": ("langchain_groq", "ChatGroq", _call),
"ollama": ("langchain_ollama", "ChatOllama", _call),
"xai": ("langchain_xai", "ChatXAI", _call),
"perplexity": ("langchain_perplexity", "ChatPerplexity", _call),
# ... 共 30+ 个提供商
}
使用方式对比:
python
# ---- Classic 方式:每个提供商需要单独导入 ----
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
model_openai = ChatOpenAI(model="gpt-4o", temperature=0.7)
model_anthropic = ChatAnthropic(model="claude-sonnet-4-20250514")
# ---- 新版方式:统一入口 ----
from langchain.chat_models import init_chat_model
model_openai = init_chat_model("openai:gpt-4o", temperature=0.7)
model_anthropic = init_chat_model("anthropic:claude-sonnet-4-20250514")
# 还支持自动推断提供商
model = init_chat_model("gpt-4o") # 自动识别为 openai
model = init_chat_model("claude-sonnet-4-20250514") # 自动识别为 anthropic
# 可配置模式------运行时动态切换
configurable_model = init_chat_model(
configurable_fields=["model", "model_provider", "temperature"],
config_prefix="main_",
)
# 在调用时指定具体模型
result = configurable_model.invoke(
messages,
config={"configurable": {"main_model": "gpt-4o", "main_model_provider": "openai"}}
)
init_chat_model() 的核心优势在于:
- 延迟导入:只在调用时加载对应的 Partner 包,避免不必要的依赖
- 字符串格式解析 :
"provider:model-id"格式简洁直观 _ConfigurableModel包装器 :通过RunnableConfig实现运行时模型切换
3.3.3 Middleware 系统------可组合的 Agent 行为扩展
新版引入了完整的 Middleware 系统,这是 Classic 中完全不存在的概念。Middleware 允许开发者在 Agent 执行循环的各个阶段插入自定义逻辑,无需修改核心代码。
AgentMiddleware 基类 (来源:types.py):
python
class AgentMiddleware(Generic[StateT, ContextT, ResponseT]):
"""Base middleware class for an agent."""
state_schema: type[StateT] = _DefaultAgentState
tools: Sequence[BaseTool]
@property
def name(self) -> str:
return self.__class__.__name__
# 生命周期钩子(每个都有同步 + 异步版本)
def before_agent(self, state, runtime) -> dict[str, Any] | None: ...
def before_model(self, state, runtime) -> dict[str, Any] | None: ...
def after_model(self, state, runtime) -> dict[str, Any] | None: ...
def after_agent(self, state, runtime) -> dict[str, Any] | None: ...
# 拦截钩子
def wrap_model_call(self, request, handler) -> ModelResponse | AIMessage: ...
def wrap_tool_call(self, request, handler) -> ToolMessage | Command: ...
Middleware 生命周期:
START
↓
before_agent ← 全局初始化
↓
┌─────────────────────── Agent Loop ───────────────────────┐
│ before_model ← 模型调用前(可修改消息/工具/提示词) │
│ ↓ │
│ wrap_model_call ← 拦截模型调用(重试/降级/缓存) │
│ ↓ │
│ [模型执行] │
│ ↓ │
│ after_model ← 模型调用后(可修改结果) │
│ ↓ │
│ wrap_tool_call ← 拦截工具调用(验证/mock/限流) │
│ ↓ │
│ [工具执行] │
│ ↓ │
│ (重复直到模型不再调用工具) │
└──────────────────────────────────────────────────────────┘
↓
after_agent ← 全局清理
↓
END
17 个内置 Middleware
新版在 langchain/agents/middleware/ 目录下提供了 17 个内置中间件:
| 类别 | 中间件 | 功能 |
|---|---|---|
| 模型管理 | ModelRetryMiddleware |
指数退避重试 |
ModelFallbackMiddleware |
模型降级链 | |
ModelCallLimitMiddleware |
调用次数限制 | |
| 工具管理 | ToolRetryMiddleware |
工具调用重试 |
ToolCallLimitMiddleware |
工具调用限制 | |
LLMToolEmulator |
LLM 模拟工具执行 | |
LLMToolSelectorMiddleware |
LLM 驱动的工具选择 | |
| 人机交互 | HumanInTheLoopMiddleware |
人类审批流 |
| 数据处理 | SummarizationMiddleware |
对话历史总结 |
PIIMiddleware |
PII 检测与脱敏 | |
ContextEditingMiddleware |
上下文编辑 | |
| 文件/系统 | FilesystemFileSearchMiddleware |
文件搜索 |
ShellToolMiddleware |
Shell 命令执行 | |
TodoListMiddleware |
TODO 管理 |
Middleware 的组合是嵌套式的 :第一个中间件是最外层,wrap_model_call 的处理器链通过 _chain_model_call_handlers() 组合,形成类似洋葱模型的执行顺序。
自定义 Middleware 示例:
python
from langchain.agents.middleware.types import AgentMiddleware, ModelRequest, ModelResponse
class LoggingMiddleware(AgentMiddleware):
"""记录每次模型调用的输入输出"""
def wrap_model_call(self, request, handler):
print(f"[LOG] Model input: {len(request.messages)} messages")
response = handler(request)
print(f"[LOG] Model output: {response.result[0].content[:100]}")
return response
# 使用装饰器工厂------更简洁的写法
from langchain.agents.middleware.types import wrap_model_call
@wrap_model_call(name="rate_limiter")
def rate_limit_middleware(request, handler):
import time
time.sleep(1) # 简单限速
return handler(request)
3.3.4 AgentState------类型化的状态管理
新版用 TypedDict 定义 Agent 状态,替代了 Classic 中原始的字典 I/O:
AgentState 定义 (来源:types.py):
python
class AgentState(TypedDict, Generic[ResponseT]):
"""State schema for the agent."""
messages: Required[Annotated[list[AnyMessage], add_messages]]
jump_to: NotRequired[Annotated[JumpTo | None, EphemeralValue, PrivateStateAttr]]
structured_response: NotRequired[Annotated[ResponseT, OmitFromInput]]
三个字段各有深意:
messages:消息列表,使用 LangGraph 的add_messagesreducer。这是 Agent 的"记忆",每次执行都会追加新消息而非覆盖。jump_to:流程控制字段,可以跳转到'tools'、'model'或'end'。通过EphemeralValue注解标记为临时值,不会被持久化。structured_response:结构化输出结果,支持泛型ResponseT,通过OmitFromInput注解从输入 schema 中排除。
中间件可以通过 state_schema 属性扩展基础状态:
python
class MyMiddlewareState(AgentState):
"""扩展状态,添加自定义字段"""
retry_count: NotRequired[int]
context_summary: NotRequired[str]
class MyMiddleware(AgentMiddleware):
state_schema = MyMiddlewareState
def before_model(self, state, runtime):
# 访问扩展的状态字段
if state.get("retry_count", 0) > 3:
return {"jump_to": "end"}
3.4 架构对比:从宏观到微观
3.4.1 架构层次对比
Classic 的三层架构:
┌──────────────────────────────────────────────────────┐
│ langchain_classic (v1.0.3) 兼容入口层 │
│ __getattr__ 动态导入 + _warn_on_import 废弃警告 │
├──────────────────────────────────────────────────────┤
│ 核心实现层(本地代码) │
│ ├── agents/ 9 种 Agent + AgentExecutor 循环 │
│ ├── chains/ 87 种 Chain 预制件 │
│ ├── memory/ 26 种 Memory 实现 │
│ └── document_loaders/ 171+ 种加载器(→ community) │
├──────────────────────────────────────────────────────┤
│ langchain_core (基础抽象) │
│ langchain_community (社区集成) │
└──────────────────────────────────────────────────────┘
新版的精简架构:
┌──────────────────────────────────────────────────────┐
│ langchain (v1.2.15) 精简入口层 │
│ create_agent() + init_chat_model() + Middleware │
├──────────────┬───────────────┬────────────────────────┤
│ langchain-core │ langgraph │ Partner 包 │
│ (基础抽象) │ (图编排引擎) │ (模型/向量库集成) │
└──────────────┴───────────────┴────────────────────────┘
3.4.2 核心概念映射表
| Classic 概念 | 新版对应 | 迁移说明 |
|---|---|---|
AgentExecutor + Agent 子类 |
create_agent() |
返回 LangGraph CompiledStateGraph |
AgentType 枚举(9 种) |
不再需要 | create_agent() 自动处理工具调用策略 |
LLMChain |
LCEL 管道 `prompt | model |
SequentialChain / SimpleSequentialChain |
RunnableSequence(管道操作符 ` |
`) |
RouterChain / MultiPromptChain |
RunnableBranch 或 LangGraph 条件边 |
声明式路由 |
BaseMemory(26 种实现) |
LangGraph Checkpointer + BaseStore |
图级状态持久化 |
ConversationBufferMemory |
AgentState.messages + add_messages reducer |
消息自动累积 |
ConversationSummaryMemory |
SummarizationMiddleware |
中间件自动总结 |
| 手动错误处理 | ModelFallbackMiddleware / ModelRetryMiddleware |
声明式容错 |
initialize_agent(agent=AgentType.OPENAI_FUNCTIONS) |
create_agent(model, tools=...) |
自动检测工具调用能力 |
| Agent Toolkits(30+) | 自定义工具 + Middleware | 按需组合 |
document_loaders/(171+) |
迁移到 langchain_community |
不再由主包管理 |
handle_parsing_errors |
ToolRetryMiddleware |
细粒度重试控制 |
max_iterations |
ModelCallLimitMiddleware / ToolCallLimitMiddleware |
分别限制模型和工具调用次数 |
3.4.3 代码量对比
| 指标 | Classic | 新版 |
|---|---|---|
| Python 文件数 | 1300+ | ~30 |
| 核心依赖数 | 7 | 3 |
| 公开 API 数量 | 数百个 | ~20 |
agents/__init__.py 导出 |
164 个 | 2 个 |
chains/__init__.py 导出 |
87 个 | 不存在 |
| Agent 创建方式 | initialize_agent() + AgentType + Agent 子类 + AgentExecutor |
create_agent() |
| 模型初始化 | 每个 Provider 单独导入 | init_chat_model("provider:model") |
| 行为定制 | 继承 Agent/Chain 子类 | Middleware 组合 |
3.5 Classic 兼容层:优雅的过渡设计
Classic 版本并没有简单地被弃用,而是通过精心设计的兼容层实现了平滑过渡。这个兼容层的设计值得深入分析。
3.5.1 __getattr__ 延迟导入机制
Classic 的 __init__.py(来源:init.py)使用 __getattr__ 实现模块级的动态导入:
python
def __getattr__(name: str) -> Any:
if name == "MRKLChain":
from langchain_classic.agents import MRKLChain
_warn_on_import(name, replacement="langchain_classic.agents.MRKLChain")
return MRKLChain
if name == "OpenAI":
from langchain_community.llms import OpenAI
_warn_on_import(name, replacement="langchain_community.llms.OpenAI")
return OpenAI
if name == "PromptTemplate":
from langchain_core.prompts import PromptTemplate
_warn_on_import(name, replacement="langchain_core.prompts.PromptTemplate")
return PromptTemplate
if name == "LLMBashChain":
msg = (
"This module has been moved to langchain-experimental. "
"For more details: ..."
)
raise ImportError(msg)
# ...
这个 __getattr__ 实现了三种重定向策略:
- 内部重定向 :从
langchain_classic根级别重定向到子模块(如MRKLChain→langchain_classic.agents) - 跨包重定向 :从
langchain_classic重定向到langchain_community或langchain_core(如OpenAI→langchain_community.llms) - 移除告知 :对已完全移除的模块抛出
ImportError并告知迁移路径(如LLMBashChain→langchain-experimental)
3.5.2 _warn_on_import 废弃警告
python
def _warn_on_import(name: str, replacement: str | None = None) -> None:
"""Warn on import of deprecated module."""
from langchain_classic._api.interactive_env import is_interactive_env
if is_interactive_env():
# 在 Jupyter/IPython 中不显示警告
return
if replacement:
warnings.warn(
f"Importing {name} from langchain root module is no longer supported. "
f"Please use {replacement} instead.",
stacklevel=3,
)
设计亮点:
- 环境感知 :通过
is_interactive_env()检测 Jupyter/IPython 环境,在交互式环境中静默不警告,避免在自动补全时触发大量警告 stacklevel=3:让警告指向用户代码中的import语句,而非框架内部代码- 明确的替代方案:每个警告都告知具体的迁移路径
3.5.3 create_importer 通用导入工厂
Classic 的子模块(如 agents/__init__.py、chains/__init__.py)使用更通用的 create_importer 工厂函数:
python
# agents/__init__.py 的延迟导入
DEPRECATED_LOOKUP = {
"create_json_agent": "langchain_community.agent_toolkits.json.base",
"create_openapi_agent": "langchain_community.agent_toolkits.openapi.base",
"create_sql_agent": "langchain_community.agent_toolkits.sql.base",
"load_tools": "langchain_community.agent_toolkits.load_tools",
# ...
}
DEPRECATED_CODE = [
"create_csv_agent", # → langchain_experimental
"create_pandas_dataframe_agent", # → langchain_experimental
"create_spark_dataframe_agent", # → langchain_experimental
]
_import_attribute = create_importer(__package__, deprecated_lookups=DEPRECATED_LOOKUP)
def __getattr__(name: str) -> Any:
if name in DEPRECATED_CODE:
msg = f"{name} has been moved to langchain_experimental."
raise ImportError(msg)
return _import_attribute(name)
create_importer 的核心实现(来源:langchain_classic/_api/module_import.py)提供了:
- 白名单验证 :只允许从
{langchain_community, langchain_core, langchain_classic}导入 - 缺失依赖提示 :当目标模块不存在时,自动提示安装
langchain-community - 内部调用检测 :通过
internal.is_caller_internal(depth=3)避免对框架内部调用发出警告 - 统一的废弃警告模板 :所有警告都遵循
since="0.1"/removal="1.0"格式
3.5.4 废弃装饰器体系
Classic 使用统一的 @deprecated 装饰器标记所有废弃的类和函数:
python
# 来源:langchain_classic/_api/deprecation.py
AGENT_DEPRECATION_WARNING = (
"LangChain agents will continue to be supported, but it is recommended for new "
"use cases to be built with LangGraph. LangGraph offers a more flexible and "
"full-featured framework for building agents, including support for "
"tool-calling, persistence of state, and human-in-the-loop workflows."
)
# 使用示例
@deprecated("0.1.0", message=AGENT_DEPRECATION_WARNING, removal="1.0")
class AgentType(str, Enum): ...
@deprecated("0.1.0", message=AGENT_DEPRECATION_WARNING, removal="1.0")
def initialize_agent(...) -> AgentExecutor: ...
@deprecated(since="0.1.17", alternative="RunnableSequence, e.g., `prompt | llm`", removal="1.0")
class LLMChain(Chain): ...
@deprecated(since="0.3.1", removal="1.0.0", message="Please see the migration guide at: ...")
class ConversationBufferMemory(BaseChatMemory): ...
废弃标记遵循统一的规则:
since:标注废弃的起始版本removal:标注计划移除的版本(统一为"1.0")alternative:推荐的替代方案message:自定义迁移指引
3.6 迁移实战:从 Classic 到新版
3.6.1 基础 LLM 调用迁移
Classic:
python
from langchain_classic.chains import LLMChain
from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
prompt = PromptTemplate(
input_variables=["topic"],
template="Write a short poem about {topic}."
)
llm = ChatOpenAI(model="gpt-4o")
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.invoke({"topic": "spring"})
print(result["text"])
新版(LCEL):
python
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain.chat_models import init_chat_model
prompt = PromptTemplate(
input_variables=["topic"],
template="Write a short poem about {topic}."
)
model = init_chat_model("openai:gpt-4o")
chain = prompt | model | StrOutputParser()
result = chain.invoke({"topic": "spring"})
print(result)
关键变化:
LLMChain→ LCEL 管道prompt | model | parser- 不再需要显式导入
ChatOpenAI,使用init_chat_model()统一初始化 - 输出从
result["text"]变为直接的字符串
3.6.2 Agent 创建迁移
Classic:
python
from langchain_classic.agents import initialize_agent, AgentType
from langchain_classic.agents import ZeroShotAgent, AgentExecutor
from langchain_classic.chains import LLMChain
from langchain_openai import ChatOpenAI
# 方式一:使用工厂函数
llm = ChatOpenAI(model="gpt-4o")
agent_executor = initialize_agent(
tools=[search_tool, calculator_tool],
llm=llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True,
max_iterations=15,
handle_parsing_errors=True,
)
result = agent_executor.invoke({"input": "What's 2+2?"})
# 方式二:手动组装
prompt = ZeroShotAgent.create_prompt(tools=tools)
llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = ZeroShotAgent(llm_chain=llm_chain, tools=tools)
executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=10,
early_stopping_method="generate",
)
新版:
python
from langchain.agents import create_agent
from langchain.agents.middleware import ModelCallLimitMiddleware
agent = create_agent(
"openai:gpt-4o",
tools=[search_tool, calculator_tool],
system_prompt="You are a helpful assistant.",
middleware=[
ModelCallLimitMiddleware(max_calls=15),
],
)
result = agent.invoke({"messages": [("human", "What's 2+2?")]})
关键变化:
initialize_agent()+AgentType+AgentExecutor→ 单一的create_agent()max_iterations→ModelCallLimitMiddleware(更精确的控制)- 输入从
{"input": "..."}变为{"messages": [...]}(消息驱动) - 不再需要选择 Agent 类型,
create_agent()自动处理工具调用
3.6.3 Memory 迁移
Classic:
python
from langchain_classic.memory import ConversationBufferMemory
from langchain_classic.chains import ConversationChain
memory = ConversationBufferMemory()
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True,
)
# 多轮对话
conversation.invoke({"input": "Hi, I'm Alice"})
conversation.invoke({"input": "What's my name?"}) # 记住了上下文
新版:
python
from langchain.agents import create_agent
from langgraph.checkpoint.memory import MemorySaver
# 使用 LangGraph Checkpointer 实现持久化
memory = MemorySaver()
agent = create_agent(
"openai:gpt-4o",
system_prompt="You are a helpful assistant.",
checkpointer=memory,
)
# 多轮对话通过 thread_id 关联
config = {"configurable": {"thread_id": "user-123"}}
agent.invoke({"messages": [("human", "Hi, I'm Alice")]}, config)
agent.invoke({"messages": [("human", "What's my name?")]}, config)
关键变化:
ConversationBufferMemory→MemorySaver(LangGraph Checkpointer)- Memory 不再嵌入 Chain,而是通过配置传递
- 多轮对话通过
thread_id区分不同会话 - 消息历史自动累积(
add_messagesreducer)
3.6.4 对话总结迁移
Classic:
python
from langchain_classic.memory import ConversationSummaryBufferMemory
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=300,
)
新版:
python
from langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
agent = create_agent(
"openai:gpt-4o",
middleware=[
SummarizationMiddleware(), # 自动总结长对话
],
checkpointer=memory_saver,
)
3.6.5 错误处理与降级迁移
Classic:
python
# 有限的错误处理能力
executor = AgentExecutor(
agent=agent,
tools=tools,
handle_parsing_errors=True, # 只处理解析错误
max_iterations=10,
)
# 模型降级需要手动实现
try:
result = primary_llm.invoke(prompt)
except Exception:
result = fallback_llm.invoke(prompt)
新版:
python
from langchain.agents import create_agent
from langchain.agents.middleware import (
ModelFallbackMiddleware,
ModelRetryMiddleware,
ToolRetryMiddleware,
)
agent = create_agent(
"anthropic:claude-sonnet-4-20250514",
tools=tools,
middleware=[
ModelRetryMiddleware(max_retries=3), # 模型调用自动重试
ModelFallbackMiddleware( # 模型降级链
fallback_models=["openai:gpt-4o", "openai:gpt-4o-mini"],
),
ToolRetryMiddleware(max_retries=2), # 工具调用自动重试
],
)
3.6.6 结构化输出迁移
Classic:
python
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel
class Answer(BaseModel):
answer: str
confidence: float
parser = PydanticOutputParser(pydantic_object=Answer)
chain = prompt | llm | parser
新版:
python
from langchain.agents import create_agent
from pydantic import BaseModel
class Answer(BaseModel):
answer: str
confidence: float
# 直接传入 Pydantic 模型,自动选择最佳策略
agent = create_agent(
"openai:gpt-4o",
tools=tools,
response_format=Answer,
)
result = agent.invoke({"messages": [("human", "What's the capital of France?")]})
# result["structured_response"] 是 Answer 实例
3.7 create_agent() 内部实现:图构建过程
理解 create_agent() 的内部实现有助于深入掌握新版架构。factory.py 是一个 1869 行的文件,其中 create_agent() 函数及其内部辅助函数构成了完整的图构建流程。
3.7.1 核心构建步骤
python
# 简化的 create_agent 内部流程
def create_agent(model, tools, *, middleware, ...):
# 1. 模型初始化
if isinstance(model, str):
model = init_chat_model(model)
# 2. 系统消息处理
system_message = SystemMessage(content=system_prompt) if system_prompt else None
# 3. 中间件验证(去重检查)
if len({m.name for m in middleware}) != len(middleware):
raise AssertionError("Please remove duplicate middleware instances.")
# 4. 中间件分类:哪些实现了 wrap_model_call / wrap_tool_call
middleware_w_wrap_model_call = [
m for m in middleware
if m.__class__.wrap_model_call is not AgentMiddleware.wrap_model_call
]
# 5. 组合处理器链
wrap_model_call_wrapper = _chain_model_call_handlers(wrappers)
wrap_tool_call_wrapper = _chain_tool_call_wrappers(wrappers)
# 6. 创建 ToolNode
tool_node = ToolNode(
tools=available_tools,
wrap_tool_call=wrap_tool_call_wrapper,
) if available_tools else None
# 7. 构建 StateGraph
graph = StateGraph(state_schema)
graph.add_node("model", model_node)
graph.add_node("tools", tool_node)
# 8. 添加中间件节点和边
for m in middleware:
_add_middleware_edge(graph, m, ...)
# 9. 添加条件边(模型 → 工具 / 模型 → 结束)
graph.add_conditional_edges("model", _make_model_to_tools_edge(...))
graph.add_conditional_edges("tools", _make_tools_to_model_edge(...))
# 10. 编译并返回
return graph.compile(
checkpointer=checkpointer,
store=store,
interrupt_before=interrupt_before,
interrupt_after=interrupt_after,
)
3.7.2 图结构示意
┌────────────────┐
│ START │
└───────┬────────┘
│
┌──────────▼──────────┐
│ before_agent │ (中间件节点,可选)
└──────────┬──────────┘
│
┌──────────▼──────────┐
┌──────│ MODEL 节点 │◄─────────────┐
│ │ (before_model → │ │
│ │ wrap_model_call → │ │
│ │ after_model) │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ │ 有工具调用 │ 无工具调用 │ │
│ ▼ ▼ │ │
│ ┌──────────┐ ┌──────────┐ │ │
│ │ TOOLS │ │ END │ │ │
│ │(wrap_ │ │ │ │ │
│ │tool_call)│ └──────────┘ │ │
│ └────┬─────┘ │ │
│ │ │ │
│ └─────────────────────┘ │
│ 回到 MODEL ─────────────────┘
│
│ (jump_to 流程控制)
└──────────────► after_agent → END
这个图结构清晰地展示了新版 Agent 如何利用 LangGraph 的图能力替代了 Classic AgentExecutor 的简单循环。
3.8 迁移决策框架
3.8.1 什么时候应该迁移?
| 场景 | 建议 |
|---|---|
| 新项目 | 直接使用新版 |
使用 LLMChain 的简单应用 |
迁移到 LCEL 管道(改动最小) |
使用 AgentExecutor 的 Agent |
迁移到 create_agent()(获得中间件和持久化能力) |
使用 ConversationBufferMemory |
迁移到 LangGraph Checkpointer |
使用 ConversationSummaryMemory |
迁移到 SummarizationMiddleware |
深度使用 chains/ 中的特殊 Chain |
评估是否可以用 LCEL 或 LangGraph 替代 |
使用 document_loaders/ |
直接从 langchain_community 导入 |
3.8.2 迁移的核心心智模型转换
从 Classic 到新版的迁移,本质上是以下心智模型的转换:
Classic 思维: 新版思维:
"我需要找到合适的 Chain 类" → "我用管道组合 Runnable"
"我需要选择合适的 Agent 类型" → "我用 create_agent + 中间件"
"我需要选择合适的 Memory 类" → "我用 Checkpointer + Middleware"
"我需要找到预制的解决方案" → "我用基础原语按需组合"
这种转变反映了整个 LLM 应用开发领域的成熟:从"框架提供预制方案"到"框架提供组合原语",从"大而全"到"精简可组合"。
3.9 本章小结
| 维度 | Classic(langchain-classic v1.0.3) | 新版(langchain v1.2.15) |
|---|---|---|
| 设计哲学 | 功能全包,预制方案优先 | 精简入口,组合原语优先 |
| 核心抽象 | Chain + Agent + Memory | create_agent() + Middleware + StateGraph |
| 编排方式 | SequentialChain / AgentExecutor 循环 |
LangGraph 状态图 |
| 状态管理 | BaseMemory(链级内嵌) |
Checkpointer + BaseStore(图级持久化) |
| 行为定制 | 继承子类重写 | 中间件组合 |
| 模型集成 | 每个 Provider 单独导入 | init_chat_model("provider:model") |
| 错误处理 | handle_parsing_errors 布尔值 |
ModelRetryMiddleware + ModelFallbackMiddleware |
| 人工介入 | 不支持 | HumanInTheLoopMiddleware + interrupt_before/after |
| 结构化输出 | PydanticOutputParser(手动接入) |
response_format(内置支持) |
| 兼容策略 | __getattr__ 动态导入 + @deprecated 装饰器 |
N/A(全新设计) |
Classic 版本完成了它的历史使命------降低 LLM 应用开发的入门门槛,让开发者能够快速构建原型。而新版则面向生产环境,提供更灵活、更可控、更可扩展的架构。两者之间通过精心设计的兼容层和废弃策略实现平滑过渡,这种演进方式本身就是优秀软件工程的范例。
下一章预告 :第四章:模型集成生态 ------ Partner 包架构与 init_chat_model 统一入口 将深入探讨 Partner 包的标准化架构、
_BUILTIN_PROVIDERS注册机制、Model Profiles 系统,以及如何创建自己的 Partner 集成。