当前位置:首页 > 技术分享 > 正文内容

手把手教你从零搭建一个RAG知识库问答系统

admin1周前 (07-08)技术分享47

为什么你需要一个RAG系统?

先问一个问题:你调用大模型API时,有没有遇到过这些尴尬时刻?

  • 问它你公司内部的操作手册,它一本正经地胡说八道

  • 让它分析你刚收到的客户邮件,它说“我的知识截止到2024年”

  • 想让它帮你从一堆文档里找答案,你得手动复制粘贴好几轮

根本原因很简单:大模型没有你的私有数据。

RAG(Retrieval-Augmented Generation,检索增强生成)就是解决这个问题的标准方案。它的核心思路极其朴素:

在让AI回答之前,先从你的知识库里检索相关内容,然后把这些内容“塞”进提示词里,让AI基于这些资料来回答。

这样一来,AI就从一个“什么都懂一点但什么都不精”的博学家,变成了你专属的“读过你所有文档的实习生”。


整体架构:一张图看懂RAG

在动手之前,我们先看清全貌。一个完整的RAG系统只有两大阶段:

阶段一:数据索引(离线准备,做一次就行)

text
你的文档(PDF/Word/网页)
       ↓
   文档加载器(读取)
       ↓
   文本分割器(切成小段)
       ↓
   嵌入模型(转为向量)
       ↓
   向量数据库(存储索引)

阶段二:问答检索(在线执行,每次提问都走一遍)

text
用户提问
   ↓
问题向量化(用同一个嵌入模型)
   ↓
向量相似度检索(在数据库里找最相关的Top-K段落)
   ↓
构建提示词(检索到的内容 + 用户问题)
   ↓
大模型生成回答
   ↓
返回给用户

这套架构没什么高深的理论,但组合起来就能解决大模型的“失忆症”问题。下面我们一步一步把它变成可运行的代码。


准备工作:安装依赖

我选择的技术栈是 Python + LangChain + ChromaDB + OpenAI风格的API,这是目前最主流、最轻量的组合。

bash
pip install langchain langchain-community chromadb pypdf sentence-transformers

说明:如果你用的是国内大模型(文心、通义、智谱等),只需替换嵌入模型和LLM的调用方式即可,架构完全一样。本文示例用开源的 sentence-transformers 做嵌入,避免依赖特定云服务。


Step 1:加载文档(以PDF为例)

python
from langchain_community.document_loaders import PyPDFLoader# 加载PDF文件loader = PyPDFLoader("你的文档.pdf")documents = loader.load()print(f"共加载了 {len(documents)} 页内容")print(documents[0].page_content[:200])  # 预览第一页的前200字

PyPDFLoader会把PDF每一页作为一个独立的Document对象,包含文本内容和元数据(页码等)。

如果报错pypdf 库没装全,执行 pip install pypdf 即可。


Step 2:文本分割(核心步骤)

为什么要把文档切成小段?因为大模型的上下文窗口有限(即使是128K的模型,塞太多不相关内容也会稀释注意力),而且向量检索的精度也要求文本块足够聚焦。

python
from langchain.text_splitter import RecursiveCharacterTextSplitter# 创建一个文本分割器text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每块最大500个字符
    chunk_overlap=50,      # 块与块之间重叠50个字符(防止切断关键信息)
    separators=["\n\n", "\n", "。", "!", "?", ",", " ", ""])# 执行分割chunks = text_splitter.split_documents(documents)print(f"文档被切成了 {len(chunks)} 个文本块")

关键参数解释

  • chunk_size=500:不是越大越好。一般500-1000字符适合中文场景,太长检索精度下降,太短丢失上下文。

  • chunk_overlap=50:重叠部分能保证一个完整的知识点不会被拦腰切断。

如果你处理的文档很规整(比如技术手册),可以试试 MarkdownHeaderTextSplitter,按标题层级切分,效果更佳。


Step 3:向量化并存入数据库

这一步把文字变成“语义坐标”,让计算机能计算“苹果”和“水果”比“苹果”和“汽车”更接近。

python
from langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import Chroma# 加载开源的嵌入模型(中文优化版)embeddings = HuggingFaceEmbeddings(
    model_name="shibing624/text2vec-base-chinese")# 创建向量数据库(自动完成向量化和存储)vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embeddings,
    persist_directory="./chroma_db"  # 持久化目录)print("向量数据库创建完成!")

运行完这步,你的本地目录下会出现一个 ./chroma_db 文件夹,里面就是索引好的向量数据。下次运行时,你可以直接加载这个目录,无需重新索引。

如果你用的是OpenAI的Embeddings API,可以替换为:

python
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(api_key="你的key")

Step 4:实现检索函数

python
def search_docs(query, k=3):
    """检索与问题最相关的K个文本块"""
    results = vectorstore.similarity_search(query, k=k)
    return results# 测试一下test_query = "这个产品的主要功能是什么?"relevant_chunks = search_docs(test_query)for i, chunk in enumerate(relevant_chunks):
    print(f"\n--- 相关段落 {i+1} ---")
    print(chunk.page_content[:150] + "...")

这个函数返回的是最相关的文本块列表,按相似度从高到低排序。


Step 5:组合RAG问答(最后一步)

把检索到的内容和用户的提问组合成一个完整的提示词,送给大模型。

python
from langchain_openai import ChatOpenAI# 初始化大模型(这里用OpenAI格式,可替换为国内模型)llm = ChatOpenAI(
    model="gpt-3.5-turbo",
    temperature=0.3,
    api_key="你的key",  # 或用环境变量
    base_url="https://api.openai.com/v1"  # 国内模型请替换对应地址)def rag_ask(question):
    # 1. 检索相关文档
    docs = search_docs(question, k=3)
    
    # 2. 拼接上下文
    context = "\n\n".join([doc.page_content for doc in docs])
    
    # 3. 构建提示词
    prompt = f"""你是一个专业的文档问答助手。请根据以下参考资料回答问题。如果参考资料中没有相关信息,请直接说"资料中没有提到相关内容",不要编造。

参考资料:{context}问题:{question}回答:"""    
    # 4. 调用大模型
    response = llm.invoke(prompt)
    return response.content# 测试answer = rag_ask("这个产品的退款政策是什么?")print(answer)

完整代码汇总(拿来即用)

把上面所有代码片段拼在一起,就是一个完整的RAG系统。为了方便你直接运行,我整理了一个整合版本(只有50行左右):

python
# 安装依赖:pip install langchain langchain-community chromadb pypdf sentence-transformersfrom langchain_community.document_loaders import PyPDFLoaderfrom langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_community.embeddings import HuggingFaceEmbeddingsfrom langchain_community.vectorstores import Chromafrom langchain_openai import ChatOpenAI# ---------- 配置 ----------PDF_PATH = "你的文档.pdf"EMBEDDING_MODEL = "shibing624/text2vec-base-chinese"OPENAI_API_KEY = "你的key"  # 或从环境变量读取# ---------- 1. 加载 ----------loader = PyPDFLoader(PDF_PATH)docs = loader.load()# ---------- 2. 分割 ----------splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)chunks = splitter.split_documents(docs)# ---------- 3. 索引 ----------embeddings = HuggingFaceEmbeddings(model_name=EMBEDDING_MODEL)vectorstore = Chroma.from_documents(chunks, embeddings, persist_directory="./db")vectorstore.persist()# ---------- 4. 检索 ----------def retrieve(query, k=3):
    return vectorstore.similarity_search(query, k=k)# ---------- 5. 问答 ----------llm = ChatOpenAI(model="gpt-3.5-turbo", api_key=OPENAI_API_KEY)def ask(question):
    docs = retrieve(question)
    context = "\n\n".join([d.page_content for d in docs])
    prompt = f"根据资料回答:\n{context}\n\n问题:{question}"
    return llm.invoke(prompt).content# ---------- 6. 运行 ----------if __name__ == "__main__":
    while True:
        q = input("\n请输入问题(输入q退出):")
        if q.lower() == 'q':
            break
        print("回答:", ask(q))

进阶优化(效果翻倍的几个小技巧)

如果你发现回答质量不够理想,按以下优先级逐一优化:

优化点做法预期效果
① 调整分块大小根据文档类型尝试 chunk_size=300800检索精度提升
② 增加检索数量k=5 给模型更多上下文召回率提升
③ 优化提示词明确要求"如果找不到就直说,不要编造"减少幻觉
④ 换嵌入模型BAAI/bge-large-zh-v1.5(目前中文SOTA)语义检索更准
⑤ 增加重排序检索后用 CohereRerank 再排序一次相关段落更靠前

其中最立竿见影的是④和⑤,但①-③不需要额外成本,建议先调优。


常见问题排查

Q:加载PDF时报错“No module named 'pypdf'”?

bash
pip install pypdf

Q:向量数据库已经存在,如何增量添加新文档?

python
vectorstore = Chroma(persist_directory="./db", embedding_function=embeddings)vectorstore.add_documents(new_chunks)

Q:用国内大模型怎么替换?

以智谱为例:

python
from langchain_community.chat_models import ChatZhipuAI
llm = ChatZhipuAI(model="glm-4", api_key="你的key")

其他模型替换方式类似,只需修改 ChatOpenAI 为对应SDK即可。


到这里,你已经亲手搭建了一个完整的RAG知识库问答系统。别小看这50行代码,它背后的架构和当下最火的AI产品(比如各个大厂的文档助手、企业知识库)在核心原理上完全一致。

接下来你可以做什么?

  • 把你的工作文档、学习笔记、个人博客丢进去,做一个专属知识库

  • 加上一个Web界面(Streamlit或Gradio),变成可分享的产品

  • 接入飞书或微信机器人,让团队都能用

动手跑起来吧,遇到任何具体报错,欢迎留言讨论。下一篇文章,我会写如何用这个RAG系统做更复杂的Agent应用,敬请期待。


📌 完整代码已上传,建议你对着文章手敲一遍,理解会比直接复制深刻得多。动手过程中有任何卡点,欢迎在评论区交流。


相关文章

AIGC办公场景实战教程——5个项目即学即用

一、引言:AI不会取代你,但会用AI的人会2026年,大模型的能力已经足够强大,但大部分人仍然只把AI当作"高级搜索引擎" ——问一个问题,得到一个答案,然后就结束了。真正的效率提...

2026年穿梭车故障率统计:哪个品牌最省心?

“穿梭车一年修三次?那你不是买了设备,是请了个爹。”我在统计30个在运项目的故障记录后发现:不同品牌穿梭车的故障率差距高达5倍。最省心的品牌平均8000小时无故障,最闹心的品牌2000小时就要修一次。...

从调包侠到AI开发者:我的实战避坑指南

最近后台收到不少读者留言,问得最多的问题是:“我想学AI开发,但不知道从哪开始,网上课程太多反而挑花了眼。”说实话,我太理解这种感觉了。两年前我转行做AI应用开发时,也踩过无数坑——花了三个月死磕线性...

从“调包侠”到“模型匠人”:一个项目贯穿的AI开发进阶实战

项目锚点:我们要做什么?最终目标:做一个“手写公式识别器”——你画一个数学公式(如 y = 2x + 1 或 E=mc²),模型识别出符号并输出LaTeX代码。这个项目覆盖了:图像分类 → 目标检测...

第一圈代码数字识别:用SVM + HOG快速建立基线

完整代码(Google Colab / Jupyter Notebook 均可)python numpy  np matplotlibpyplot &...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。