【笔记】 wow-rag 第1课-手搓一个土得掉渣的RAG

正文详见:wow-rag/notebooks/第1课-手搓一个土得掉渣的RAG.ipynb at main · datawhalechina/wow-raghttps://github.com/datawhalechina/wow-rag/blob/main/notebooks/%E7%AC%AC1%E8%AF%BE-%E6%89%8B%E6%90%93%E4%B8%80%E4%B8%AA%E5%9C%9F%E5%BE%97%E6%8E%89%E6%B8%A3%E7%9A%84RAG.ipynb

一、前期准备

1、创建环境

安装到一个你想安装的地方,不要让C盘太满了。

conda create --prefix=/home/conda_env/rag python=3.11.5

2、安装依赖库

首先进入环境:

conda activate /home/conda_env/rag

依次安装依赖库:

pip install faiss-cpu scikit-learn scipy faiss-gpu
pip install openai ZHIpuAI
pip install python-dotenv 

3、获取智谱API

智谱AI开放平台https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys

4、导入API

新建一个.env文件存储你的API

 5、定义智谱API

from zhipuai import ZhipuAI

client = ZhipuAI(api_key=api_key) # 请填写您自己的APIKey

二、构造知识库

RAG的原理是先在文档中搜索,把搜索到最接近的内容喂给大模型,让大模型根据喂给它的内容进行回答,因此需要存储文档块,便于检索。

1、切分文本文档

将长篇文章切分为小的文本块,方便后续检索。

2、文本块向量化

通过emb_model将文本块向量化并存储到数据库中。

3、数据库检索

数据库检索首先需要将输入文本转化问向量,和文本块转化过程一样。然后通过Faiss库的向量搜索方法寻找数据库中和输入文本最相似的文本块并返回。

三、构造prompt

在数据库中检索到需要的数据后,通过prompt告诉给大模型,让大模型根据该数据回复用户的输入。

<think> 我们正在构建一个简易的RAG系统,主要步骤包括:文档加载、文本分割、向量化(嵌入)、向量存储与检索、以及使用大语言模型生成答案。 参考引用内容: [^1]介绍了RAG技术的定义和工作原理,我们可以借鉴其核心思想:通过检索外部知识来增强生成模型的能力。 [^2]提供了使用Python实现RAG系统的实战案例,我们可以参考其实现思路。 [^3]指出了RAG实现中的挑战,但我们构建简易系统,暂不考虑复杂情况。 [^4]详细介绍了MultiQueryRetriever等高级技术,但我们的简易系统可以先实现基本查询。 [^5]给出了欧氏距离公式,我们可以用来计算向量相似度(但实际中更常用余弦相似度)。 步骤: 1. 文档加载:将文档(如文本文件、PDF等)加载进来。 2. 文本分割:将大文档分割成小块(chunks),因为大语言模型一次处理的文本长度有限。 3. 向量化:使用预训练的嵌入模型(如sentence-transformers)将文本块转换为向量。 4. 构建向量存储:存储文本块及其对应的向量。 5. 检索:根据用户查询,将查询文本向量化,然后在向量存储中检索最相似的文本块(通常使用余弦相似度或欧氏距离)。 6. 生成:将检索到的相关文本块(作为上下文)与用户查询一起输入给大语言模型(如ChatGPT、LLaMA等),生成答案。 简易实现(使用Python): 我们将使用以下库: - `langchain`:提供文档加载、文本分割等工具(但为了动实现,我们这里尽量自己写,或者用更基础的库) - `sentence-transformers`:用于文本向量化 - `annoy` 或 `faiss`:用于高效向量检索(为了简易,也可以直接用余弦相似度计算) 但为了真正动,我们可以简化: - 向量存储使用列表存储向量,然后通过计算每个向量的相似度来检索(效率低,但数据量小可以接受) - 不使用langchain,自己写分割和加载。 具体步骤: 1. 安装必要库: ```bash pip install sentence-transformers # 如果没有其他依赖,我们先不用faiss,自己实现线性扫描 ``` 2. 文档加载与分割: - 假设我们有一个文本文件,读取并分割成固定大小的块(例如按句子或固定字符数)。 3. 向量化: - 使用sentence-transformers模型,比如`all-MiniLM-L6-v2`(轻量级,效果不错)。 4. 存储: - 将每个文本块和对应的向量存储起来(比如在内存中用一个列表存储文本块,一个numpy数组存储向量)。 5. 检索: - 用户输入查询,将查询向量化。 - 计算查询向量与每个文本块向量的相似度(余弦相似度)。 - 取相似度最高的前k个文本块作为上下文。 6. 生成答案: - 将查询和检索到的上下文组合成一个提示(prompt),输入给大语言模型(如使用OpenAI API,或者本地运行的LLM)。 注意:这里我们使用OpenAI API作为生成模型(需要API key),当然也可以使用其他模型。 代码结构示例: 步骤1:加载文档并分割 ```python def load_and_split_document(file_path, chunk_size=500, chunk_overlap=50): # 读取文件 with open(file_path, 'r', encoding='utf-8') as f: text = f.read() # 简易分割:按字符分割 chunks = [] start = 0 while start < len(text): end = min(start + chunk_size, len(text)) chunks.append(text[start:end]) start = end - chunk_overlap # 重叠部分 if start < 0: start = 0 return chunks ``` 步骤2:向量化 ```python from sentence_transformers import SentenceTransformer model = SentenceTransformer('all-MiniLM-L6-v2') def get_embeddings(texts): return model.encode(texts) ``` 步骤3:存储向量和文本 ```python # 假设我们有一个文档列表 document_chunks = load_and_split_document('example.txt') embeddings = get_embeddings(document_chunks) # 存储为列表和数组 ``` 步骤4:检索函数 ```python import numpy as np def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) def retrieve(query, embeddings, chunks, top_k=3): query_embedding = get_embeddings([query])[0] similarities = [] for emb in embeddings: sim = cosine_similarity(query_embedding, emb) similarities.append(sim) # 获取top_k索引 sorted_indices = np.argsort(similarities)[::-1][:top_k] # 从大到小排序取前k个 return [chunks[i] for i in sorted_indices] ``` 步骤5:生成答案 ```python import openai openai.api_key = 'your-api-key' def generate_answer(query, context_chunks): context = "\n\n".join(context_chunks) prompt = f"""根据以下上下文信息回答问题。如果你不知道答案,就说不知道。 上下文: {context} 问题:{query} 答案:""" response = openai.Completion.create( engine="text-davinci-003", # 或者使用其他模型 prompt=prompt, max_tokens=150, temperature=0.5 ) return response.choices[0].text.strip() ``` 步骤6:组合 ```python def simple_rag_system(query): # 检索 context_chunks = retrieve(query, embeddings, document_chunks) # 生成 answer = generate_answer(query, context_chunks) return answer ``` 注意:上述代码是一个高度简化的版本,实际中需要考虑效率(使用向量索引)、上下文长度限制(确保总长度不超过模型限制)等。 改进点(可选): - 使用更高效向量索引(如FAISS)加速检索。 - 分割文本时使用更智能的方法(如按句子分割,避免截断单词)。 以上就是一个简易动实现的RAG系统。根据引用[^2]的思想,我们通过检索相关文档片段来增强生成模型的答案准确性。 引用说明: [^1] 介绍了RAG的基本原理,我们遵循了其通过检索外部知识增强生成的思路。 [^2] 提供了使用Python构建RAG的实战参考,我们的实现步骤与其类似。 [^4] 提到的高级检索策略(如MultiQueryRetriever)在我们的简易系统中未使用,但可以后续扩展。 §§相关问题§§ 1. RAG系统中如何选择合适的分块大小? 2. 除了余弦相似度,还有哪些方法可以计算文本向量的相似度? 3. 如何评估一个RAG系统的性能? </think> ### 动实现简易RAG系统的方法 我将分步说明如何动实现一个简易的检索增强生成(RAG)系统,核心流程参考引用[^2][^4]的实现思路: #### 1. **系统架构设计** ```mermaid graph LR A[文档加载] --> B[文本切分] B --> C[向量嵌入] C --> D[向量存储] E[用户查询] --> F[查询嵌入] F --> G[向量检索] G --> H[上下文组装] H --> I[LLM生成] ``` #### 2. **核心实现步骤** **① 文档加载与分割**(参考[^4]) ```python def split_document(text, chunk_size=500): """ 按固定大小分割文本 :param text: 输入文本 :param chunk_size: 块大小 :return: 文本块列表 """ return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)] ``` **② 文本向量化**(参考[^5]) ```python from sentence_transformers import SentenceTransformer # 加载轻量级嵌入模型 embedding_model = SentenceTransformer('all-MiniLM-L6-v2') def get_embeddings(texts): """ 生成文本向量 :param texts: 文本列表 :return: 向量列表 """ return embedding_model.encode(texts).tolist() ``` **③ 向量存储与检索** ```python import numpy as np from sklearn.metrics.pairwise import cosine_similarity class VectorStore: def __init__(self): self.chunks = [] self.embeddings = [] def add_documents(self, chunks): """存储文档向量""" self.chunks.extend(chunks) self.embeddings.extend(get_embeddings(chunks)) def retrieve(self, query, top_k=3): """检索最相关文本块""" query_vec = get_embeddings([query])[0] similarities = cosine_similarity([query_vec], self.embeddings)[0] top_indices = np.argsort(similarities)[-top_k:][::-1] return [self.chunks[i] for i in top_indices] ``` **④ 生成答案**(参考[^1][^2]) ```python def generate_answer(query, context_chunks): """ LLM生成最终答案 :param query: 用户问题 :param context_chunks: 检索到的文本块 """ context = "\n\n".join(context_chunks) prompt = f"""根据以下上下文回答问题: {context} 问题:{query} 答案:""" # 实际使用需替换为真实API调用 return call_llm_api(prompt) # 示例调用 vector_store = VectorStore() vector_store.add_documents(split_document(load_document("data.txt"))) contexts = vector_store.retrieve("什么是RAG技术?") answer = generate_answer("什么是RAG技术?", contexts) ``` #### 3. **关键技术点** 1. **向量相似度计算** 使用余弦相似度而非欧氏距离(参考[^5]): $ \text{cosine similarity} = \frac{A \cdot B}{\|A\| \|B\|} $ 2. **上下文组装** 将top-k相关片段拼接为提示词上下文(参考[^4]) 3. **优化方向**(参考[^3]) - 添加元数据过滤 - 实现[^4]提到的MultiQuery检索 - 加入重新排序(rerank)机制 #### 4. **完整工作流程** 1. 准备知识库文档(TXT/PDF等) 2. 分割文档为文本块(通常512-1024字符) 3. 生成文本块向量并存储 4. 接收用户查询并向量化 5. 检索最相关的文本块 6. 将查询+检索结果输入LLM生成答案 > 提示:实际部署时可使用FAISS替代内存存储提升检索效率[^4],添加缓存机制减少重复计算[^3]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值