Rag基本流程
📝 RAG技术解析:结合检索与生成的AI问答系统 RAG(Retrieval-Augmented Generation)是一种融合信息检索和大语言模型生成的技术框架,通过以下流程提升问答准确性: 1️⃣ 文档加载:读取原始文本数据 2️⃣ 文本分块:将长文本切分为语义连贯的片段 3️⃣ 向量化:使用嵌入模型(如BGE)转换为高维向量 4️⃣ 向量存储:存入专业数据库(推荐FAISS/Chroma
·
🧠 什么是 RAG?
RAG 是一种结合 信息检索(Retrieval) 与 大语言模型生成(Generation) 的技术框架,用于让 LLM 在回答问题时能基于外部知识库(如文档、数据库),从而提高准确性、减少幻觉。
🔁 RAG 的基本流程(对应你的代码)
1️⃣ 加载文档(Document Loading)
with open("doc.txt", encoding="utf-8") as f:
raw_text = f.read()
- 作用:从本地文件、网页、PDF 等来源读取原始文本。
- ✅ 建议:生产中可使用
Unstructured、PyPDFLoader、WebBaseLoader(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) - 或按句子/段落分割,保留语义完整性。
- 使用 LangChain 的
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(), PyPDFLoader, WebBaseLoader |
| 2 | 文本分块 | RecursiveCharacterTextSplitter |
| 3 | 向量化 | SentenceTransformer, OpenAIEmbeddings |
| 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)更多推荐


所有评论(0)