阿拉善盟网站建设_网站建设公司_内容更新_seo优化
2026/1/20 9:41:03 网站建设 项目流程

构建基础 RAG 系统 —— 给大模型装 “外挂知识库” 的全流程实践

当大模型遇到 “知识滞后”(比如不知道 2025 年的新政策)或 “幻觉输出”(编造不存在的事实)时,我们该如何解决?答案是给它装一个 “外挂知识库”—— 这就是 RAG(Retrieval-Augmented Generation,检索增强生成)的核心价值。本次作业的目标,就是亲手搭建一个基础 RAG 系统,从数据处理到最终生成回答,完整走通 “检索→增强→生成” 的闭环。无论你是想解决大模型的事实性错误,还是想让模型能 “查阅” 特定文档(如教材、报告),这份实操指南都能帮你把理论落地。

一、先搞懂:为什么要做 RAG 系统?——3 个核心痛点的解决方案

在动手前,我们先明确 RAG 的定位:它不是替代大模型,而是给大模型 “补短板”。传统大模型的 3 个核心痛点,正好是 RAG 的优势所在:

  1. 知识滞后:大模型的训练数据有 “截止日期”(比如 2023 年),无法获取之后的新信息。RAG 通过实时检索外部知识库(如最新新闻、政策文档),让模型能 “查阅最新资料”;
  2. 容易幻觉:大模型会基于概率生成文本,偶尔编造看似合理但错误的内容。RAG 让模型 “只基于检索到的权威信息回答”,像写论文时 “引用参考文献” 一样,大幅降低幻觉率;
  3. 领域适配难:通用大模型对医疗、法律等专业领域的知识深度不足。RAG 可接入专业知识库(如医疗指南、法律条文),让模型快速具备 “专业能力”,无需复杂的模型微调。

本次作业搭建的基础 RAG 系统,就是要解决这些问题,比如让模型能 “检索高中数学教材内容” 来解答题目,或 “查阅产品说明书” 来回答用户咨询 —— 本质是让模型从 “闭卷考试” 变成 “开卷考试”。

二、作业核心目标:掌握 RAG 的 4 个关键能力

本次作业不追求复杂的优化,重点是理解 RAG 的核心流程并实现基础功能,完成后你将掌握:

  1. 数据处理能力:把原始文档(如 TXT、PDF)拆成适合检索的 “语义块”(Chunk),避免因文档太长导致检索不精准;
  2. 索引构建能力:用嵌入模型(Embedding Model)将文本块转成 “数字向量”,并存储到向量数据库中,实现快速检索;
  3. 检索实现能力:将用户的问题转成向量后,在向量数据库中找到 “语义最相似” 的文本块,作为 “参考资料”;
  4. 生成整合能力:把 “用户问题 + 检索到的参考资料” 组织成提示词,让大模型基于这些资料生成准确回答。

三、实操步骤:5 步搭建基础 RAG 系统 —— 代码 + 逻辑双解析

本次作业将用Python+LangChain+FAISS实现(工具选择的原因:LangChain 简化流程,FAISS 是轻量级向量数据库,无需复杂部署,适合新手),全程不涉及高深算法,跟着步骤走就能完成。

1. 准备工作:安装必备工具库

首先安装 3 个核心库,打开终端输入以下命令(若用 Anaconda 环境,需先激活对应环境):

python

运行

# 安装LangChain(简化RAG流程的工具集) pip install langchain # 安装FAISS(轻量级向量数据库,用于存储向量) pip install faiss-cpu # 若有GPU,可替换为faiss-gpu # 安装Sentence-BERT(轻量级嵌入模型,用于文本转向量) pip install sentence-transformers # 安装文档加载工具(支持TXT、PDF等格式) pip install pypdf python-dotenv

这些工具的分工很明确:LangChain 负责串联 “加载文档→分块→向量化→检索→生成” 全流程,FAISS 负责存向量,Sentence-BERT 负责把文本转成向量。

2. 第一步:数据处理 —— 把文档拆成 “可检索的语义块”

原始文档(如一本 500 页的教材)直接检索会 “抓不住重点”,必须拆成更小的 “语义块”—— 就像把一本书拆成 “章节→段落”,既能保留完整语义,又能精准定位。

具体操作:
  • 步骤 1:加载文档:以 “高中数学必修 1 PDF 教材” 为例,用 LangChain 的PyPDFLoader加载文档;

    python

    运行

    from langchain_community.document_loaders import PyPDFLoader # 加载PDF文档(替换为你的文档路径) loader = PyPDFLoader("高中数学必修1.pdf") # 把PDF按页拆分成文档对象 documents = loader.load()
  • 步骤 2:拆分语义块:用RecursiveCharacterTextSplitter分块,核心参数是chunk_size(块大小,按字符数算)和chunk_overlap(块重叠度,避免拆分导致语义断裂)。👉 作业建议:chunk_size=300(每块约 300 字符,对应 1-2 个段落),chunk_overlap=50(每块重叠 50 字符),适合教材类文档;

    python

    运行

    from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=300, # 每块最大字符数 chunk_overlap=50, # 块重叠字符数 length_function=len, # 按字符数计算长度 is_separator_regex=False # 不使用正则分割 ) # 拆分文档,得到语义块列表 chunks = text_splitter.split_documents(documents)
  • 关键提醒:分块不是 “越细越好”—— 太细会导致语义不完整(比如把一个公式拆到两个块里),太粗会导致检索精准度下降(比如一块包含 3 个知识点)。可根据文档类型调整:教材类选 300-500 字符,新闻类选 200-300 字符。

3. 第二步:构建索引 —— 把文本块转成 “可检索的向量”

计算机无法直接 “理解” 文本,必须把语义块转成 “高维向量”(比如 768 维的数字列表)—— 语义越相似的文本,向量距离越近。这一步就是构建 “向量索引”,为后续检索做准备。

具体操作:
  • 步骤 1:选择嵌入模型:作业推荐用all-MiniLM-L6-v2(Sentence-BERT 系列的轻量级模型,速度快、效果好,适合入门);

    python

    运行

    from langchain_community.embeddings import SentenceTransformerEmbeddings # 初始化嵌入模型 embedding_model = SentenceTransformerEmbeddings( model_name="all-MiniLM-L6-v2" # 轻量级模型,约40MB )
  • 步骤 2:构建向量索引:用 FAISS 将语义块的向量存储起来,形成可快速检索的索引;

    python

    运行

    from langchain_community.vectorstores import FAISS # 把语义块转成向量,并构建FAISS索引 db = FAISS.from_documents( documents=chunks, # 拆分后的语义块 embedding=embedding_model# 嵌入模型 ) # 保存索引(下次用不用重新构建,直接加载即可) db.save_local("math_textbook_faiss_index")
  • 后续加载索引:若下次想继续使用,无需重新处理文档,直接加载已保存的索引:

    python

    运行

    # 加载已有的FAISS索引 db = FAISS.load_local( "math_textbook_faiss_index", # 索引保存路径 embedding_model, # 同之前的嵌入模型 allow_dangerous_deserialization=True # 允许加载本地索引(开发环境用) )

4. 第三步:实现检索 —— 让系统 “找到最相关的资料”

当用户提出问题(如 “什么是函数的定义域?”)时,系统需要先把问题转成向量,再在 FAISS 索引中找到 “向量距离最近” 的 Top-K 个语义块(比如 Top-3,即最相关的 3 个段落)。

具体操作:

python

运行

# 1. 定义用户问题 user_query = "什么是函数的定义域?请结合教材内容解释" # 2. 检索最相关的3个语义块(k=3,可根据需求调整) retriever = db.as_retriever(search_kwargs={"k": 3}) retrieved_chunks = retriever.get_relevant_documents(user_query) # 3. 打印检索结果(查看找到的参考资料) print("检索到的相关内容:") for i, chunk in enumerate(retrieved_chunks, 1): print(f"\n【第{i}条参考】") print(f"内容:{chunk.page_content}") print(f"来源:第{chunk.metadata['page']}页") # metadata包含文档页码等信息
  • 关键逻辑:检索的核心是 “语义相似度匹配”,不是 “关键词匹配”—— 比如用户问 “函数的定义域怎么求”,即使语义块里写的是 “定义域的定义与求解步骤”,也能被精准检索到(因为语义相似)。
  • 作业小挑战:尝试调整k的值(如 k=2、k=5),观察检索结果的数量对后续生成回答的影响(k 太小可能信息不足,k 太大可能引入冗余)。

5. 第四步:生成回答 —— 让大模型 “基于参考资料说话”

这一步是 RAG 的 “增强” 核心:把 “用户问题 + 检索到的参考资料” 组织成提示词,再交给大模型生成回答,确保回答 “有依据、不胡说”。

具体操作(以开源模型 Llama 3 为例,也可改用 ChatGPT 等):

python

运行

from langchain.chains import RetrievalQA from langchain_community.llms import HuggingFacePipeline from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline # 1. 加载开源大模型(Llama 3-8B,轻量级,适合作业场景) model_name = "meta-llama/Llama-3.2-8B-Instruct" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name) # 2. 构建模型管道 pipe = pipeline( "text-generation", model=model, tokenizer=tokenizer, max_new_tokens=512, # 最大生成字符数 temperature=0.3 # 低温度,保证回答稳定准确 ) llm = HuggingFacePipeline(pipeline=pipe) # 3. 构建RAG问答链(串联检索和生成) qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", # 简单直接的提示组织方式:把所有参考资料塞进提示 retriever=retriever, return_source_documents=True # 让回答包含参考资料来源(方便溯源) ) # 4. 生成回答 result = qa_chain({"query": user_query}) # 5. 打印结果 print("最终回答:") print(result["result"]) print("\n回答依据(参考资料):") for i, doc in enumerate(result["source_documents"], 1): print(f"{i}. 第{doc.metadata['page']}页:{doc.page_content[:100]}...") # 显示前100字符
  • 提示组织方式:作业用chain_type="stuff"(简单直接,适合基础场景),进阶场景可改用map_reduce(先总结每个参考块,再整合)或refine(逐步优化回答)。
  • 核心价值:生成的回答会明确基于检索到的教材内容,比如 “根据教材第 12 页的定义,函数的定义域是指……”,既解决了幻觉问题,又能追溯来源。

四、作业关键难点:3 个容易踩的坑及解决方案

在实操中,新手容易遇到 “检索不准”“生成杂乱” 等问题,这 3 个常见坑的解决方法要记牢:

1. 坑 1:分块大小不合适,导致语义断裂或检索不准

  • 表现:检索到的内容不完整(比如公式只一半),或回答时漏关键信息;
  • 解决方案:根据文档类型调整chunk_size
    • 教材 / 技术文档(多公式、长段落):300-500 字符,保留标题层级(比如用MarkdownHeaderTextSplitter拆分,保留 “章节标题→小节标题”);
    • 新闻 / 短文本(段落短、信息散):200-300 字符,chunk_overlap设为 30-50,避免拆分句子。

2. 坑 2:嵌入模型选得太复杂,导致速度慢

  • 表现:向量化时卡住,或检索响应时间超过 10 秒;
  • 解决方案:新手优先选轻量级模型:
    • 入门级:all-MiniLM-L6-v2(40MB,速度快,适合小文档);
    • 进阶级:all-mpnet-base-v2(1.1GB,精度更高,适合专业文档);
    • 避免一上来就用大模型(如text-embedding-3-large),除非有 GPU 支持。

3. 坑 3:生成回答时,参考资料没被充分利用

  • 表现:回答还是 “凭模型记忆”,没引用检索到的内容;
  • 解决方案:优化提示词模板(用chain_type="custom"自定义提示):

    python

    运行

    from langchain.prompts import PromptTemplate # 自定义提示模板:明确要求模型引用参考资料 prompt_template = """ 请根据以下参考资料,回答用户的问题。回答必须包含参考资料的来源(如“第X页”),不允许编造信息。 参考资料: {context} 用户问题:{question} 回答: """ prompt = PromptTemplate( template=prompt_template, input_variables=["context", "question"] ) # 用自定义提示构建问答链 qa_chain = RetrievalQA.from_chain_type( llm=llm, chain_type="stuff", retriever=retriever, chain_type_kwargs={"prompt": prompt}, # 传入自定义提示 return_source_documents=True )

通过明确指令,强制模型 “必须用参考资料”,避免 “忽略检索结果” 的问题。

五、如何评估你的 RAG 系统?——2 个简单有效的方法

作业完成后,不能只看 “能生成回答”,还要判断系统好不好用,这 2 个评估方法足够基础且实用:

  1. 定性评估:看回答是否 “有依据、无幻觉”—— 比如用户问 “函数定义域的求解步骤”,回答是否引用了检索到的教材内容,有没有编造步骤;
  2. 定量评估:用 JudgeBoi 评估 2 个核心指标:
    • 检索准确率:检索到的 Top-K 个语义块,有多少是真正和问题相关的(比如 K=3 时,相关的块数≥2 才算合格);
    • 生成准确性:回答与参考资料的匹配度(比如 “是否准确引用了教材定义”“步骤是否和参考资料一致”)。

六、知识联动:RAG 与之前内容的衔接

本次作业不是孤立的,它是之前学过的 “上下文工程”“提示工程” 的综合应用:

  • 与上下文工程的衔接:RAG 的 “检索参考资料” 本质是 “精准筛选上下文”,把最有用的信息塞进大模型的 “上下文窗口”,避免信息过载;
  • 与提示工程的衔接:生成回答时的 “提示模板设计”,是提示工程的具体落地 —— 通过明确指令,让模型高效利用参考资料,这和之前 “优化提示词控制推理长度” 的思路一致。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询