Langchain 和 RAG 最佳实践

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
简介: 这是一篇关于LangChain和RAG的快速入门文章,主要参考了由Harrison Chase和Andrew Ng讲授的​​Langchain chat with your data​​​课程。你可以在​​rag101仓库​​​中查看完整代码。本文翻译自我的英文博客,最新修订内容可随时参考:​​LangChain 与 RAG 最佳实践​​。

这是一篇关于LangChain和RAG的快速入门文章,主要参考了由Harrison Chase和Andrew Ng讲授的Langchain chat with your data课程。你可以在rag101仓库中查看完整代码。本文翻译自我的英文博客,最新修订内容可随时参考:LangChain 与 RAG 最佳实践

LangChain与RAG最佳实践

简介

LangChain

LangChain是用于构建大语言模型(LLM)应用的开源开发框架,其组件如下:

提示(Prompt)

  • 提示模板(Prompt Templates):用于生成模型输入。
  • 输出解析器(Output Parsers):处理生成结果的实现。
  • 示例选择器(Example Selectors):选择合适的输入示例。

模型(Models)

  • 大语言模型(LLMs)
  • 聊天模型(Chat Models)
  • 文本嵌入模型(Text Embedding Models)

索引(Indexes)

  • 文档加载器(Document Loaders)
  • 文本分割器(Text Splitters)
  • 向量存储(Vector Stores)
  • 检索器(Retrievers)

链(Chains)

  • 可作为其他链的构建块。
  • 提供超过20种特定应用的链。

代理(Agents)

  • 支持5种代理帮助语言模型使用外部工具。
  • 代理工具包(Agent Toolkits):提供超过10种实现,代理通过特定工具执行任务。

RAG流程

整个RAG流程基于向量存储加载(Vector Store Loading)和检索增强生成(Retrieval-Augmented Generation)。

向量存储加载

从不同来源加载数据,拆分并将其转换为向量嵌入。

检索增强生成

  1. 用户输入查询(Query)后,系统将从向量存储中检索最相关的文档片段(Relevant Splits)。
  2. 检索到的相关片段将组合成一个提示(Prompt),该提示会与上下文一起传递给大语言模型(LLM)。
  3. 最后,语言模型将根据检索到的片段生成答案并返回给用户。

加载器(Loaders)

可以使用加载器处理不同种类和格式的数据。有些是公开的,有些是专有的;有些是结构化的,有些是非结构化的。

一些有用的库:

  • PDF:pypdf
  • YouTube音频:yt_dlp、pydub
  • 网页:beautifulsoup4

更多加载器可查看官方文档。完整代码可在这里查看。

PDF

现在进行实践:
首先安装库:

pip install langchain-community 
pip install pypdf

可以在以下代码中查看演示:

from langchain.document_loaders import PyPDFLoader

# 实际上,langchain调用pypdf库来加载pdf文件
loader = PyPDFLoader("ProbRandProc_Notes2004_JWBerkeley.pdf")
pages = loader.load()

print(type(pages))
# <class 'list'>
print(len(pages))
# 打印总页数

# 以第一页为例
page = pages[0]
print(type(page))
# <class 'langchain_core.documents.base.Document'>

# 页面内容包括:
# 1. page_content:页面文本内容
# 2. metadata:页面元数据

print(page.page_content[0:500])
print(page.metadata)

网页加载器(Web Base Loader)

同样先安装库:

pip install beautifulsoup4

WebBaseLoader基于beautifulsoup4库。

from langchain_community.document_loaders import WebBaseLoader

loader = WebBaseLoader("https://zh.d2l.ai/")
pages = loader.load()
print(pages[0].page_content[:500])

# 也可以使用json进行后处理
# import json
# convert_to_json = json.loads(pages[0].page_content)

分割器(Splitters)

将文档分割成更小的块,同时保留有意义的关系。

为何分割?

  • GPU限制:参数超过10亿的GPT模型,前向传播无法处理如此大的参数,因此分割是必要的。
  • 计算更高效。
  • 某些模型有固定的序列长度限制。
  • 更好的泛化能力。

然而,分割点可能会丢失一些信息,因此分割时应考虑语义。

分割器类型

  • 字符文本分割器(CharacterTextSplitter)
  • Markdown标题文本分割器(MarkdownHeaderTextSplitter)
  • Token文本分割器(TokenTextsplitter)
  • SentenceTransformersTokenTextSplitter
  • 递归字符文本分割器(RecursiveCharacterTextSplitter):递归尝试按不同字符分割,找到可行的分割方式。
  • 语言分割器(Language):适用于C++、Python、Ruby、Markdown等语言
  • NLTK文本分割器(NLTKTextSplitter):使用NLTK(自然语言工具包)分割句子
  • Spacy文本分割器(SpacyTextSplitter):使用Spacy分割句子

更多信息请查看文档

字符文本分割器与递归字符文本分割器示例

完整代码可在这里查看。

from langchain.text_splitter import RecursiveCharacterTextSplitter, CharacterTextSplitter

example_text = """撰写文档时,作者会使用文档结构对内容进行分组。这可以向读者传达哪些想法是相关的。例如,紧密相关的想法在句子中。相似的想法在段落中。段落构成文档。\n\n 段落通常用一个或两个回车符分隔。回车符就是你在这个字符串中看到的“反斜杠n”。句子以句号结尾,但同时也有空格。单词之间用空格分隔。"""

c_splitter = CharacterTextSplitter(
    chunk_size=450,  # 块大小
    chunk_overlap=0,  # 块重叠部分,可与前一个块共享
    separator=' '
)
r_splitter = RecursiveCharacterTextSplitter(
    chunk_size=450,
    chunk_overlap=0, 
    separators=["\n\n", "\n", " ", ""]  # 分隔符优先级
)

print(c_splitter.split_text(example_text))
# 按450个字符分割
print(r_splitter.split_text(example_text))
# 先按\n\n分割

向量存储与嵌入(Vectorstores and Embeddings)

回顾RAG流程:

优势:

  1. 提高查询的准确性:查询相似块时,准确性更高。
  2. 提高查询效率:查询相似块时减少计算量。
  3. 提高查询的覆盖范围:块可以覆盖文档的每个点。
  4. 便于嵌入处理。

嵌入(Embeddings)


如果两个句子含义相似,那么它们在高维语义空间中会更接近。

向量存储(Vector Stores)

将每个块存储在向量存储中。当用户查询时,查询会被嵌入,然后找到最相似的向量,即这些块的索引,然后返回这些块。


实践

嵌入

完整代码可在这里查看。
首先安装库:
chromadb是一个轻量级向量数据库。

pip install chromadb

我们需要一个好的嵌入模型,你可以选择你喜欢的。参考文档
这里我使用ZhipuAIEmbeddings,因此需要安装库:

pip install zhipuai

测试代码如下:

from langchain_community.embeddings import ZhipuAIEmbeddings

embed = ZhipuAIEmbeddings(
    model="embedding-3",
    api_key="输入你自己的api key"
)

input_texts = ["这是一个测试查询1。", "这是一个测试查询2。"]
print(embed.embed_documents(input_texts))

向量存储

完整代码可在这里查看。

pip install langchain-chroma

然后我们可以使用Chroma来存储嵌入。

from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import ZhipuAIEmbeddings

# 加载网页
loader = WebBaseLoader("https://en.d2l.ai/")
docs = loader.load()

# 分割文本为块
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1500,
    chunk_overlap=150
)
splits = text_splitter.split_documents(docs)
# print(len(splits))

# 设置嵌入模型
embeddings = ZhipuAIEmbeddings(
    model="embedding-3",
    api_key="你的api key"
)

# 设置持久化目录
persist_directory = r'.'

# 创建向量数据库
vectordb = Chroma.from_documents(
    documents=splits,
    embedding=embeddings,
    persist_directory=persist_directory
)
# print(vectordb._collection.count())

# 查询向量数据库
question = "Recurrent"
docs = vectordb.similarity_search(question, k=3)
# print(len(docs))
print(docs[0].page_content)

然后你可以在特定目录中找到chroma.sqlite3文件。

检索(Retrieval)

这部分是RAG的核心部分。

上一部分我们已经使用了similarity_search方法。除此之外,我们还有其他方法:

  • 基本语义相似性(Basic semantic similarity)
  • 最大边际相关(Maximum Marginal Relevance, MMR)
  • 元数据(Metadata)
  • LLM辅助检索(LLM Aided Retrieval)

相似性搜索(Similarity Search)

相似性搜索计算查询向量与数据库中所有文档向量的相似性,以找到最相关的文档。相似性度量方法包括余弦相似度欧氏距离,它们可以有效度量两个向量在高维空间中的接近程度。

然而,仅依赖相似性搜索可能导致多样性不足,因为它只关注查询与内容的匹配,忽略了不同信息之间的差异。在某些应用中,尤其是需要覆盖多个不同方面的信息时,最大边际相关(MMR)的扩展方法可以更好地平衡相关性和多样性。

实践

实践部分见上一部分。

最大边际相关(MMR, Maximum Marginal Relevance)

仅检索最相关的文档可能会忽略信息的多样性。例如,如果只选择最相似的响应,结果可能非常相似甚至包含重复内容。MMR的核心思想是平衡相关性和多样性,即选择与查询最相关的信息,同时确保信息在内容上具有多样性。通过减少不同片段之间的信息重复,MMR可以提供更全面和多样化的结果集。

MMR的流程如下:

  1. 查询向量存储:首先使用嵌入模型将查询转换为向量。
  2. 选择fetch_k个最相似的响应:从向量存储中找到前k个最相似的向量。
  3. 在这些响应中选择k个最具多样性的:通过计算每个响应之间的相似性,MMR会更倾向于选择彼此差异更大的结果,从而增加信息的覆盖范围。这个过程确保返回的结果不仅“最相似”,而且“互补”。

关键参数是lambda,它是相关性和多样性的权重:

  • 当lambda接近1时,MMR更像相似性搜索。
  • 当lambda接近0时,MMR更像随机搜索。

实践

我们可以调整“向量存储”部分的代码以使用MMR方法。完整代码在retrieval/mmr.py文件中。

# 使用MMR查询向量数据库
question = "How the neural network works?"
# 获取8个最相似的文档,然后选择2个最相关且最具多样性的文档
docs_mmr = vectordb.max_marginal_relevance_search(question, fetch_k=8, k=2)
print(docs_mmr[0].page_content[:100])
print(docs_mmr[1].page_content[:100])

元数据(Metadata)

当我们的查询有特定条件时,可以使用元数据来过滤结果。例如,页码、作者、时间戳等信息,这些信息可以在检索时作为过滤条件,从而提高查询的准确性。

实践

完整代码可在这里查看。
从另一个网站添加新文档,然后过滤特定网站的结果。

from langchain_chroma import Chroma
from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import ZhipuAIEmbeddings

# 加载网页
loader = WebBaseLoader("https://en.d2l.ai/")
docs = loader.load()

# 分割文本为块
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=1500,
    chunk_overlap=150
)
splits = text_splitter.split_documents(docs)
# print(len(splits))

# 设置嵌入模型
embeddings = ZhipuAIEmbeddings(
    model="embedding-3",
    api_key="你的_api_key"
)

# 设置持久化目录
persist_directory = r'.'

# 创建向量数据库
vectordb = Chroma.from_documents(
    documents=splits,
    embedding=embeddings,
    persist_directory=persist_directory
)
# print(vectordb._collection.count())

# 从另一个网站添加新文档
new_loader = WebBaseLoader("https://www.deeplearning.ai/")
new_docs = new_loader.load()

# 分割新文档文本为块
new_splits = text_splitter.split_documents(new_docs)

# 添加到现有向量数据库
vectordb.add_documents(new_splits)

# 获取所有文档
all_docs = vectordb.similarity_search("What is the difference between a neural network and a deep learning model?", k=20)

# 打印文档的元数据
for i, doc in enumerate(all_docs):
    print(f"Document {i+1} metadata: {doc.metadata}")
# Document 1 metadata: {'language': 'en', 'source': 'https://en.d2l.ai/', 'title': 'Dive into Deep Learning — Dive into Deep Learning 1.0.3 documentation'}
# Document 2 metadata: {'language': 'en', 'source': 'https://en.d2l.ai/', 'title': 'Dive into Deep Learning — D


### LLM辅助检索(LLM Aided Retrieval)  
该方法利用语言模型自动解析句子语义,提取过滤信息。  

#### SelfQueryRetriever  
LangChain提供的`SelfQueryRetriever`模块可通过语言模型分析问题语义,提取**向量搜索的搜索词****文档元数据的过滤条件**- 例如,对于问题“除了维基百科,还有哪些健康网站?”,SelfQueryRetriever可推断“维基百科”为过滤条件,即排除来源为维基百科的文档。  

#### 实践  
```python  
from langchain.llms import OpenAI  
from langchain.retrievers.self_query.base import SelfQueryRetriever  
from langchain.chains.query_constructor.base import AttributeInfo  

llm = OpenAI(temperature=0)  

metadata_field_info = [  
    AttributeInfo(  
        name="source",  
        description="文档片段的来源,应为`docs/loaders.pdf`、`docs/text_splitters.pdf`或`docs/vectorstores.pdf`中的一个",  
        type="string",  
    ),  
    AttributeInfo(  
        name="page",  
        description="文档所在的页码",  
        type="integer",  
    ),  
]  

document_content_description = "检索增强生成相关的课程讲义"  
retriever = SelfQueryRetriever.from_llm(  
    llm,  
    vectordb,  
    document_content_description,  
    metadata_field_info,  
    verbose=True  
)  

question = "第二讲的主题是什么?"

压缩(Compression)

向量检索返回的完整文档片段可能包含大量冗余信息,LangChain的“压缩”机制通过以下步骤优化:

  1. 标准向量检索获取候选文档。
  2. 使用语言模型基于查询语义压缩文档,仅保留相关部分。
    • 例如,查询“蘑菇的营养价值”时,压缩后仅保留与“营养价值”相关的句子。

实践

from langchain.retrievers import ContextualCompressionRetriever  
from langchain.retrievers.document_compressors import LLMChainExtractor  

def pretty_print_docs(docs):  
    print(f"\n{'-' * 100}\n".join([f"Document {i+1}:\n\n" + d.page_content for i, d in enumerate(docs)]))  

llm = OpenAI(temperature=0)  
compressor = LLMChainExtractor.from_llm(llm)  
compression_retriever = ContextualCompressionRetriever(  
    base_compressor=compressor,  
    base_retriever=vectordb.as_retriever()  
)  
question = "第二讲的主题是什么?"  
compressed_docs = compression_retriever.get_relevant_documents(question)  
pretty_print_docs(compressed_docs)

问答(Question Answering)

流程

  1. 从向量存储中检索多个相关文档。
  2. 压缩相关片段以适应LLM上下文,生成系统提示(System Prompt)并整合用户问题。
  3. 将信息传递给LLM生成答案。

RetrievalQA链

优势

  • 提高答案准确性:结合检索结果与LLM生成能力。
  • 支持知识库实时更新:依赖向量存储数据,可动态更新。
  • 减轻模型记忆负担:通过外部知识减少对模型内部参数的依赖。

其他方法

  • Map-Reduce:将文档分块,每个块独立生成答案后合并(适合大量文档并行处理)。
  • Refine:基于首个块生成初始答案,后续块逐步优化(适合高质量答案生成)。
  • Map-Rerank:分块生成答案后按相关性排序,取最高分结果(适合精准匹配场景)。

实践

完整代码见这里

from langchain_community.chat_models import ChatZhipuAI  
from langchain.chains import RetrievalQA  
from langchain.prompts import PromptTemplate  

loader = WebBaseLoader("https://en.d2l.ai/")  
docs = loader.load()  
splits = RecursiveCharacterTextSplitter(chunk_size=1500).split_documents(docs)  
vectordb = Chroma.from_documents(splits, ZhipuAIEmbeddings(api_key="your_key"))  

chat = ChatZhipuAI(model="glm-4-flash", api_key="your_key")  
template = """基于以下上下文回答问题:\n{context}\n问题:{question}\n答案:"""  
qa_chain = RetrievalQA.from_chain_type(  
    chat,  
    retriever=vectordb.as_retriever(),  
    chain_type_kwargs={
   "prompt": PromptTemplate.from_template(template)}  
)  
result = qa_chain({
   "query": "这本书的主题是什么?"})  
print(result["result"])

对话检索链(Conversational Retrieval Chain)

流程

结合对话历史(Memory)与检索能力,实现上下文感知的多轮交互:

  1. 对话历史:记录用户对话上下文。
  2. 问题:用户提问传递给检索模块。
  3. 检索器:从向量存储获取相关内容。
  4. 提示整合:将问题、检索结果与对话历史组合为LLM输入。
  5. LLM生成答案:基于完整上下文生成响应。

记忆模块(Memory)

ConversationBufferMemory存储对话历史,支持多轮交互:

from langchain.memory import ConversationBufferMemory  
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)

实践

完整代码见这里

qa = ConversationalRetrievalChain.from_llm(chat, vectordb.as_retriever(), memory=memory)  
# 第一轮提问  
result = qa.invoke({
   "question": "推荐一本深度学习书籍"})  
print(result['answer'])  
# 第二轮追问  
result = qa.invoke({
   "question": "这本书的作者是谁?"})  # 自动关联历史对话  
print(result['answer'])

最佳实践总结

  1. 数据预处理
    • 使用RecursiveCharacterTextSplitter保留语义,避免硬性切割。
    • 代码文档采用LanguageTextSplitter按语法结构分割。
  2. 向量存储选择
    • 小规模数据:Chroma(本地部署便捷)。
    • 大规模数据:FAISS(高效检索)或Pinecone(云端扩展)。
  3. 检索策略优化
    • 复杂查询结合MMR与元数据过滤,平衡相关性与多样性。
    • 使用SelfQueryRetriever自动解析用户意图,减少手动配置成本。
  4. 对话系统设计
    • 采用ConversationalRetrievalChain维护对话历史,提升交互连贯性。
    • 限制记忆长度(如ConversationTokenBufferMemory),避免上下文膨胀。

资源链接

通过合理组合LangChain组件与RAG流程,可高效构建基于自定义知识库的智能问答系统,显著提升LLM在垂直领域的应用效果。

相关实践学习
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
目录
相关文章
|
数据库
Langchain中改进RAG能力的3种常用的扩展查询方法
有多种方法可以提高检索增强生成(RAG)的能力,其中一种方法称为查询扩展。我们这里主要介绍在Langchain中常用的3种方法
719 0
|
4月前
|
数据采集 存储 人工智能
智能体(AI Agent)开发实战之【LangChain】(二)结合大模型基于RAG实现本地知识库问答
智能体(AI Agent)开发实战之【LangChain】(二)结合大模型基于RAG实现本地知识库问答
|
6月前
|
存储 人工智能 自然语言处理
LangChain RAG入门教程:构建基于私有文档的智能问答助手
本文介绍如何利用检索增强生成(RAG)技术与LangChain框架构建基于特定文档集合的AI问答系统。通过结合检索系统和生成机制,RAG能有效降低传统语言模型的知识局限与幻觉问题,提升回答准确性。文章详细展示了从环境配置、知识库构建到系统集成的全流程,并提供优化策略以改进检索与响应质量。此技术适用于专业领域信息检索与生成,为定制化AI应用奠定了基础。
1393 5
LangChain RAG入门教程:构建基于私有文档的智能问答助手
|
12月前
|
存储 人工智能 搜索推荐
解锁AI新境界:LangChain+RAG实战秘籍,让你的企业决策更智能,引领商业未来新潮流!
【10月更文挑战第4天】本文通过详细的实战演练,指导读者如何在LangChain框架中集成检索增强生成(RAG)技术,以提升大型语言模型的准确性与可靠性。RAG通过整合外部知识源,已在生成式AI领域展现出巨大潜力。文中提供了从数据加载到创建检索器的完整步骤,并探讨了RAG在企业问答系统、决策支持及客户服务中的应用。通过构建知识库、选择合适的嵌入模型及持续优化系统,企业可以充分利用现有数据,实现高效的商业落地。
416 6
|
12月前
|
机器学习/深度学习 人工智能 开发框架
解锁AI新纪元:LangChain保姆级RAG实战,助你抢占大模型发展趋势红利,共赴智能未来之旅!
【10月更文挑战第4天】本文详细介绍检索增强生成(RAG)技术的发展趋势及其在大型语言模型(LLM)中的应用优势,如知识丰富性、上下文理解和可解释性。通过LangChain框架进行实战演练,演示从知识库加载、文档分割、向量化到构建检索器的全过程,并提供示例代码。掌握RAG技术有助于企业在问答系统、文本生成等领域把握大模型的红利期,应对检索效率和模型融合等挑战。
570 14
|
12月前
|
存储 人工智能 搜索推荐
揭秘LangChain+RAG如何重塑行业未来?保姆级实战演练,解锁大模型在各领域应用场景的神秘面纱!
【10月更文挑战第4天】随着AI技术的发展,大型语言模型在各行各业的应用愈发广泛,检索增强生成(RAG)技术成为推动企业智能化转型的关键。本文通过实战演练,展示了如何在LangChain框架内实施RAG技术,涵盖金融(智能风控与投资决策)、医疗(辅助诊断与病历分析)及教育(个性化学习推荐与智能答疑)三大领域。通过具体示例和部署方案,如整合金融数据、医疗信息以及学生学习资料,并利用RAG技术生成精准报告、诊断建议及个性化学习计划,为企业提供了切实可行的智能化解决方案。
458 5
|
12月前
|
存储 搜索推荐 数据库
运用LangChain赋能企业规章制度制定:深入解析Retrieval-Augmented Generation(RAG)技术如何革新内部管理文件起草流程,实现高效合规与个性化定制的完美结合——实战指南与代码示例全面呈现
【10月更文挑战第3天】构建公司规章制度时,需融合业务实际与管理理论,制定合规且促发展的规则体系。尤其在数字化转型背景下,利用LangChain框架中的RAG技术,可提升规章制定效率与质量。通过Chroma向量数据库存储规章制度文本,并使用OpenAI Embeddings处理文本向量化,将现有文档转换后插入数据库。基于此,构建RAG生成器,根据输入问题检索信息并生成规章制度草案,加快更新速度并确保内容准确,灵活应对法律与业务变化,提高管理效率。此方法结合了先进的人工智能技术,展现了未来规章制度制定的新方向。
435 3
|
12月前
LangChain-06 RAG With Source Doc 通过文档进行检索增强
LangChain-06 RAG With Source Doc 通过文档进行检索增强
95 3
|
12月前
|
存储 自然语言处理
LangChain-04 RAG Retrieval-Augmented Generation 检索增强生成
LangChain-04 RAG Retrieval-Augmented Generation 检索增强生成
149 3
|
12月前
LangChain-05 RAG Conversational 增强检索会话
LangChain-05 RAG Conversational 增强检索会话
93 2

热门文章

最新文章