RAG 每日一技(十四):化繁为简,统揽全局——用LangChain构建高级RAG流程

前情回顾

欢迎回来!经过十三天的学习,我们已经不再是RAG领域的新手。我们的武器库里装满了各种精良的武器:ChromaDB向量库、BM25关键词搜索、BGE-ReRanker精排器、HyDE查询生成器......

但一个严峻的现实摆在面前:这些武器都是独立存在的。如果我们想构建一个"先用HyDE改写查询,然后进行混合搜索,再对结果进行精排,最后把上下文喂给LLM"的复杂流程,我们就必须手动编写大量的**"胶水代码"**来连接每一步。

这种手动操作,就像在没有图纸和标准卡扣的情况下,用胶水去粘乐高积木。它不仅繁琐、易错,而且每当你想更换一个零件(比如换个LLM模型),就得把粘好的结构拆开重做,毫无效率可言。

我们需要一个"乐高底板"和一套"标准接口",来帮我们轻松地编排和管理这一切。这个"底板",就是大名鼎鼎的AI应用开发框架------LangChain

LangChain是什么?为什么需要它?

LangChain是一个开源框架,旨在简化由大型语言模型(LLM)驱动的应用程序的开发。它不是要取代我们学过的任何一个组件,而是要成为它们的**"指挥家""编排者"**。

它的核心价值在于:

  1. 标准化:为LLM、向量数据库、检索器等各种组件提供了统一的、标准化的接口。
  2. 可组合性:提供了一种优雅的方式,将这些标准化的组件像链条一样"链接"起来,形成复杂的工作流。
  3. 生态丰富:集成了市面上几乎所有主流的模型、工具和数据库。

而实现这一切的现代核心,就是LangChain表达式语言(LCEL) ,它允许我们用一种极其简洁、类似管道符 | 的语法来构建"链(Chain)"。

上手实战:用LCEL构建"检索-精排-生成"链

我们将用LCEL来构建一个包含检索、精排、生成三个步骤的高级RAG流程,让你亲身感受它的魅力。

首先,确保你安装了所有相关的库:

bash 复制代码
pip install langchain langchain-openai langchain_community chromadb sentence-transformers
1. 准备标准化的组件

在LangChain中,我们需要将之前的工具都用LangChain的"包装器"来初始化。

python 复制代码
import os
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_community.cross_encoders import BgeReranker
from langchain.retrievers.contextual_compression import ContextualCompressionRetriever
from langchain.prompts import ChatPromptTemplate
from langchain.schema.runnable import RunnablePassthrough
from langchain.schema.output_parser import StrOutputParser

# 设置你的OpenAI API Key
# os.environ["OPENAI_API_KEY"] = "sk-..."

# 准备一些示例文档
docs = [
    "LangChain是一个强大的AI编排框架。",
    "RAG系统的关键在于检索的质量。",
    "BgeReranker是一个性能优秀的精排模型。",
    "LCEL是LangChain中用于构建链的表达式语言。"
]

# --- 组件1: 向量库与检索器 ---
# 使用OpenAI的Embedding模型
embeddings = OpenAIEmbeddings()
# 使用Chroma向量库
vectorstore = Chroma.from_texts(docs, embeddings)
# 基础检索器
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 3})

# --- 组件2: 精排器 ---
# 初始化BgeReranker
reranker = BgeReranker()
# 将精排器包装成一个ContextualCompressionRetriever
# 这个检索器会自动用reranker对base_retriever的结果进行精排
compression_retriever = ContextualCompressionRetriever(
    base_compressor=reranker, base_retriever=base_retriever
)

# --- 组件3: Prompt, LLM, 和输出解析器 ---
prompt_template = ChatPromptTemplate.from_template(
    """
    请根据以下上下文回答问题:
    ---
    上下文:
    {context}
    ---
    问题: {question}
    """
)
llm = ChatOpenAI(model="gpt-4o-mini")
output_parser = StrOutputParser()
2. 使用LCEL组合"链"

接下来是见证奇迹的时刻!我们将用 | 符号把所有组件串联起来。

python 复制代码
# LCEL的魔法!
# 这是一个字典,定义了进入Prompt的数据流
# context由精排检索器处理用户问题后提供
# question则直接来源于用户的原始输入
setup_and_retrieval = {
    "context": compression_retriever,
    "question": RunnablePassthrough() 
}

# 构建完整的链
chain = (
    setup_and_retrieval
    | prompt_template
    | llm
    | output_parser
)

解读这条链:

  1. setup_and_retrieval 负责准备数据。当它运行时,它会并行地:
    • compression_retriever 处理输入,得到精排后的上下文 context
    • RunnablePassthrough() 将原始输入(也就是用户问题)原封不动地传递下去,作为 question
  2. | prompt_template:上一步产生的字典 {"context": ..., "question": ...} 被送入Prompt模板,格式化成一个完整的Prompt。
  3. | llm:格式化好的Prompt被送入LLM。
  4. | output_parser:LLM的输出被送入输出解析器,提取出干净的字符串结果。
3. 调用链并查看结果
python 复制代码
# 只需一个invoke,即可驱动整条复杂的链
response = chain.invoke("LangChain的LCEL是什么?")
print(response)

输出结果(示例):

根据提供的上下文,LCEL是LangChain中用于构建链的表达式语言。

看到没有?我们用区区几行声明式的代码,就定义了一条包含并行处理、检索、精排、Prompt格式化、LLM调用、结果解析等多个步骤的复杂工作流。这背后所有的"胶水代码",LangChain都为我们处理了。这就是框架的力量!

总结与预告

今日小结:

  • 手动编写"胶水代码"来连接RAG的各个组件,是低效且难以维护的。
  • LangChain 等编排框架通过标准化接口可组合性,极大地简化了高级RAG流程的构建。
  • LCEL(LangChain表达式语言) 及其 | 语法,是当前构建和理解LangChain工作流最核心、最优雅的方式。

LangChain以其灵活性和强大的生态,成为了构建LLM应用的事实标准之一。但它并非唯一的选择。在RAG这个垂直领域,还有一个可以与LangChain分庭抗礼的强大对手。

这个框架认为,RAG的核心是"数据",一切都应该围绕如何更好地索引、组织和查询数据来构建。

明天预告:RAG 每日一技(十五):换个"引擎"看世界------以数据为中心的LlamaIndex

明天,我们将把目光投向RAG领域的另一位巨头:LlamaIndex。我们将探索它与LangChain在设计哲学上的不同,看看它以"数据为中心"的理念,为我们构建RAG系统提供了怎样的一条新路径。

相关推荐
bcbnb6 分钟前
怎么在 Windows 上架 iOS APP?签名 + 发布一文全懂
后端
AI决策者洞察8 分钟前
AI正在“掏空”我们的脑子,但方式超出你的想象——慢慢学AI160
人工智能
机器学习之心11 分钟前
单变量单步时序预测:CNN-GRU卷积神经网络结合门控循环单元
人工智能·cnn·gru·cnn-gru
终将超越过去14 分钟前
机器学习-聚类
人工智能·机器学习
掘金一周14 分钟前
只有 7 KB!前端圈疯传的 Vue3 转场动效神库!效果炸裂! | 掘金一周 8.7
前端·后端·ai编程
枣伊吕波19 分钟前
十一、请求响应-请求:简单参数和实体参数(简单实体参数与复杂实体参数)
java·spring boot·后端
苇柠19 分钟前
SpringMVC基础
java·后端·spring
白白白鲤鱼22 分钟前
Vue2项目—基于路由守卫实现钉钉小程序动态更新标题
服务器·前端·spring boot·后端·职场和发展·小程序·钉钉
Sunlightʊə27 分钟前
05.LinkedList与链表
java·数据结构·算法·链表
qq_5139704432 分钟前
力扣 hot100 Day67
算法·leetcode·职场和发展