掘金 人工智能 05月08日 10:13
构建简单的 RAG 系统并集成 DeepSeek 大模型
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了如何构建一个简单的RAG(检索增强生成)系统,该系统结合了检索和生成技术,利用大型语言模型(LLM)如DeepSeek,在生成回答前从外部知识库中检索相关信息,从而提高回答的准确性和时效性。文章详细阐述了RAG的工作流程、技术选型,并提供了Python代码实现,包括环境准备、知识库准备、代码实现等步骤,最终实现了一个能够从本地知识库检索信息并生成基于上下文回答的系统。

💡 RAG 的核心在于结合检索和生成。用户提问后,系统先在知识库中检索相关信息,构建更丰富的提示,再由大语言模型生成最终回答,从而提高准确性和时效性。

💻 构建 RAG 系统涉及多个技术组件。主要使用 Python 编程语言,结合 LangChain 框架简化 LLM 应用开发,利用 Sentence Transformers 生成文本嵌入,FAISS 构建向量数据库,以及 DeepSeek API 提供的 LLM 服务接口。

📚 实现 RAG 系统的关键步骤包括环境准备、知识库准备和代码实现。这包括安装必要的 Python 库,获取 DeepSeek API Key,准备知识库文档,进行文本分割、嵌入和构建向量数据库,最后与 DeepSeek 大模型集成,创建 RAG 链并进行提问。

RAG (Retrieval Augmented Generation) 是一种结合了检索(Retrieval)和生成(Generation)两大能力的技术。它允许大型语言模型(LLM)在生成回答之前,先从外部知识库中检索相关信息,从而提高回答的准确性、时效性,并减少“幻觉”现象。本文将详细介绍如何构建一个简单的 RAG 系统,并以 DeepSeek 大模型为例,展示如何将其集成到流程中。

RAG 的核心思想

RAG 的工作流程通常如下:

    用户提问 (Query):用户提出一个问题。信息检索 (Retrieval):系统将用户的问题在知识库中进行搜索,找出最相关的文档片段。上下文增强 (Context Augmentation):将检索到的相关信息与用户的原始问题一起,构建成一个更丰富的提示(Prompt)。答案生成 (Generation):将增强后的提示输入到大语言模型(如 DeepSeek)中,由模型生成最终的回答。

技术选型

为了实现一个简单的 RAG 系统,我们将使用以下工具:

实现步骤

1. 环境准备

首先,确保你安装了必要的 Python 库。

pip install langchain sentence-transformers faiss-cpu deepseek-llm openai python-dotenv

接下来,你需要获取 DeepSeek API Key。访问 DeepSeek 开放平台 注册并获取你的 API Key。

建议将 API Key 存储在项目根目录下的 .env 文件中,以避免硬编码:

# .env 文件内容DEEPSEEK_API_KEY="your_actual_api_key"

2. 准备知识库

创建一个名为 knowledge_base 的文件夹,并在其中放入一些文本文件作为你的知识库。例如:

knowledge_base/deepseek_info.txt:

DeepSeek是一家致力于研究通用人工智能(AGI)的公司。DeepSeek的使命是“用AI改变世界”。DeepSeek发布了多款强大的语言模型,包括DeepSeek Coder和DeepSeek LLM。DeepSeek LLM 67B在多个基准测试中表现出色。

knowledge_base/rag_intro.txt:

RAG代表检索增强生成。它结合了信息检索系统的能力和大型语言模型的生成能力。RAG可以帮助减少LLM的幻觉,并提供基于特定文档的答案。

3. 代码实现

下面是完整的 Python 代码实现:

import osfrom dotenv import load_dotenvfrom langchain_community.document_loaders import DirectoryLoader, TextLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import FAISSfrom langchain_deepseek import ChatDeepseek # langchain_community.chat_models for older versionsfrom langchain.chains import RetrievalQAfrom langchain.prompts import PromptTemplate# 1. 加载环境变量 (DeepSeek API Key)load_dotenv()api_key = os.getenv("DEEPSEEK_API_KEY")if not api_key:    raise ValueError("DEEPSEEK_API_KEY not found in .env file or environment variables.")# --- 知识库处理 ---# 2. 加载知识库文档# 使用 TextLoader 加载单个文件,或 DirectoryLoader 加载整个目录print("加载知识库文档...")loader = DirectoryLoader('./knowledge_base/', glob="**/*.txt", loader_cls=TextLoader, loader_kwargs={'encoding': 'utf-8'})documents = loader.load()if not documents:    print("未能加载任何文档,请检查 'knowledge_base' 文件夹和文件路径。")    exit()print(f"成功加载 {len(documents)} 个文档。")# 3. 文本分割# 将加载的文档分割成更小的块,以便嵌入和检索print("分割文档...")text_splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)texts = text_splitter.split_documents(documents)if not texts:    print("未能分割文档。")    exit()print(f"文档被分割成 {len(texts)} 个文本块。")# 4. 文本嵌入# 使用 HuggingFace 上的预训练模型将文本块转换为向量# 'all-MiniLM-L6-v2' 是一个轻量级且效果不错的模型print("生成文本嵌入...")embeddings_model_name = "sentence-transformers/all-MiniLM-L6-v2"embeddings = HuggingFaceEmbeddings(model_name=embeddings_model_name)# 5. 构建向量数据库# 使用 FAISS 将嵌入向量化并存储,以便进行快速相似性搜索print("构建向量数据库...")# FAISS.from_documents 会自动处理嵌入和索引vector_store = FAISS.from_documents(texts, embeddings)print("向量数据库构建完成。")# --- 与 DeepSeek 大模型集成 ---# 6. 初始化 DeepSeek LLM# 使用 DeepSeek 的聊天模型# 注意:model_name 可能需要根据 DeepSeek 官方文档更新# 常见的模型如 'deepseek-chat' 或 'deepseek-coder' (如果你用coder模型)print("初始化 DeepSeek LLM...")llm = ChatDeepseek(    model="deepseek-chat", # 或者其他 DeepSeek 模型,如 "deepseek-coder"    api_key=api_key,    temperature=0.1 # 控制生成文本的随机性,较低的值使输出更确定)print("DeepSeek LLM 初始化完成。")# 7. 创建 RAG 链 (RetrievalQA)# RetrievalQA 链封装了检索、构建提示和调用 LLM 的整个过程# 定义一个Prompt模板 (可选,但推荐用于更好地控制输出)prompt_template = """请根据以下提供的上下文信息来回答问题。如果你在上下文中找不到答案,请说你不知道,不要试图编造答案。保持答案简洁。上下文:{context}问题: {question}答案:"""PROMPT = PromptTemplate(    template=prompt_template, input_variables=["context", "question"])chain_type_kwargs = {"prompt": PROMPT}print("创建 RAG 链...")qa_chain = RetrievalQA.from_chain_type(    llm=llm,    chain_type="stuff", # "stuff" 是最简单的方法,将所有检索到的文本块直接塞入上下文    retriever=vector_store.as_retriever(search_kwargs={"k": 3}), # k=3 表示检索最相关的3个文档块    chain_type_kwargs=chain_type_kwargs,    return_source_documents=True # 同时返回源文档,方便溯源)print("RAG 链创建完成。")# 8. 进行提问print("\n--- 开始提问 ---")while True:    user_query = input("\n请输入你的问题 (输入 '退出' 来结束程序): ")    if user_query.lower() == '退出':        break    if not user_query.strip():        print("问题不能为空,请重新输入。")        continue    print(f"\n正在处理问题: {user_query}")    try:        result = qa_chain.invoke({"query": user_query}) # LangChain 0.1.0+ 使用 invoke        print("\n模型回答:")        print(result["result"])        print("\n引用的源文档片段:")        for i, source_doc in enumerate(result["source_documents"]):            print(f"--- 片段 {i+1} (来自: {source_doc.metadata.get('source', '未知来源')}) ---")            print(source_doc.page_content)            print("-" * 20)    except Exception as e:        print(f"处理问题时发生错误: {e}")print("\n程序已退出。")

详细解释

    加载环境变量 (load_dotenv, os.getenv):

      .env 文件安全地加载 DEEPSEEK_API_KEY。这是保护敏感信息的良好实践。

    加载知识库文档 (DirectoryLoader, TextLoader):

      DirectoryLoader 用于从指定目录加载所有匹配 glob 模式(这里是 *.txt)的文件。loader_cls=TextLoader 指定用 TextLoader 来处理每个找到的文件。loader_kwargs={'encoding': 'utf-8'} 确保以 UTF-8 编码读取文件,避免中文乱码。

    文本分割 (RecursiveCharacterTextSplitter):

      LLM 的上下文窗口长度有限,且对较短、集中的文本块进行嵌入效果更好。RecursiveCharacterTextSplitter 会尝试按特定字符(如 \n\n, \n, )递归地分割文本。chunk_size=500: 每个文本块的最大字符数。chunk_overlap=50: 块之间的重叠字符数,有助于保持语义的连续性,避免重要信息在分割点被切断。

    文本嵌入 (HuggingFaceEmbeddings):

      嵌入是将文本转换为高维向量的过程,使得语义相似的文本在向量空间中也相近。HuggingFaceEmbeddings 使用 sentence-transformers 库从 Hugging Face Hub 下载并运行指定的嵌入模型(这里是 sentence-transformers/all-MiniLM-L6-v2,一个流行且高效的模型)。这些嵌入是在本地计算的。备选方案:DeepSeek 可能也提供自己的嵌入 API。如果使用其 API,则需要替换此步骤,调用 DeepSeek 的嵌入接口,这可能带来与 DeepSeek LLM 更一致的语义理解。

    构建向量数据库 (FAISS):

      FAISS 是一个用于高效相似性搜索和稠密向量聚类的库。FAISS.from_documents(texts, embeddings) 这个便捷方法会:
        对所有分割后的 texts(文档块)使用提供的 embeddings 模型生成向量。将这些向量和原始文本块一起存储在 FAISS 索引中。
      现在,我们可以用一个新的查询向量来快速找到数据库中最相似的(即最相关的)文本块。

    初始化 DeepSeek LLM (ChatDeepseek):

      ChatDeepseek 是 LangChain 中与 DeepSeek 聊天模型交互的类。model="deepseek-chat": 指定要使用的 DeepSeek 模型。请查阅 DeepSeek 官方文档获取最新的可用模型名称 (如 deepseek-chat, deepseek-coder 等)。api_key=api_key: 传入之前加载的 API Key。temperature=0.1: 控制模型输出的创造性/随机性。较低的温度(如0.1-0.3)使输出更具确定性和事实性,适合 RAG 场景。较高的温度(如0.7-1.0)则更具创造性。

    创建 RAG 链 (RetrievalQA):

      RetrievalQA 是 LangChain 提供的一个标准链,它将检索器(Retriever)和 LLM 组合在一起。llm=llm: 指定我们初始化的 DeepSeek LLM。chain_type="stuff": 这是最直接的链类型。它获取所有检索到的文档,将它们的内容“塞入”(stuff)到提示中,然后将这个组合的提示传递给 LLM。其他链类型如 map_reduce, refine, map_rerank 用于处理大量文档或需要更复杂处理的场景。retriever=vector_store.as_retriever(search_kwargs={"k": 3}):
        vector_store.as_retriever(): 将我们的 FAISS 向量数据库转换为一个 LangChain Retriever 对象。search_kwargs={"k": 3}: 配置检索器在每次查询时返回最相关的 k=3 个文档块。可以根据需求调整 k 的值。
      prompt_templatePROMPT: 定义了一个自定义的提示模板。这非常重要,因为它指导 LLM 如何利用提供的上下文来回答问题,并指示其在信息不足时如何回应(例如,“如果你在上下文中找不到答案,请说你不知道”)。{context}{question} 是占位符,RetrievalQA 链会自动填充它们。chain_type_kwargs={"prompt": PROMPT}: 将自定义提示应用到 stuff 链。return_source_documents=True: 使链在返回 LLM 生成的答案(result)的同时,也返回检索到的源文档片段(source_documents)。这对于调试和验证答案来源非常有用。

    进行提问 (qa_chain.invoke):

      在一个循环中接收用户输入。qa_chain.invoke({"query": user_query}) (在 LangChain 0.1.0+ 版本中,旧版为 qa_chain({"query": user_query})qa_chain.run(user_query)):
        用户的 user_query 首先被传递给 retrieverretriever 使用嵌入模型将 user_query 转换为向量,并在 FAISS 向量数据库中执行相似性搜索,找到 k=3 个最相关的文档块。这些文档块的内容(作为 context)和原始 user_query(作为 question)被填充到 PROMPT 模板中。最终形成的完整提示被发送给 DeepSeek LLM (llm)。LLM 根据提供的上下文和问题生成答案。
      打印 LLM 的回答 (result["result"]) 和引用的源文档 (result["source_documents"])。

如何运行

    将上述 Python 代码保存为 simple_rag.py。确保你的 .env 文件和 knowledge_base 文件夹与 simple_rag.py 在同一目录下。在终端中运行脚本: python simple_rag.py程序会加载文档、构建索引,然后提示你输入问题。

进阶与优化方向

这个简单的 RAG 系统是一个很好的起点,但还有许多可以优化和扩展的地方:

    更优的文本分割策略: 探索不同的 chunk_sizechunk_overlap,或使用基于语义的分割器。更强的嵌入模型: all-MiniLM-L6-v2 是轻量级的,对于复杂任务,可以考虑更大更强的嵌入模型,或者 DeepSeek 官方提供的嵌入服务(如果可用)。混合搜索 (Hybrid Search): 结合关键词搜索(如 BM25)和向量搜索,可能会提高检索效果。重排 (Re-ranking): 在初步检索后,使用一个更复杂的模型(如 Cross-Encoder)对检索到的文档进行重排序,以提高最顶部结果的质量。提示工程 (Prompt Engineering): 进一步优化提示模板,引导 LLM 更好地利用上下文。上下文管理: 对于需要多轮对话的场景,需要管理对话历史和上下文。处理找不到答案的情况: 更优雅地处理知识库中没有相关信息的情况。异步处理与流式输出: 对于生产环境,异步处理和流式输出可以改善用户体验。评估: 建立评估 RAG 系统性能的指标和流程(如 RAGAS 框架)。用户界面: 使用 Streamlit 或 Gradio 为 RAG 系统创建一个简单的 Web UI。

总结

通过 LangChain、Sentence Transformers、FAISS 和 DeepSeek API,我们成功构建了一个基础的 RAG 系统。这个系统能够从本地知识库中检索信息,并利用 DeepSeek 大模型的强大能力生成基于上下文的回答。RAG 是增强 LLM 应用能力的关键技术,希望本文能为你探索更高级的 AI 应用打下坚实的基础。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

RAG DeepSeek LangChain LLM
相关文章