🧠 什么是 RAG?

RAG 是一种结合 信息检索(Retrieval)大语言模型生成(Generation) 的技术框架,用于让 LLM 在回答问题时能基于外部知识库(如文档、数据库),从而提高准确性、减少幻觉。


🔁 RAG 的基本流程(对应你的代码)

1️⃣ 加载文档(Document Loading)

with open("doc.txt", encoding="utf-8") as f:
    raw_text = f.read()
  • 作用:从本地文件、网页、PDF 等来源读取原始文本。
  • ✅ 建议:生产中可使用 UnstructuredPyPDFLoaderWebBaseLoader(LangChain 提供)等工具处理多格式文档。

2️⃣ 文本分块(Text Chunking / Splitting)

texts = splitter(raw_text)
  • 作用:将长文档切分为语义连贯、长度适中的“块”(chunks),便于后续向量化和检索。
  • ⚠️ 你的 splitter 函数实现了带重叠(overlap)的滑动窗口分块,逻辑正确但较复杂。
  • ✅ 更推荐做法
    • 使用 LangChain 的 RecursiveCharacterTextSplitter
      from langchain.text_splitter import RecursiveCharacterTextSplitter
      splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)
      texts = splitter.split_text(raw_text)
    • 或按句子/段落分割,保留语义完整性。

3️⃣ 向量化(Embedding)

embedding = SentenceTransformer("models/AI-ModelScope/bge-large-zh-v1___5")
embeddings = embedding.encode(texts)
  • 作用:将每个文本块转换为高维向量(embedding),用于计算语义相似度。
  • ✅ 你使用了中文效果较好的 BGE 模型,这是合理选择。
  • 💡 注意:模型路径 "models/AI-ModelScope/..." 应确保本地存在或能联网下载。

4️⃣ 存储向量(Vector Storage)

persist(texts, embeddings)  # 存为 JSON
  • 作用:持久化文本块及其向量,供后续检索使用。
  • ⚠️ 问题:用 JSON 存储在数据量大时效率极低(无索引、无法快速检索)。
  • ✅ 生产建议:使用专业向量数据库,如:
    • FAISS(Facebook 开源,轻量)
    • Chroma(简单易用)
    • Milvus / Pinecone / Weaviate(大规模场景)
    • 示例(FAISS):
      from langchain.vectorstores import FAISS
      from langchain.embeddings import HuggingFaceEmbeddings
      db = FAISS.from_texts(texts, embedding_model)
      db.save_local("faiss_index")

5️⃣ 检索(Retrieval)

contexts = search("孙悟空自称为什么?")
  • 作用:对用户查询进行向量化,并在向量库中查找最相似的 top-k 文本块。
  • ✅ 你使用了 余弦相似度近似(点积),在归一化向量下等价于余弦相似度。
  • 💡 优化:若用 FAISS,可直接调用 .similarity_search(query, k=5),支持高效 ANN(近似最近邻)搜索。

6️⃣ 生成答案(Generation)

chat_model.invoke(messages)
  • 作用:将检索到的上下文(context)和用户问题一起输入 LLM,生成最终回答。
  • ✅ Prompt 设计合理:“根据已知内容:{context},回答用户问题。”
  • 🔒 安全提示:注意不要泄露敏感文档内容;可加“如果不知道,请回答‘我不知道’”。

📌 总结:RAG 标准流程

步骤 名称 工具/方法
1 文档加载 open()PyPDFLoaderWebBaseLoader
2 文本分块 RecursiveCharacterTextSplitter
3 向量化 SentenceTransformerOpenAIEmbeddings
4 向量存储 FAISS / Chroma / Milvus(非 JSON!)
5 检索 相似度搜索(top-k)
6 生成 LLM(Qwen, ChatGLM, GPT 等)

# 1、读取文档
with open("doc.txt", encoding="utf-8") as f:
    raw_text = f.read()
# print(raw_text)
 
import re
# 2、分割
def splitter(text, chunk_size=200, chunk_overlap=20, separator=""):
    splites = re.split(separator, text)
    # print(splites)
    total = 0  # 当前块的累计长度
    current_doc = []  # 存储当前正在构建的块的片段列表
    docs = []  # 存储所有块的列表  [[chunk1],[chunk2],...]
    separator_len = len(separator)
    for s in splites:
        _len = len(s)
        if total + _len + (separator_len if len(current_doc) > 0 else 0) > chunk_size:
            if current_doc: # 如果current_doc不为空, 则将其连接成一个字符串并添加在docs列表,表示一个块已完成。
                docs.append(separator.join(current_doc))
            # 处理重叠, 从current_doc的开头移除片段,直到满足重叠需求
            while total > chunk_overlap or (total + _len + (separator_len if len(current_doc) > 0 else 0) > chunk_size and total > 0): 
                removed_len = len(current_doc[0])
                total -= removed_len
                current_doc.pop(0)
         
        # 将当前片段s添加到当前块中,需要更新total的长度
        current_doc.append(s)
        total += _len
 
    # current_doc剩余的片段,添加进docs
    if current_doc:
        docs.append(separator.join(current_doc))
    return docs
 
texts = splitter(raw_text)
# print(texts)
 
 
# 3、向量化
from sentence_transformers import SentenceTransformer
embedding = SentenceTransformer("models/AI-ModelScope/bge-large-zh-v1___5")
embeddings = embedding.encode(texts)
 
import os
import json
# 4、放入数据库,只会执行一次
# {texts: embeddings}
def persist(texts, embeddings, path="database"):
    if not os.path.exists(path): # 如果不存在,则创建
        os.makedirs(path)
    data = {} # 将文本和向量存储成字典
    for i, text in enumerate(texts):
        data[text] = embeddings[i].tolist()  # 将text作为键,将向量作为值
     
    with open(f"{path}/text_embeddings.json", "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=4)
 
# persist(texts, embeddings)
 
import numpy as np
# 5、检索
def search(query, database="database/text_embeddings.json", k=5):
    query_vector = embedding.encode([query])[0].tolist()
    # print(query_vector)
 
    with open(database, "r", encoding="utf-8") as f:
        loaded_data = json.load(f)
     
    # 计算相似度
    similarities = []  # 用于存储查询向量与数据库中向量的相似度
    for text, vector in loaded_data.items():
        similarity  = np.dot(query_vector, vector)  # 点积
        similarities.append((text, similarity))
 
    similarities.sort(key=lambda x: x[1], reverse=True)
    return similarities[:k]
 
contexts = search("孙悟空自称为什么?")
# print(contexts)
context = "".join(c[0] for c in contexts)
 
# 6、调用LLM
from langchain_openai import ChatOpenAI
chat_model = ChatOpenAI(
    model="Qwen2___5-7B-Instruct",
    api_key="EMPTY",
    base_url="http://192.168.180.180:29806/v1"
)
 
# 7、定义消息
messages = [
    {"role":"system", "content": f"根据已知内容:{context},回答用户问题。"},
    {"role":"user", "content": f"问题:孙悟空自称为什么?"}
]
 
# 8、输出
output = chat_model.invoke(messages).content
 
print(output)
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐