我的 LangChain 学习之旅:从核心概念到动手实践
前言
我最近正在学习 LangChain,这是一个强大的大型语言模型(LLM)应用开发框架。为了检验自己的学习水平并明确下一步的方向,我与 AI 进行了一次深入的问答式学习。这篇笔记记录了从评估我的初始认知,到深入理解核心组件,再到解决具体疑惑的全过程。希望我的学习路径和思考,也能为同样走在这条路上的你提供一些参考。
第一站:知识水平评估——我最初的理解
在开始时,我对 LangChain 的理解停留在比较宏观的层面:
- 核心价值:我认为 LangChain 的价值在于为 LLM 赋能,拓展其能力边界,解决大模型应用开发缓慢的问题。
- 基础组件:
Models: 对接 OpenAI 等大模型 API。Prompts: 提示词模板。Output Parsers: 用于结构化输出。
- RAG 流程:我对检索增强生成(RAG)的流程非常熟悉,即 加载 -> 分割 -> 向量化 -> 存储 -> 检索。
- Chains vs. Agents:我能区分
Chains(确定性流程)和Agents(动态决策)的区别。
AI 指出了我的知识盲区:Memory(记忆)、LCEL(LangChain 表达式语言)以及 Debugging(调试)。这便是我此行学习的重点。
第二站:核心组件深潜——那些我新学到的关键概念
这是本次学习的核心,我重点掌握了以下几个以前模糊不清或完全不知道的概念。
1. LCEL (LangChain Expression Language) - 现代化的构建方式
这是我此行最大的收获之一。LCEL 是 LangChain 官方推荐的、用于组合组件的声明式方法。
- 核心语法:使用管道符
|将组件像流水线一样连接起来。 - 代码示例:一个简单的“提示+模型+解析器”链。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
prompt = ChatPromptTemplate.from_template("请告诉我关于 {topic} 的一个笑话。")
model = ChatOpenAI()
output_parser = StrOutputParser()
# 这就是 LCEL 的魔力!
chain = prompt | model | output_parser
chain.invoke({"topic": "程序员"})- 优势:相比旧版的
Chain类,LCEL 更简洁、透明,并且原生支持流式(streaming)、并行(parallelism)和异步(async)调用,是现代 LangChain 开发的基石。
2. 调试 LCEL 链 - set_debug(True)
学会了如何构建,下一步就是如何调试。对于 LCEL,verbose=True 的旧方法不再适用。取而代之的是一个强大的全局函数:
- 启用方法:
from langchain.globals import set_debug
set_debug(True)- 日志解读:开启后,控制台会打印出非常详细的、带颜色的日志。通过
>符号可以清晰地看到链的嵌套结构,[llm/start]、[parser/end]等标签则标明了每个组件的类型、输入、输出和耗时。这对于理解复杂链条的数据流向至关重要。
3. RunnableSequence vs. RunnableParallel - 链的两种基本形态
在解读日志时,我遇到了这两个概念,它们的区别是理解 LCEL 工作流的关键。
RunnableSequence(顺序执行):就是由|创建的流水线。上一步的输出是下一步的输入,按顺序执行。RunnableParallel(并行执行):通常表现为一个字典。它会将同一个输入分发给字典中的多个处理分支,各分支独立运行,最后将结果汇总成一个字典。它常用于为下一步的 Prompt 准备多个不同的输入变量(如context和question)。
4. RunnableLambda - 流水线上的“DIY”工具
当标准组件不满足需求时,RunnableLambda 就派上了用场。
- 作用:它可以将任何一个 Python 函数包装成一个可以在 LCEL 链中使用的组件。
- 应用场景:执行自定义的数据转换、调用外部 API、筛选数据,或者仅仅是在链中某个位置打印中间数据进行调试。在我们的对话式 RAG 示例中,它被用来从
Memory对象中提取历史聊天记录。
5. Retriever (检索器) - 知识库的“图书管理员”
我之前只知道从向量数据库检索,但对 Retriever 的角色有些模糊。
- 核心职责:一个专门负责根据查询,从数据源中检索出最相关信息的接口。
- 与 Vector Store 的区别:
Vector Store(向量数据库) 是存储和搜索的底层工具,是被动的数据库。Retriever(检索器) 是封装了检索逻辑的上层应用接口。它知道“如何去取信息”。
代码示例:
# 从一个向量数据库创建一个检索器 retriever = vectorstore.as_retriever()这个
retriever对象现在就是一个可以被放入 LCEL 链中的组件了。
第三站:旅途终点与新起点
通过这次深入的探讨学习,我不仅补齐了知识短板,更重要的是建立了一套完整的 LangChain 知识体系。
我的核心收获:
- 现代化开发范式:我已完全理解 LCEL,并能用它来构建和调试链。这是从“能用”到“会用”的关键一步。
- 构建对话应用的核心:掌握了
Memory组件,我终于可以构建能联系上下文的聊天机器人了。 - 洞察内部机制:通过学习解读
debug日志,我对数据如何在链条中流动有了直观的认识,这让我有能力解决更复杂的问题。
接下来,我将基于今天的学习,继续我的 LangChain 探索之旅:
- 动手实践:构建一个更复杂的、带有查询重写功能的对话式 RAG Agent。
- 探索高级功能:研究
LangServe以便将我的应用部署为 API。