LangChain 设计与实现
前言
写作动机
2025 年 10 月 17 日,langchain 与 langchain-core 同时发布 1.0.0,LangChain 完成了从 0.x 到 1.0 的蜕变。这不仅仅是一个版本号的变化——它意味着 API 的稳定化、架构的成熟化,以及从"快速实验框架"到"生产级基础设施"的定位转变。
在此之前,0.x 时代走了将近两年:2024 年 1 月发布 0.1.0,2024 年 9 月发布 0.3.0,包结构被拆成 langchain-core / langchain / 各家 partner 包,为 1.0 的 API 冻结做完了铺垫。
LangChain 是 AI 应用开发领域使用最广泛的框架。但大多数开发者的使用方式是:复制官方示例,调整参数,遇到问题搜 Stack Overflow。他们知道 ChatOpenAI | prompt | parser 可以组成一个管线,但不知道 | 操作符背后发生了什么;知道 AgentExecutor 可以让模型调用工具,但不知道 Agent 循环的停止条件是如何判定的。
这个专栏要回答的是:LangChain 内部到底是怎么运作的?
当你写下 chain = prompt | llm | parser 时,LCEL 如何将三个组件编织成一个支持流式、异步、批处理的统一管线?当 Agent 决定调用一个工具时,从模型输出到工具执行再到结果回传,经历了哪些步骤?当你配置了 ConversationBufferMemory 时,历史消息如何在每一轮对话中被注入?
这个专栏讲什么
本专栏从 LangChain 的两个核心包出发:
- langchain-core(轻量、无第三方依赖)——定义所有基础抽象:Runnable、消息、提示词、工具、回调
- langchain(构建在 core 之上)——实现高级功能:Chain、Agent、Memory、Retriever
每一章聚焦一个核心模块,从设计意图出发,深入源码实现,大量使用 Mermaid 图表可视化架构关系和数据流,最后总结可迁移的设计模式。
本专栏读者
- AI 应用开发者:用 LangChain 构建过项目,想理解框架内部机制以便更好地调试和优化
- 框架开发者:正在设计 AI 应用框架,想学习 LangChain 的抽象设计和接口哲学
- Python 开发者:对高级 Python 模式(协议类、泛型、异步生成器、元编程)感兴趣
- 技术决策者:需要评估 LangChain 是否适合你的项目
本专栏组织
全专栏 18 章,按照从底层抽象到上层应用的顺序:
| 部分 | 章节 | 核心模块 |
|---|---|---|
| 核心抽象 | Ch2-4 | Runnable/LCEL、消息系统、多模态 |
| 模型与提示 | Ch5-7 | 语言模型抽象、提示词模板、输出解析 |
| 工具与检索 | Ch8-10 | 工具系统、文档加载、向量存储 |
| 组合与编排 | Ch11-13 | Chain 组合、回调/可观测性、记忆管理 |
| Agent 系统 | Ch14-15 | Agent 架构、工具调用 Agent |
| 生产与进阶 | Ch16-18 | 序列化、Partner 集成、设计模式 |
每章结构:设计意图 → 源码剖析 → Mermaid 可视化 → 可迁移模式。
源码版本
本专栏基于 langchain-core 1.2.26 与 langchain-classic 1.0.3 源码分析。仓库是 monorepo,
langchain-core 每个发布版都打了 git tag,checkout 到本专栏基准快照即可逐行对照:
git clone https://github.com/langchain-ai/langchain.git
cd langchain
git checkout langchain-core==1.2.26 # 对应 commit 0a1d290a,2026-04-03
libs/ 下的目录名与包名并不一一对应,这是 1.0 拆包留下的历史痕迹,第一次读很容易走错门:
| 目录 | 包名 | 该快照版本 |
|---|---|---|
libs/core/langchain_core/ |
langchain-core |
1.2.26 |
libs/langchain/langchain_classic/ |
langchain-classic |
1.0.3 |
libs/langchain_v1/langchain/ |
langchain |
1.2.15 |
本专栏的主要解剖对象是 langchain_core;讲 Chain、Memory、AgentExecutor 这些
0.x 时代沉淀下来的经典组件时,读的是 langchain_classic——1.0 拆包时它们被整体
迁进了这个独立发行包。
别把 langchain-classic 1.0.3 当成 langchain 1.0.3
本专栏写到的 1.0.3 是 langchain-classic 的版本号(2026-03-13 发布)。
PyPI 上的 langchain 1.0.3 是 2025-10-29 发布的另一个包,两者不是一回事。
代码引用的约定
正文里大量代码块顶部有这样一行标注:
# 源码文件:libs/core/langchain_core/runnables/base.py (第2911行)
它指向的是上述快照里的真实位置,行号可以直接跳。但要说清楚一点:
代码块是节选,不是原文粘贴。 为了让主干逻辑在一屏内读得完,正文通常会 删去完整的类型标注与泛型参数、省略防御性分支与日志、把多行签名压成一行, 并补上中文注释。
举个例子,Runnable.invoke 在源码里的签名是这样的:
def invoke(self, input: Input, config: RunnableConfig | None = None, **kwargs: Any) -> Output:
正文为了聚焦控制流,多半会写成 def invoke(self, input, config=None, **kwargs):。
但有三类内容永远按原文引用,一个字都不会改:常量的值、报错信息的文本、
关键的条件判断。比如 DEFAULT_RECURSION_LIMIT = 25、
COPIABLE_KEYS = ["tags", "metadata", "callbacks", "configurable"]、
"RunnableBranch requires at least two branches"、
type(self).add_messages != BaseChatMessageHistory.add_messages——
这些是你 debug 时真正会去 grep 的东西,改写它们等于制造错误答案。
所以:读结构看正文的节选,抄代码请回到源码。
感谢 Harrison Chase 和 LangChain 团队创建了这个定义了 AI 应用开发范式的框架,并保持完全开源。