☰
LangChain全家桶解析:从核心包到Agent编排一次理清
2026/10/27 7:43:28 网站建设 项目流程

后台催更的时候,我正好在重构一个用 LangChain 写了半年的项目。重构到一半突然意识到,如果不先把整个 LangChain 生态的版图画清楚,写再多 demo 也是在沙滩上盖楼。所以这篇学习笔记系列第三篇,我先把 LangChain 全家桶里究竟都有什么东西、每个包到底管哪一段、以及为什么社区天天在“LangChain、Dify、CrewAI 选哪个”之间吵来吵去,一次性梳理完。它不会带你手把手写代码,而是一张地图:看过之后你再去看具体教程,至少能分得清哪些内容是核心,哪些只是某个版本的临时补丁。

1. 从 0.x 到全家桶:LangChain 生态到底发生了什么

1.1 我为什么把这篇文章放在系列第三篇

这个系列前两篇一直在讲 prompt 工程和链式调用,很多读者留言问:“怎么你写的 LangChain 和我搜到的 LangChain 不太一样?” 这不是错觉,而是 LangChain 生态在短短两年里经历了几次大分裂。早期入门教程里最常见的一套操作是from langchain import LLMChain,再配一个PromptTemplate,然后chain.run()。到了 0.1.x、0.2.x 时代,这套写法已经慢慢从文档里消失,取而代之的是langchain-core、langchain-openai、langgraph这些拆分出来的包。

更麻烦的是,LangChain 官方又把“全家桶”这个概念进一步扩大了:除了主库之外,还有 LangGraph、LangServe、LangSmith、LangChain CLI,每个名字都像 LangChain 的亲戚,但职责完全不同。如果不站在生态全景的高度去看,入门的同学很容易把某个第三方包的报错归咎于“LangChain 难用”,其实锅根本不在核心库身上。

因此这篇笔记是系列里最该先读的一篇。它不解决具体某个函数的用法,而是回答几个更底层的问题:LangChain 全家桶包含哪些成员?它们之间怎么协作?选型时和 Dify、CrewAI 这类框架比,各自的边界在哪里?看完之后,再回头看具体代码会轻松很多。

1.2 全家桶不是“一个包”,而是一组包

很多人误以为 LangChain 是一个“大而全的库”,其实从版本拆分之后,它更像是一组围绕 LLM 应用开发的工具集,核心思路是分层:核心抽象层、社区集成层、运行时层、可观测性层。

先用一张表格建立整体印象:

包名职责开源/服务形态
langchain-core核心抽象、接口、LCEL 表达式开源核心库
langchain / langchain-community各种第三方集成、工具、加载器的汇总开源
langchain-openai / langchain-google 等按厂商拆分的官方集成包开源
langgraphAgent 状态图、持久化、人工介入开源
langserve将链或图发布成 REST API开源
langsmith调试、追踪、评估、数据集管理托管服务为主
langchain-cli创建项目、管理依赖开源命令行工具

这种拆分逻辑很像前端生态把“框架核心”和“组件库”分开,避免把所有依赖都塞进一个包里。核心库只定义对话消息、文档对象、工具、Runnable 接口等最稳定的概念,而具体对接哪家模型、哪个向量库,则由单独的包负责。

这样做的好处是,你不需要为了用一个向量数据库而安装一堆无关依赖。做生产项目时,依赖体积和供应链安全都很重要,拆包后锁依赖更干净。代价也很明显——新手看到一长串pip install langchain-xxx的指令会本能地抵触,这也是中文社区里“LangChain 太重了”声音的来源之一。

1.3 命名规律和选型直觉

我整理 LangChain 全家桶时总结出一条命名规律:前缀是langchain-,后面跟着的是集成对象。例如langchain-openai集成 OpenAI 模型,langchain-pinecone集成 Pinecone 向量库,langchain-postgres集成 Postgres。这些包通常负责把第三方服务转换成 LangChain 的统一接口。

另一类包不带厂商名,比如langgraph和langserve,它们是整套生态里更上层的运行时工具。我的选型直觉是:核心代码尽量只依赖langchain-core,模型调用根据实际供应商引入对应厂商包,复杂的 Agent 状态管理交给langgraph,需要对外服务再考虑langserve。这样会最大程度减少不同包之间的版本摩擦。

2. 核心包分工:langchain-core、langgraph、langserve、langsmith 各管哪一段

2.1 langchain-core:所有家当的地基

先看langchain-core,它是理解整个生态的钥匙。这个包内部解决了几类问题:第一类是数据模型,比如对话里的SystemMessage、AIMessage、HumanMessage,以及文档对象Document;第二类是接口协议,比如所有链和模型都遵循的Runnable接口,它定义了invoke、batch、stream方法;第三类是工具和输出的规范,让大模型可以按照 JSON Schema 调用外部工具。

可以这样理解:langchain-core负责“形态统一”,不管背后是 OpenAI、Anthropic 还是本地模型,只要实现了 Runnable 接口,就能被同一个表达式串联起来。这也是 LCEL(LangChain Expression Language)能成立的前提。写代码时,我建议新手优先熟悉langchain_core.runnables和langchain_core.prompts两个子模块,它们几乎是所有链式调用的基础。

2.2 langgraph:把链升级成可暂停的图

在 LangChain 早期,链被设计成线性管道:A 调用 B,B 调用 C,结果按顺序走完。但真实 Agent 场景里完全不是这样,你需要条件分支、循环、等待人审批,甚至让 Agent 一边执行一边把中间状态存下来。这不是普通链能表达的事情,而是状态机的领域。

langgraph就是 LangChain 生态里的状态机运行时。它把每个环节建模成图里的节点,把上下文建模成“状态”,节点的执行顺序则由边和条件决定。最实用的特性是它支持 checkpoint,即把 Agent 跑到一半的状态保存下来,下一次可以从断点继续。这正好对应了生产环境里最需要的“持久化”能力。

我自己的感觉是,langgraph的出现是 LangChain 生态从“调用链”走向“完整 Agent 运行时”的分水岭。如果你发现用普通链写 Agent 时总是到处打补丁,那大概率是时候换到langgraph了。

2.3 langserve:把流程交给 HTTP

langserve解决的问题非常直白:你写好了链或 Agent,怎么让别的服务调用它?它基于 FastAPI 把 Runnable 包装成 HTTP 接口,提供/invoke、/batch、/stream之类的标准端点,省掉自己写路由、请求体解析、错误处理的重复劳动。

不过需要泼一盆冷水:langserve适合快速暴露原型服务,但它不是万能的网关方案。生产环境里往往还需要加鉴权、限流、负载均衡、可观测性字段透传等,这些最好在 API 网关层做,而不是指望langserve全包。我的实践做法是,langserve负责把 LangChain 组件变成内部微服务,再统一挂到网关下面。

2.4 langsmith:给全家桶装上仪表盘

最后是langsmith,它主要解决大模型应用里“debug 难”的问题。普通程序出错有堆栈,LLM 应用出错往往是因为 prompt 稍微变一下、模型温度不同、甚至同一段输入在两次调用里结果不同。langsmith会把每次 run 的输入、输出、耗时、token 消耗、中间调用链都记录下来,并支持做回归评估。

我建议从学习阶段就开始用langsmith:哪怕只有一个 API key,也能让你直观地看到链的每一步发生了什么,比盲目打印日志高效得多。尤其当你开始调 RAG 或者 Agent 时,能看到每一步检索到了什么、模型具体使用了哪个工具,问题定位速度会快一个量级。

3. 全家桶里的主力模块:模型、提示、链、记忆、工具、检索

3.1 Models 层:Chat Models 与 Embeddings 的边界

LangChain 对模型的处理有一个容易被忽略的区别:LLM和Chat Models。早期很多教程把二者混为一谈,实际上Chat Models接收的是消息列表,返回的是消息对象;而传统LLM接收字符串,返回字符串。现在绝大多数应用都在用Chat Models,所以学习时可以优先掌握它。

Embeddings 则是另一类模型,负责把文本变成向量。它在 RAG 场景里至关重要,因为文档切分后要先用 Embeddings 向量化,再存进向量库,查询时把问题也向量化,然后做余弦相似度检索。很多人把 Embeddings 和 Chat Models 放在一起聊,但它们做的事情完全不同,一个是理解语义生成向量,一个是生成对话内容。

3.2 Prompts 和 Output Parsers:最容易被低估的两个组件

写 LangChain 时最容易被人忽略的两个模块是Prompts和Output Parsers。Prompt 不只是“拼字符串”,它涉及消息模板和示例少样本策略。一个成熟的ChatPromptTemplate会定义系统消息、用户消息、可选的历史消息槽位,再通过参数填充变成完整的模型输入。

Output Parsers 解决的是“模型返回非结构化文本、程序需要结构化数据”的矛盾。比如让模型抽取出 JSON,再用StrOutputParser或JsonOutputParser解析成对象。我个人习惯是把这一步看作协议转换层:模型本身是不稳定的,而输出解析器可以把不稳定的自然语言转换成稳定的数据格式,方便下游直接落库或调用工具。

3.3 Chains 与 LCEL:老一代链和新一代写法的区别

LangChain 的链经历了“类继承时代”和“LCEL 时代”两个阶段。老一代写法会定义各种 Chain 类,比如LLMChain、RetrievalQAChain,它们内部有固定的输入输出字段,想改行为时要看文档、调参数,非常僵硬。LCEL 则用一种管道风格组合组件:

from langchain_openai import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_template("把这段中文翻译成英文:{text}") model = ChatOpenAI(model="gpt-4o-mini", temperature=0) chain = prompt | model result = chain.invoke({"text": "天气很好"}) print(result.content)

这种prompt | model的写法背后是 Runnable 协议,每个组件都实现了invoke和stream,所以可以自由组合、并行、加回调。学习 LangChain 时,我强烈建议直接进入 LCEL 思路,不要再研究旧的 Chain API。新项目中就算调用第三方框架,只要理解 Runnable 协议,迁移成本也非常低。

3.4 Memory、Tools、Retrievers:Agent 真正成形的地方

记忆、工具、检索是 LangChain 全家桶里把“单次调用”变成“连续工作流”的三个关键模块。

记忆模块用来维护对话历史。最简单的做法是把历史消息塞进 prompt,但生产级场景要考虑长度控制、摘要压缩、持久化存储。LangChain 里提供了多种 memory 方案,但要注意的是,很多旧版 memory 类在新版里已经改名,写代码前最好先确认当前版本对应的 API。

工具模块则负责把外部函数暴露给 Agent。只要写一个普通函数,加上类型注解和 docstring,就能用@tool装饰器把它包装成模型可以调用的工具。这个设计的底层逻辑是:模型本身不能执行代码,它只能“请求”调用某个函数,然后由 LangChain 运行时去执行,再把结果回传给模型继续推理。

Retrievers 则是 RAG 的入口。它接收一个查询字符串,返回一组相关文档。LangChain 里有很多重试、过滤、重排的扩展点,比如先召回粗结果,再用ContextualCompressionRetriever做二次压缩,过滤掉无关片段。RAG 调优时大部分时间都花在 Retriever 这一层,而不是模型层。

4. LangChain、Dify、CrewAI 三选一:生态全景里的位置

4.1 为什么大家总拿这三个对比

中文社区里“agent 框架哪个好”几乎成了日经帖,LangChain、Dify、CrewAI 经常被拉到一起对比。其实它们的定位差异远大于相似之处:LangChain 是一个开发者库,Dify 是一个偏平台化的低代码工具,CrewAI 则是专注于多智能体协作的轻量框架。三者不是替代关系,更像是不同团队、不同阶段的选择题。

4.2 Dify——工作流优先,低代码落地

Dify 最大的优势是可视化。你可以用画布编排大模型应用,包括 prompt 编排、知识库接入、工具绑定、发布 API,前端还有现成的聊天组件。对非纯技术团队来说,Dify 的学习成本比 LangChain 低不少,尤其是在做内部知识库问答、客服助手这类需求时,Dify 能快速上线。

但 Dify 也有边界:它更像是一个“应用平台”,当你需要非常复杂的 Agent 逻辑或深度定制状态流转时,可视化画布会变成一种限制。相比 LangChain 的代码自由度,Dify 的扩展方式是插件和自定义组件,要求你按照它的约定来写。如果团队熟悉 Python,Dify 的很多功能其实都能用 LangChain 重现。

4.3 CrewAI——角色协作优先,轻量编排

CrewAI 的核心心智模型是“角色 + 任务 + 流程”:你定义几个 Agent,给它们分配不同的 role,再定义任务和协作方式,框架会自动协调执行。它在多代理仿真、流程编排这类场景中很顺手,代码量比 LangChain 原生少,也更直观。

但 CrewAI 的深度和生态广度目前不如 LangChain 全家桶。当你需要精确控制每一步流程,或者要接一些冷门数据源时,CrewAI 可能需要自己补轮子。我的建议是,如果项目核心是“多角色协作”,CrewAI 值得一试;如果项目核心是“工具调用 + RAG + 长期维护”,LangChain 体系更稳。

4.4 LangChain——生态广度优先,但要自行掌控复杂度

LangChain 的优势在于集成最多。从模型厂商、向量库、文档加载器到监控平台,你能想到的组件几乎都有对应的第三方包。这也是“全家桶”最大的价值:不需要从头造轮子。

代价也很明显,LangChain 的学习曲线陡峭,版本演进快,中间抽象层多。很多抱怨 LangChain 难用的人,其实是跳过了核心概念直接抄高版本代码,再被一堆异常信息劝退。如果你愿意花时间理解langchain-core和langgraph的底层逻辑,LangChain 的复杂度会从“混乱”变成“可控”。

4.5 一张选型表和一个实用建议

对比维度LangChainDifyCrewAI
上手速度中,依赖基础概念快,可视化优先中,概念简洁
可定制程度高中中高
生态集成广度高中中
多 Agent 编排依赖 LangGraph,灵活有限强项
可视化界面无有无
生产级追踪LangSmith自带部分能力需要自行集成

我个人在项目里的判断标准很简单:如果目标是“给业务团队一个能自己维护的应用平台”,选 Dify;如果要做的是研究性、深度定制的 Agent 产品,走 LangChain + LangGraph 路线;如果想快速验证多角色协作剧本,CrewAI 最轻。三个框架并不互斥,你甚至可以先用 Dify 做原型,再用 LangChain 做生产版本。

5. Agent Inbox:人工介入变成一等公民

5.1 从“卡住的 Agent”说起

任何做过真实 Agent 项目的人都会遇到一个尴尬场景:Agent 执行到一半需要用户确认,比如“确定要删除这条数据吗?”、“请补充一下收货地址”。如果你把这些问题直接丢给模型,它大概率会用一段自然语言描述“我需要用户输入”,但程序并不知道去哪里拿这个输入。于是 Agent 只能卡住,或者靠工程师临时写一个无限循环轮询去等结果。

过去的主流做法是“打断式交互”:Agent 调用一个专门的工具,启动一个等待用户输入的通道,用户填完表单后再继续。问题在于,等待期间整个运行链路必须保持在线,网络波动、进程重启都会导致状态丢失。LangChain 生态给出的更优雅方案,就是把人工介入抽象成一种“消息收件箱”。

5.2 Agent Inbox 的交互范式

Agent Inbox 这个思路,简单说就是给 Agent 准备一个任务收件箱。当 Agent 遇到需要人工决策的信息时,它不再原地死等,而是把当前状态保存下来,把问题塞进收件箱,然后进入暂停状态。用户、管理员、甚至另一个系统都可以通过 API 查看收件箱里的请求,逐条处理:补充信息、批准、拒绝、或直接告诉 Agent 换一种方案。

处理完之后,Agent 从保存的检查点恢复执行,继续往下跑。这种范式最大的价值是“异步化”:Agent 的执行和人的审批解耦,不需要所有人同时在电脑前盯着对话窗口。对长时间运行的 Agent、批处理任务、审批流程来说,Inbox 的设计比同步问答更贴近真实业务。

5.3 它和 LangGraph 打断点的关系

如果你熟悉 LangGraph,会发现 Agent Inbox 和 LangGraph 的 interrupt 机制是一脉相承的。LangGraph 已经有interrupt()函数,可以在节点执行到一半时暂停图,把控制权交给人或其他进程;等收到进一步指令后再从断点恢复。Agent Inbox 可以理解为把这种底层能力产品化,提供了一个统一的收件箱界面和 API,你不用自己去设计“暂停后如何把状态存起来”这套逻辑。

这也代表了 langchain 全家桶对未来 Agent 交互的一种判断:未来的 Agent 不应该是“全自动一次跑完”的黑盒,而是应该具备和人协同工作的能力,知道什么时候该停下来问人。人工介入不再是一种异常,而是一等公民。

6. 中文学习路线:我的全家桶使用心得和典型误区

6.1 按什么顺序学最省力

如果把中文社区里所有 LangChain 教程摊开看,最大的问题是“版本混乱”。同一个函数在不同版本里写法不同,甚至包的位置都变了。我的建议是,学习路径不要按发布日期走,而是按依赖关系走:

第一站读langchain-core的核心概念,理解 message、prompt、model、output parser;第二站熟悉 LCEL,把prompt | model | parser的管道逻辑弄熟;第三站进入langgraph,理解 state、node、edge 和 interrupt;第四站再碰外部集成,比如 RAG 和工具调用;最后才是 LangSmith 的追踪评估。

这个顺序背后的逻辑是:先把不依赖外部服务的“最小闭环”跑通,再引入外部依赖,最后用可观测性工具提高调试效率。很多教程一上来就让人跑 RAG,结果被文档切分、向量库、合并重排一堆概念淹没,其实很劝退。

6.2 三个我踩过且高频出现的坑

第一个坑是直接安装langchain全家桶包,然后通过langchain.agents、langchain.chains找类。新版本里的核心类很多已经移到了langchain_core,导入路径变了。遇到ModuleNotFoundError时,先检查是不是版本差异导致的导入路径问题。

第二个坑是把旧教程里的LLMChain直接搬进新项目。现在很多新例子里,一个prompt | model就可以完成同样的事,没必要再保留旧写法。理解 LCEL 的操作符语义之后,代码更短,调试也更直观。

第三个坑是忽视输出解析。很多同学写完链之后直接把result.content拿去做下游处理,一旦模型输出了几句废话,整个流程就崩了。正确做法是定义好OutputParser或者用with_structured_output,尽可能把模型输出限制成结构化格式。

6.3 中文教程和官方文档怎么搭配

我平时会把中文教程当作“概念翻译器”来用,遇到不好理解的概念先看看中文社区的通俗解释;但一旦决定开工,一定打开官方 API 参考去看当前版本的签名。中文教程的保质期通常不长,因为 LangChain 一直在迭代,而官方文档更新最快。

结合这个系列来看,我的个人建议是:系列笔记只沉淀“不会变的东西”,比如 Runnable 协议、PromptTemplate 的价值、LangGraph 的图心智模型;具体的 API 调用写法标注好版本号,这样一年后回看还有参考价值。现在打开工具链时,我会先写一行注释标明 LangChain 版本,这已经成了习惯。

最后再分享一个小技巧:遇到不确定的 API,直接在本地环境里打印help(对象)或者看它的__repr__,比四处搜中文教程更可靠。LangChain 全家桶虽然大,但它的核心抽象其实很稳定:一个 Runnable 走天下,一张状态图管全部。把这个底层逻辑抓住,再大的生态也不会让你迷路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询