(2025)使用langchain实现朴素与进阶RAG

本文将带你用langchain实现朴素RAG与进阶RAG

朴素RAG

朴素RAG介绍

朴素RAG的工作流程相对简单直接:

  1. 用户查询:用户提出问题
  2. 向量化:将用户问题转换为向量表示
  3. 向量检索:在向量数据库中检索相关内容
  4. 增强生成:将检索结果与用户问题一起输入大模型
  5. 生成答案:大模型生成最终回答并返回给用户

代码实现

环境准备
本机Ollama需要下载了模型,若未下载使用ollama run deepseek-r1:7b

# 安装必要依赖
pip install langchain langchain-community chromadb  beautifulsoup4  sentence-transformers langchain-ollama

一个RAG分为一下5个部分

  1. 加载: 通过 document_loaders 完成数据加载
  2. 分割: text_splitter 将大型文档分割成更小的块,便于索引和模型处理
  3. 存储: 使用 vectorstores embeddings 模型存储和索引分割的内容
  4. 检索: 使用 RetrievalQA 基于用户输入,使用检索器从存储中检索相关分割
  5. 生成: llms 使用包含问题和检索数据的提示生成答案
from langchain.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
from langchain.llms import Ollama
import os
os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'


loader = WebBaseLoader("https://blog.youkuaiyun.com/ngadminq/article/details/147687050")
documents = loader.load()

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50
)
chunks = text_splitter.split_documents(documents)


embedding_model = HuggingFaceEmbeddings(
    model_name="BAAI/bge-small-zh"
)

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embedding_model,
    persist_directory="./chroma_db"
)

# 创建检索器
retriever = vectorstore.as_retriever()

template = """
根据以下已知信息,简洁并专业地回答用户问题。
如果无法从中得到答案,请说"我无法从已知信息中找到答案"。

已知信息:
{context}

用户问题:{question}

回答:
"""
prompt = PromptTemplate(
    template=template,
    input_variables=["context", "question"]
)

#
llm = Ollama(model="deepseek-r1:7b")  # 本地部署的模型


qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever,
    chain_type_kwargs={"prompt": prompt}
)


question = "图灵的论文叫什么?"
result = qa_chain.invoke({"query": question})
print(result["result"])

在这里插入图片描述

进阶RAG-1

进阶RAG介绍

在这里插入图片描述
进阶RAG的关键在于对流程各个环节进行深度优化,考虑更多细节,使系统在实际应用中能够应对各种复杂场景。
进阶RAG的优化可以分为三个关键阶段:检索前优化、检索过程优化以及检索后优化。接下来,我们将详细探讨每个阶段的优化策略。

检索前优化

检索前优化主要围绕知识库的构建和索引结构的优化展开,为高质量检索奠定基础。

1. 增强数据质量

朴素RAG常常忽略对原始数据的预处理,而进阶RAG高度重视数据质量。具体措施包括:

  • 修订和简化数据内容:确保文档的正确性和可读性
  • 删除不相关信息:清理文档中的噪声数据
  • 消除歧义:明确模糊表述,减少理解偏差
  • 维护上下文连贯性:确保文档片段间的逻辑连贯
2. 优化索引结构

索引结构的设计直接影响检索的效率和准确性,进阶RAG提供了多种索引优化策略:

  • 调整Chunk大小:根据场景特点和数据特性,合理划分文档片段。过大的Chunk会引入噪声,过小则可能割裂上下文,需要根据应用场景找到平衡点。
  • 利用图数据关系:通过知识图谱实现跨索引查询。例如,对于"感冒常用药物的副作用"这类查询,传统向量检索可能难以找到不包含"感冒"关键词的特定药品(如"白加黑"),而知识图谱可以通过节点关系轻松建立这种关联。
  • 构建层级索引:通过多层索引提升检索效率。当数据量庞大时,可以先对文档生成摘要或概述形成上层索引(即总结一下生成新的索引),用户查询先匹配上层索引缩小范围,再在相关文档中进行精细检索,大幅提高检索速度。用向量数据库直接内置的,通常不用开发者实现。
  • 加入元数据信息:存储和利用文档的元数据(如文件名、日期、作者等)辅助检索。例如,当用户查询"2024年9月9日的会议纪要"时,可以先基于元数据快速定位文档,再获取具体内容。
3. 混合检索与对齐优化

即在知识库中存储的用户常见问题:

  • 为每个文档预先设计可能的用户问题集
  • 用户查询首先与这些预设问题匹配
  • 找到最相似的预设问题后,直接检索对应文档

这种方法通过问题-文档的预对齐,提高了检索的精准度,特别适合FAQ类场景。

检索过程优化

检索过程的优化主要围绕向量化模型的改进展开:

向量化模型优化

虽然在实际应用中较少采用,但为特定场景微调embeding模型是提升检索质量的有效路径:

  • 收集领域特定数据集
  • 针对特定场景微调向量化模型
  • 提高向量表示对特定领域语义的敏感度
检索后优化

检索后优化主要关注如何处理和筛选检索结果,以及如何有效地将检索内容传递给大模型。

1. 重排序(Rerank)优化

向量检索通常会返回多个结果,但并非所有结果都与查询相关:

  • 使用重排序算法或模型对检索结果进行精排
  • 从可能包含大量噪声的初始检索结果(如10个文档)中筛选出最相关的少量文档(如3个)
  • 减少传递给大模型的无关信息,提高生成质量
2. 内容压缩与关键信息提取

即使经过重排序,检索结果中仍可能包含冗余或不相关的信息:

  • 使用专门的小模型压缩检索结果,提取关键信息
  • 删除冗余内容,保留核心事实
  • 减少输入长度,同时保持信息完整性

通过这种方式,可以在不损失关键信息的前提下,提供更精炼的上下文给大模型,既提高了生成质量,也降低了大模型的输入成本。

代码实现

  1. 不同用户的多轮对话
    使用ConversationBufferMemory实现记忆功能,同时采用类用session_id来拆分独立chain从而保证用户会话隔离。
from langchain.document_loaders import WebBaseLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import Chroma
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts import PromptTemplate
from langchain.llms import Ollama
import os
from typing import Dict

os.environ['HF_ENDPOINT'] = 'https://hf-mirror.com'


class ConversationManager:
    def __init__(self):
        # 加载和准备数据(只需执行一次)
        self.retriever = self._prepare_retriever()
        self.llm = Ollama(model="deepseek-r1:7b")

        # 用于存储每个用户会话的记忆
        self.user_memories: Dict[str, ConversationBufferMemory] = {}
        # 用于存储每个用户会话的对话链
        self.user_chains: Dict[str, ConversationalRetrievalChain] = {}

        # 提示模板
        self.qa_prompt = PromptTemplate(
            template="""
        根据以下已知信息和对话历史,简洁并专业地回答用户当前问题。
        如果无法从中得到答案,请说"我无法从已知信息中找到答案"。
        
        已知信息:
        {context}
        
        对话历史:
        {chat_history}
        
        当前问题:{question}
        
        回答:
        """,
            input_variables=["context", "chat_history", "question"]
        )

    def _prepare_retriever(self):
        """准备检索器和向量存储"""
        # 加载文档
        loader = WebBaseLoader("https://blog.youkuaiyun.com/ngadminq/article/details/147687050")
        documents = loader.load()

        # 分割文档
        text_splitter = RecursiveCharacterTextSplitter(
            chunk_size=500,
            chunk_overlap=50
        )
        chunks = text_splitter.split_documents(documents)

        # 创建嵌入模型
        embedding_model = HuggingFaceEmbeddings(
            model_name="BAAI/bge-small-zh"
        )

        # 创建向量存储
        vectorstore = Chroma.from_documents(
            documents=chunks,
            embedding=embedding_model,
            persist_directory="./chroma_db"
        )

        return vectorstore.as_retriever()

    def get_user_chain(self, session_id: str):
        """获取或创建指定session_id的对话链"""
        if session_id not in self.user_chains:
            # 为新用户创建新的记忆组件
            memory = ConversationBufferMemory(
                memory_key="chat_history",
                return_messages=True
            )
            self.user_memories[session_id] = memory

            # 为新用户创建新的对话链
            chain = ConversationalRetrievalChain.from_llm(
                llm=self.llm,
                retriever=self.retriever,
                memory=memory,
                combine_docs_chain_kwargs={"prompt": self.qa_prompt}
            )
            self.user_chains[session_id] = chain

        return self.user_chains[session_id]

    def process_query(self, session_id: str, query: str):
        """处理用户查询并返回响应"""
        chain = self.get_user_chain(session_id)
        result = chain.invoke({"question": query})
        return result["answer"]

    def clear_history(self, session_id: str):
        """清除指定用户的对话历史"""
        if session_id in self.user_memories:
            self.user_memories[session_id].clear()

    def remove_user(self, session_id: str):
        """完全移除用户会话数据"""
        if session_id in self.user_chains:
            del self.user_chains[session_id]
        if session_id in self.user_memories:
            del self.user_memories[session_id]



# 演示:如何在命令行中测试多用户功能
def test_multi_user():
    manager = ConversationManager()

    # 模拟两个不同用户的会话
    session1 = "user1"
    session2 = "user2"

    # 用户1的对话
    print(f"用户1问: Transformer架构都是由什么组成")
    print(f"回答: {manager.process_query(session1, 'Transformer架构都是由什么组成')}")

    # 用户2的对话
    print(f"用户2问: 深度学习有哪些应用")
    print(f"回答: {manager.process_query(session2, '深度学习有哪些应用')}")

    # 用户1的后续问题(应该能理解上下文)
    print(f"用户1问: 它的原理是什么")
    print(f"回答: {manager.process_query(session1, '它的原理是什么')}")

    # 用户2的后续问题(应该能理解不同的上下文)
    print(f"用户2问: 能详细说明一下这些应用吗")
    print(f"回答: {manager.process_query(session2, '能详细说明一下这些应用吗')}")


if __name__ == "__main__":
    test_multi_user()

在这里插入图片描述

进阶RAG-2

对于复杂问题,单纯的RAG流程可能无法满足需求。进阶RAG引入了Agent思想,通过问题拆解和规划,处理多步骤复杂查询:

RAG与Agent思想的融合

当面对"对比A团队和B团队的产出"这类复杂查询时,Agent+RAG的处理流程如下:

  1. 任务规划:大模型首先进行任务拆解和规划
  2. 子任务执行:将复杂问题分解为多个子问题,每个子问题分别走一遍RAG流程
    • 子任务1:检索A团队的产出数据
    • 子任务2:检索B团队的产出数据
  3. 结果综合:收集所有子任务的检索结果,交由大模型进行综合分析和比对
  4. 生成最终答案:基于综合分析结果生成完整答案

这种Agent+RAG的融合方式大大扩展了系统处理复杂问题的能力,使RAG不再局限于简单的单轮问答,而能够处理需要多步推理和多源信息整合的复杂场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

非常大模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值