前情回顾
欢迎回来!经过十三天的学习,我们已经不再是RAG领域的新手。我们的武器库里装满了各种精良的武器:ChromaDB向量库、BM25关键词搜索、BGE-ReRanker精排器、HyDE查询生成器......
但一个严峻的现实摆在面前:这些武器都是独立存在的。如果我们想构建一个"先用HyDE改写查询,然后进行混合搜索,再对结果进行精排,最后把上下文喂给LLM"的复杂流程,我们就必须手动编写大量的**"胶水代码"**来连接每一步。
这种手动操作,就像在没有图纸和标准卡扣的情况下,用胶水去粘乐高积木。它不仅繁琐、易错,而且每当你想更换一个零件(比如换个LLM模型),就得把粘好的结构拆开重做,毫无效率可言。
我们需要一个"乐高底板"和一套"标准接口",来帮我们轻松地编排和管理这一切。这个"底板",就是大名鼎鼎的AI应用开发框架------LangChain。
LangChain是什么?为什么需要它?
LangChain是一个开源框架,旨在简化由大型语言模型(LLM)驱动的应用程序的开发。它不是要取代我们学过的任何一个组件,而是要成为它们的**"指挥家"或"编排者"**。
它的核心价值在于:
- 标准化:为LLM、向量数据库、检索器等各种组件提供了统一的、标准化的接口。
- 可组合性:提供了一种优雅的方式,将这些标准化的组件像链条一样"链接"起来,形成复杂的工作流。
- 生态丰富:集成了市面上几乎所有主流的模型、工具和数据库。
而实现这一切的现代核心,就是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
)
解读这条链:
setup_and_retrieval
负责准备数据。当它运行时,它会并行地:- 用
compression_retriever
处理输入,得到精排后的上下文context
。 - 用
RunnablePassthrough()
将原始输入(也就是用户问题)原封不动地传递下去,作为question
。
- 用
| prompt_template
:上一步产生的字典{"context": ..., "question": ...}
被送入Prompt模板,格式化成一个完整的Prompt。| llm
:格式化好的Prompt被送入LLM。| 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系统提供了怎样的一条新路径。