当前位置: 首页 > article >正文

《AI Agent智能应用从0到1定制开发》学习笔记:使用RAG技术增强大模型能力,实现与各种文档的对话

思维导图

在这里插入图片描述

📚 引言

大型语言模型(如ChatGPT)虽然功能强大,但它们存在一些明显的局限性。这些模型的知识库更新较慢,无法实时学习最新内容,而且对私有数据或特定领域的专业知识了解有限。例如,ChatGPT的知识截止到特定时间点,无法感知用户本地电脑或内网中的数据。这就是为什么当我们询问非常具体或专业的内容时,它的回答可能显得泛泛而谈。

那么,如何让大模型变得更"聪明",能够获取最新知识并回答更专业的问题呢?这就是本文要介绍的RAG(检索增强生成)技术。

🔍 RAG技术概述

什么是RAG?

RAG(Retrieval-Augmented Generation,检索增强生成)是一种结合检索和生成两种技术的方法,旨在帮助计算机更好地理解和回答问题。简单来说,它让AI模型能够在回答问题前先"查阅资料",从而提供更准确、更专业的回答。

RAG的基本流程

在这里插入图片描述

RAG技术的典型流程包括:

  1. 加载数据(Loader):从各种来源加载文档数据
  2. 处理文档(Transform):对文档进行切割、整理等处理
  3. 向量化(Embedding):将文本转换为向量表示
  4. 存储(Store):将向量数据存储在向量数据库中
  5. 检索(Retrieve):根据问题检索相关文档片段
  6. 生成回答(Generate):基于检索结果生成答案

🔧 LangChain中的RAG实现

Loader:让大模型具备实时学习的能力

LangChain包装了各种Loader,使大模型能够加载各种格式的文档:

  • CSV Loader:加载表格数据
  • Directory Loader:加载整个目录的文件
  • HTML Loader:加载网页内容
  • JSON Loader:加载JSON格式数据
  • Markdown Loader:加载Markdown文档
  • PDF Loader:加载PDF文件

除此之外,LangChain还支持超过100种不同的数据源接口,包括B站、YouTube、GitHub等平台的数据。

# 加载Markdown文件示例
from langchain.document_loaders import UnstructuredMarkdownLoader

loader = UnstructuredMarkdownLoader("path/to/file.md")
data = loader.load()

# 加载CSV文件示例
from langchain.document_loaders import CSVLoader

loader = CSVLoader("path/to/file.csv")
data = loader.load()

# 加载目录中的文件
from langchain.document_loaders import DirectoryLoader

loader = DirectoryLoader("path/to/directory", glob="**/*.pdf")
data = loader.load()

文档转换:切割、总结和翻译

加载文档后,通常需要对其进行处理,以便更好地利用文档内容:

文档切割

文档切割的目的是:

  1. 降低成本,适应大模型的上下文窗口限制
  2. 将文档转换为结构化数据,便于查询

LangChain提供了多种切割器:

# 字符串分割
from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(
    separator="\n\n",
    chunk_size=1000,
    chunk_overlap=200,
    length_function=len
)
docs = text_splitter.split_documents(documents)

# 按代码分割
from langchain.text_splitter import RecursiveCharacterTextSplitter

python_splitter = RecursiveCharacterTextSplitter.from_language(
    language="python",
    chunk_size=1000,
    chunk_overlap=200
)
文档总结、精炼和翻译

LangChain还提供了文档总结、精炼和翻译的功能:

# 文档总结
from langchain.chains.summarize import load_summarize_chain
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(temperature=0)
chain = load_summarize_chain(llm, chain_type="map_reduce")
summary = chain.run(docs)

# 文档翻译
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate

template = """Translate the following text from {source_language} to {target_language}:
{text}"""

prompt = PromptTemplate(
    input_variables=["source_language", "target_language", "text"],
    template=template
)

chain = LLMChain(llm=llm, prompt=prompt)
translated = chain.run(source_language="English", target_language="Chinese", text=text)

解决"Lost in the Middle"问题

研究表明,当处理长文本时,检索性能会出现"迷失在中间"的现象:当相关信息位于输入上下文的开头或结尾时,模型表现最佳;而当相关信息位于中间位置时,即使对于专门设计的上下文模型,性能也会显著下降。

解决方案包括:

  1. 通过检索找到相关文本块
  2. 对检索结果进行重新排序,将最相关的内容放在开头和结尾
  3. 使用排序后的文本进行生成
# 排序检索结果的示例代码
def reorder_results(results):
    # 按相关性排序
    sorted_results = sorted(results, key=lambda x: x.score, reverse=True)
    
    # 重新排列,将高相关度的放在头尾
    reordered = []
    for i in range(len(sorted_results)):
        if i % 2 == 0:
            reordered.append(sorted_results[i//2])
        else:
            reordered.append(sorted_results[len(sorted_results) - 1 - i//2])
    
    return reordered

文本向量化

为什么需要文本向量化?主要有两个原因:

  1. 纯文本形式难以进行语义相关性搜索
  2. 文本处理效率低且占用空间大

文本向量化(Embedding)将文本转换为高维向量空间中的点,使得语义相似的文本在向量空间中距离更近。

# 使用OpenAI的文本嵌入模型
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()
query_vector = embeddings.embed_query("How does RAG work?")

# 使用HuggingFace的文本嵌入模型
from langchain_huggingface import HuggingFaceEmbeddings

embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-mpnet-base-v2")

向量数据库:存储和检索

向量数据库是RAG技术中不可或缺的组成部分,它能够高效地存储和检索高维向量数据。向量数据库的主要作用包括:

  1. 管理数据:以原始形式有效管理数据
  2. 存储向量:存储AI需要的高维数据
  3. 高效检索:快速找到语义相似的内容

市面上有多种向量数据库可供选择:

  • Milvus:开源向量数据库,功能完善,性能强大
  • Pinecone:云原生向量数据库,易于使用
  • Chroma:轻量级向量数据库,适合本地开发
  • FAISS:Facebook AI开发的相似性搜索库
  • Qdrant:开源向量搜索引擎
# 使用Chroma向量数据库
from langchain_community.vectorstores import Chroma

vectordb = Chroma.from_documents(
    documents=docs,
    embedding=embeddings,
    persist_directory="./chroma_db"
)

# 检索相似文档
docs = vectordb.similarity_search("What is RAG technology?", k=3)

🛠️ 实战项目:ChatDoc智能文档助手

下面,我们将学习如何构建一个名为ChatDoc的智能文档助手,它能够:

  1. 加载PDF、DOCX或Excel等格式的文档
  2. 对文档进行适当切分
  3. 使用OpenAI进行向量化
  4. 使用Chroma DB实现本地向量存储
  5. 实现与文档的对话功能

实现步骤

1. 初始化ChatDoc类
import os
from langchain.document_loaders import PDFMinerLoader, CSVLoader
from langchain.document_loaders import Docx2txtLoader, UnstructuredExcelLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA

class ChatDoc:
    def __init__(self):
        self.file_path = None
        self.file_content = None
        self.db = None
        self.embeddings = OpenAIEmbeddings()
        self.llm = ChatOpenAI(temperature=0)
2. 实现文件加载功能
def get_file(self, file_path):
    self.file_path = file_path
    _, file_extension = os.path.splitext(file_path)
    
    try:
        if file_extension == '.pdf':
            loader = PDFMinerLoader(file_path)
        elif file_extension == '.csv':
            loader = CSVLoader(file_path)
        elif file_extension == '.docx':
            loader = Docx2txtLoader(file_path)
        elif file_extension == '.xlsx' or file_extension == '.xls':
            loader = UnstructuredExcelLoader(file_path)
        else:
            print(f"不支持的文件格式: {file_extension}")
            return None
        
        self.file_content = loader.load()
        print(f"文件 {file_path} 加载成功!")
        return self.file_content
    except Exception as e:
        print(f"加载文件时出错: {e}")
        return None
3. 文本切分与向量化
def separate_and_embed(self):
    if self.file_content is None:
        print("没有加载文件内容,请先加载文件。")
        return None
    
    # 文本切分
    text_splitter = CharacterTextSplitter(
        separator="\n",
        chunk_size=1000,
        chunk_overlap=200,
        length_function=len
    )
    chunks = text_splitter.split_documents(self.file_content)
    
    # 向量化存储
    self.db = Chroma.from_documents(
        documents=chunks,
        embedding=self.embeddings,
        persist_directory="./chroma_db"
    )
    
    print(f"文本已切分为 {len(chunks)} 个块并完成向量化存储。")
    return self.db
4. 实现基本问答功能
def ask_and_find(self, question):
    if self.db is None:
        print("向量数据库未初始化,请先处理文档。")
        return None
    
    # 检索相关文档
    docs = self.db.similarity_search(question, k=3)
    
    # 构建上下文
    context = "\n\n".join([doc.page_content for doc in docs])
    
    # 创建提示
    prompt = f"""
    根据以下信息回答问题:
    
    {context}
    
    问题: {question}
    """
    
    # 生成回答
    response = self.llm.predict(prompt)
    return response
5. 使用多重查询优化检索效果
from langchain.retrievers.multi_query import MultiQueryRetriever

def improved_ask_and_find(self, question):
    if self.db is None:
        print("向量数据库未初始化,请先处理文档。")
        return None
    
    # 创建多重查询检索器
    retriever = MultiQueryRetriever.from_llm(
        retriever=self.db.as_retriever(),
        llm=self.llm
    )
    
    # 进行检索
    docs = retriever.get_relevant_documents(question)
    
    # 构建上下文
    context = "\n\n".join([doc.page_content for doc in docs])
    
    # 创建提示
    prompt = f"""
    根据以下信息回答问题:
    
    {context}
    
    问题: {question}
    """
    
    # 生成回答
    response = self.llm.predict(prompt)
    return response
6. 实现对话功能
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain

def chat_with_doc(self):
    if self.db is None:
        print("向量数据库未初始化,请先处理文档。")
        return None
    
    # 创建对话记忆
    memory = ConversationBufferMemory(
        memory_key="chat_history",
        return_messages=True
    )
    
    # 创建对话链
    qa_chain = ConversationalRetrievalChain.from_llm(
        llm=self.llm,
        retriever=self.db.as_retriever(),
        memory=memory
    )
    
    # 开始对话
    print("开始与文档对话,输入'exit'退出")
    while True:
        question = input("你: ")
        if question.lower() == 'exit':
            break
        
        result = qa_chain({"question": question})
        print(f"AI: {result['answer']}")

使用示例

# 使用ChatDoc
chat_doc = ChatDoc()

# 加载文档
chat_doc.get_file("example.pdf")

# 处理文档
chat_doc.separate_and_embed()

# 问答示例
response = chat_doc.ask_and_find("这个文档主要讲了什么?")
print(response)

# 启动交互式对话
chat_doc.chat_with_doc()

📈 RAG技术的优化方法

多重查询优化

多重查询(Multi-query)通过扩展原始问题,从不同角度生成多个相关查询,然后综合检索结果。这种方法可以提高检索的召回率和准确性。

# 多重查询示例
from langchain.retrievers.multi_query import MultiQueryRetriever

retriever = MultiQueryRetriever.from_llm(
    retriever=vectordb.as_retriever(),
    llm=ChatOpenAI(temperature=0)
)

# 原始问题
question = "如何实现RAG技术?"

# 生成的多重查询可能包括:
# 1. "RAG技术的实现步骤是什么?"
# 2. "如何在项目中使用检索增强生成?"
# 3. "实现RAG需要哪些组件?"
# 4. "RAG的代码实现示例"

上下文压缩

上下文压缩(Context compression)通过筛选和压缩检索到的文档,确保只有最相关的内容被包含在上下文中,从而提高回答质量并降低成本。

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

# 创建压缩器
compressor = LLMChainExtractor.from_llm(llm)

# 创建压缩检索器
compression_retriever = ContextualCompressionRetriever(
    base_retriever=vectordb.as_retriever(),
    doc_compressor=compressor
)

# 使用压缩检索器获取文档
compressed_docs = compression_retriever.get_relevant_documents(query)

基于重排序的优化

针对"迷失在中间"问题,可以对检索结果进行重新排序,将最相关的文档放在上下文的开头和结尾:

def rerank_documents(docs, query):
    # 计算每个文档与查询的相关性分数
    # 这里可以使用更复杂的相关性评分方法
    scored_docs = [(doc, doc.metadata.get("score", 0)) for doc in docs]
    
    # 按分数排序
    sorted_docs = sorted(scored_docs, key=lambda x: x[1], reverse=True)
    
    # 重新排列文档,使最相关的在开头和结尾
    result = []
    for i in range(len(sorted_docs)):
        if i % 2 == 0 and i//2 < len(sorted_docs):
            result.append(sorted_docs[i//2][0])
        elif i//2 < len(sorted_docs) // 2:
            result.append(sorted_docs[len(sorted_docs) - 1 - i//2][0])
    
    return result

🌟 总结与展望

通过本文,我们深入学习了RAG技术及其在LangChain中的实现。RAG技术通过将检索与生成相结合,有效解决了大模型知识时效性和专业性不足的问题,使AI能够访问最新的、专业的、私有的信息。

我们详细探讨了RAG的实现步骤:

  1. 使用各种Loader加载文档
  2. 通过文本切割、总结等方法处理文档
  3. 将文本向量化并存储在向量数据库中
  4. 根据用户问题检索相关文档
  5. 基于检索结果生成回答

同时,我们还介绍了多重查询、上下文压缩和重排序等优化技术,以提高RAG系统的性能。

最后,我们通过构建ChatDoc智能文档助手,将学到的知识应用到实践中,实现了与任意文档对话的功能。

未来,随着大模型和向量数据库技术的不断发展,RAG技术将变得更加高效、准确,并在更多领域发挥作用。通过掌握RAG技术,我们可以充分发挥大模型的潜力,构建更智能、更专业的AI应用。

💡 小提示:如果你想要进一步提升RAG系统的性能,可以考虑:

  • 尝试不同的文本嵌入模型
  • 优化文档切割策略
  • 使用混合检索方法
  • 引入用户反馈机制

希望本文能够帮助你理解和应用RAG技术,构建属于自己的智能文档助手!


🔗 参考资料

  • AI Agent智能应用从0到1定制开发
  • LangChain官方文档:https://python.langchain.com/docs/
  • “Lost in the Middle: How Language Models Use Long Contexts” 研究论文
  • 向量数据库官方文档:Milvus、Chroma、Pinecone等

http://www.kler.cn/a/600245.html

相关文章:

  • CSS语言的双向链表
  • 网络运维学习笔记(DeepSeek优化版) 020 HCIA-Datacom新增知识点02 SDN与NFV概述
  • 6(六)Jmeter线程数分段加压
  • 基于Linux的多进程并发服务器设计与实现
  • RISC-V AIA学习2---IMSIC
  • docker pull时报错:https://registry-1.docker.io/v2/
  • tortoiseSVN、source insignt、J-flash使用
  • 【Hbase】列族版本问题
  • 星型拓扑网络原理、图传模块架构与路由NAT需求探讨
  • 游戏摇杆开发:利用 Windows API 实现摇杆输入捕获
  • Android第五次面试总结(HR面)
  • 解决Python docx无法修改wps文字表格背景问题
  • Django项目入门
  • MySQL表达式之公用表表达式(CTE)
  • 搭建Redis主从集群
  • .NET三层架构详解
  • Web纯前端实现在线打开编辑保存PPT幻灯片
  • CSS 中 letter-spacing 不支持百分比
  • rust学习笔记18-迭代器
  • 开源ORB_SLAM2项目编译常见问题与应对办法