当前位置: 首页 > news >正文

高级RAG策略学习(四)——上下文窗口增强检索RAG

上下文窗口RAG系统:原理与代码实现详解

引言

检索增强生成(RAG)系统通过结合信息检索和生成模型,显著提升了问答系统的准确性和相关性。然而,传统的RAG系统在检索时往往只关注单个文档块,可能会丢失重要的上下文信息。本文将详细介绍一种基于上下文窗口的RAG系统实现,该系统通过检索相邻文档块来增强上下文信息,从而提供更准确和连贯的回答。

一、技术原理

1.1 传统RAG系统的局限性

传统的RAG系统工作流程如下:

  1. 将长文档分割成固定大小的文档块
  2. 对每个文档块进行向量化编码
  3. 根据查询进行语义相似度检索
  4. 将检索到的文档块作为上下文输入生成模型

这种方法的主要问题是:

  • 上下文断裂:文档分块可能会切断重要的上下文关系
  • 信息不完整:单个文档块可能无法提供足够的背景信息
  • 逻辑不连贯:检索到的片段可能缺乏前后逻辑关系

1.2 上下文窗口RAG的解决方案

上下文窗口RAG系统通过以下机制解决上述问题:

1.2.1 索引化分块
  • 为每个文档块分配唯一的索引号
  • 保持文档块在原文中的顺序关系
  • 在元数据中记录块的位置信息
1.2.2 邻居检索策略
  • 首先进行标准的语义相似度检索
  • 对于每个检索到的相关块,获取其前后N个相邻块
  • 将相邻块按原始顺序重新组合
1.2.3 上下文重构
  • 处理相邻块之间的重叠部分
  • 生成包含完整上下文的文档序列
  • 保持原文的逻辑连贯性

1.3 技术优势

  1. 上下文完整性:通过检索相邻块,保持了文档的原始逻辑结构
  2. 信息丰富度:提供更多的背景信息,有助于生成更准确的回答
  3. 灵活配置:可以根据需要调整邻居块的数量和重叠度
  4. 对比分析:同时提供基线和增强结果,便于效果评估

二、代码实现详解

2.1 环境配置与依赖

import os
from dotenv import load_dotenv
from langchain.docstore.document import Document
from helper_functions import *
from typing import List
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores import FAISS# 加载环境变量
load_dotenv()# 配置通义千问API
DASHSCOPE_BASE_URL = os.getenv("DASHSCOPE_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
CHAT_MODEL_NAME = "qwen-plus"
EMBEDDING_MODEL_NAME = "text-embedding-v2"

技术要点

  • 使用通义千问的嵌入模型和聊天模型
  • 通过环境变量管理API配置,提高安全性
  • 支持中文文本处理,优化本地化体验

2.2 核心功能实现

2.2.1 文档分块与索引
def split_text_to_chunks_with_indices(text: str, chunk_size: int, chunk_overlap: int) -> List[Document]:"""将文本分割为多个文档,每个文档包含指定大小的文本,同时记录每个文档的索引。"""chunks = []start = 0while start < len(text):end = start + chunk_sizechunk = text[start:end]chunks.append(Document(page_content=chunk, metadata={"index": len(chunks), "text": text}))start += chunk_size - chunk_overlapreturn chunks

关键特性

  • 索引记录:为每个块分配唯一索引,便于后续检索
  • 重叠处理:支持块间重叠,保持上下文连续性
  • 元数据保存:在元数据中保存原始文本和索引信息
2.2.2 索引检索机制
def get_chunk_by_index(vectorstore, target_index: int) -> Document:"""根据索引从向量存储中检索文档。"""all_docs = vectorstore.similarity_search("", k=vectorstore.index.ntotal)for doc in all_docs:if doc.metadata.get('index') == target_index:return docreturn None

实现原理

  • 通过遍历所有文档,匹配元数据中的索引值
  • 提供精确的索引检索功能
  • 支持快速定位特定位置的文档块
2.2.3 上下文增强检索(核心算法)
def retrieve_with_context_overlap(vectorstore, retriever, query: str, num_neighbors: int = 1, chunk_size: int = 200,chunk_overlap: int = 20) -> List[str]:"""从向量存储中检索文档,根据语义相似度和上下文重叠进行填充。"""# 1. 语义检索relevant_chunks = retriever.get_relevant_documents(query)result_sequences = []for chunk in relevant_chunks:current_index = chunk.metadata.get('index')if current_index is None:continue# 2. 确定邻居范围start_index = max(0, current_index - num_neighbors)end_index = current_index + num_neighbors + 1# 3. 检索邻居块neighbor_chunks = []for i in range(start_index, end_index):neighbor_chunk = get_chunk_by_index(vectorstore, i)if neighbor_chunk:neighbor_chunks.append(neighbor_chunk)# 4. 排序和拼接neighbor_chunks.sort(key=lambda x: x.metadata.get('index', 0))# 5. 处理重叠拼接concatenated_text = neighbor_chunks[0].page_contentfor i in range(1, len(neighbor_chunks)):current_chunk = neighbor_chunks[i].page_contentoverlap_start = max(0, len(concatenated_text) - chunk_overlap)concatenated_text = concatenated_text[:overlap_start] + current_chunkresult_sequences.append(concatenated_text)return result_sequences

算法流程

  1. 语义检索:基于查询进行初始的相似度检索
  2. 邻居扩展:为每个相关块确定前后邻居的范围
  3. 批量检索:获取指定范围内的所有邻居块
  4. 顺序重排:按原始索引顺序排列邻居块
  5. 智能拼接:处理重叠部分,生成连贯的文本序列

2.3 RAG系统封装

2.3.1 主类设计
class RAGMethod:"""RAG方法的主类,封装了文档准备、检索器设置和查询执行。"""def __init__(self, chunk_size: int = 400, chunk_overlap: int = 200):self.chunk_size = chunk_sizeself.chunk_overlap = chunk_overlapself.docs = self._prepare_docs()self.vectorstore, self.retriever = self._prepare_retriever()

设计模式

  • 封装性:将复杂的RAG流程封装在单一类中
  • 可配置性:支持自定义块大小和重叠参数
  • 模块化:分离文档准备和检索器设置逻辑
2.3.2 检索器准备
def _prepare_retriever(self):"""准备检索器,使用文档和嵌入模型创建向量存储。"""embeddings = DashScopeEmbeddings(model=EMBEDDING_MODEL_NAME,dashscope_api_key=os.getenv('DASHSCOPE_API_KEY'))vectorstore = FAISS.from_documents(self.docs, embeddings)retriever = vectorstore.as_retriever(search_kwargs={"k": 1})return vectorstore, retriever

技术选型

  • FAISS向量存储:高效的相似度搜索和聚类库
  • 通义千问嵌入:针对中文优化的嵌入模型
  • 灵活检索配置:可调整检索数量和搜索参数

2.4 命令行接口

def parse_args():import argparseparser = argparse.ArgumentParser(description="Run RAG method on a given PDF and query.")parser.add_argument("--query", type=str, default="深度学习何时在AI中变得突出?",help="用于测试检索器的查询")parser.add_argument('--chunk_size', type=int, default=400, help="文本块的大小")parser.add_argument('--chunk_overlap', type=int, default=200, help="块之间的重叠")parser.add_argument('--num_neighbors', type=int, default=1, help="用于上下文的相邻块数量")return parser.parse_args()

用户体验

  • 参数化配置:支持命令行参数调整
  • 中文界面:本地化的帮助信息
  • 默认值设置:提供合理的默认参数

三、优化策略与最佳实践

3.1 窗口大小优化策略

根据不同应用场景选择合适的窗口参数:

文档类型推荐窗口大小chunk_sizenum_neighbors说明
短文本(新闻、FAQ)小窗口200-3001减少冗余信息
中等文本(技术文档)中等窗口400-6001-2平衡信息完整性与效率
长文本(学术论文)大窗口600-8002-3需要更多上下文理解

3.2 性能优化建议

  1. 元数据管理优化:确保窗口元数据不参与向量计算,仅在检索后处理阶段使用
  2. 缓存机制:对频繁查询的邻居块进行缓存,减少重复计算
  3. 批量处理:对多个查询进行批量检索,提高系统吞吐量

3.3 与其他技术的结合

  • 重排序(Reranking):先筛选相关节点,再补充上下文窗口
  • 长上下文模型:配合GPT-4 Turbo等长上下文模型,扩大窗口容量
  • 多粒度检索:根据查询复杂度动态调整窗口层级

四、技术路径选择指南

4.1 框架工具 vs 自定义实现

选择维度框架工具路径(如LlamaIndex)自定义函数路径langchain
开发效率高(开箱即用)中(需要编码实现)
灵活性中(受框架限制)高(完全可控)
维护成本低(框架维护)高(自主维护)
定制化程度
适用场景快速原型、标准需求复杂业务、特殊需求

4.2 选择建议

  • 选择框架工具:快速搭建RAG原型,标准化需求,团队技术栈统一
  • 选择自定义实现:复杂业务逻辑,特殊检索规则,性能要求极高

五、实际应用与效果

5.1 应用场景

  1. 长文档问答:适用于技术文档、学术论文等长文本的问答系统
  2. 知识库检索:企业内部知识管理和信息检索
  3. 教育辅助:在线学习平台的智能答疑系统
  4. 法律文档分析:法律条文和案例的智能检索和分析

5.2 性能优势

通过上下文窗口增强,系统能够:

  • 提高回答准确性:更完整的上下文信息
  • 增强逻辑连贯性:保持原文的逻辑结构
  • 减少信息丢失:避免重要信息被分块切断
  • 提升用户体验:更自然和连贯的回答

5.3 配置建议

  • chunk_size:建议300-500字符,平衡信息密度和处理效率
  • chunk_overlap:建议为chunk_size的30-50%,确保上下文连续性
  • num_neighbors:建议1-2个邻居,避免引入过多噪声信息

六、总结与展望

6.1 技术创新点

  1. 索引化分块:为文档块建立有序索引,支持精确的位置检索
  2. 邻居扩展策略:智能获取相邻上下文,增强信息完整性
  3. 重叠处理算法:优雅处理块间重叠,保持文本连贯性
  4. 对比评估框架:同时提供基线和增强结果,便于效果分析

6.2 未来发展方向

  1. 动态窗口调整:根据查询复杂度自动调整邻居数量
  2. 多模态支持:扩展到图像、表格等多模态内容
  3. 实时优化:基于用户反馈动态优化检索策略
  4. 分布式部署:支持大规模文档库的分布式检索

6.3 结语

上下文窗口RAG系统通过创新的邻居检索和上下文重构机制,有效解决了传统RAG系统的上下文断裂问题。该系统不仅保持了原有的语义检索能力,还显著提升了回答的完整性和连贯性。通过本文的详细介绍,读者可以深入理解上下文窗口RAG系统的原理和实现,并将其应用到实际的项目中,构建更智能和高效的问答系统。

7. 代码

import os
from dotenv import load_dotenv
from langchain.docstore.document import Document
from helper_functions import *
from typing import List
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.vectorstores import FAISS# Load environment variables from a .env file
load_dotenv()# 配置通义千问API
DASHSCOPE_BASE_URL = os.getenv("DASHSCOPE_BASE_URL", "https://dashscope.aliyuncs.com/compatible-mode/v1")
CHAT_MODEL_NAME = "qwen-plus"
EMBEDDING_MODEL_NAME = "text-embedding-v2"# Function to split text into chunks with metadata of the chunk chronological index
def split_text_to_chunks_with_indices(text: str, chunk_size: int, chunk_overlap: int) -> List[Document]:"""将文本分割为多个文档,每个文档包含指定大小的文本,同时记录每个文档的索引。:param text: 要分割的文本:param chunk_size: 每个文档的最大字符数:param chunk_overlap: 文档之间的重叠字符数:return: 包含多个文档的列表,每个文档包含文本和索引元数据"""chunks = []start = 0while start < len(text):"""循环分割文本,每次提取一个文档的文本内容。文档的索引从0开始,每次增加1。文档的元数据包含原始文本和索引。"""end = start + chunk_sizechunk = text[start:end]chunks.append(Document(page_content=chunk, metadata={"index": len(chunks), "text": text}))start += chunk_size - chunk_overlapreturn chunks# Function to retrieve a chunk from the vectorstore based on its index in the metadata
def get_chunk_by_index(vectorstore, target_index: int) -> Document:"""根据索引从向量存储中检索文档。:param vectorstore: 向量存储对象:param target_index: 目标文档的索引:return: 检索到的文档对象"""all_docs = vectorstore.similarity_search("", k=vectorstore.index.ntotal)for doc in all_docs:if doc.metadata.get('index') == target_index:return docreturn None# Function that retrieves from the vectorstore based on semantic similarity and pads each retrieved chunk with its neighboring chunks
def retrieve_with_context_overlap(vectorstore, retriever, query: str, num_neighbors: int = 1, chunk_size: int = 200,chunk_overlap: int = 20) -> List[str]:"""从向量存储中检索文档,根据语义相似度和上下文重叠进行填充。:param vectorstore: 向量存储对象:param retriever: 检索器对象:param query: 检索查询:param num_neighbors: 要检索的邻居文档数量:param chunk_size: 每个文档的最大字符数:param chunk_overlap: 文档之间的重叠字符数:return: 包含填充后的文档序列的列表"""relevant_chunks = retriever.get_relevant_documents(query)result_sequences = []for chunk in relevant_chunks:current_index = chunk.metadata.get('index')if current_index is None:continue# Determine the range of chunks to retrievestart_index = max(0, current_index - num_neighbors)end_index = current_index + num_neighbors + 1# Retrieve all chunks in the rangeneighbor_chunks = []for i in range(start_index, end_index):neighbor_chunk = get_chunk_by_index(vectorstore, i)if neighbor_chunk:neighbor_chunks.append(neighbor_chunk)# Sort chunks by their index to ensure correct orderneighbor_chunks.sort(key=lambda x: x.metadata.get('index', 0))# Concatenate chunks, accounting for overlapconcatenated_text = neighbor_chunks[0].page_contentfor i in range(1, len(neighbor_chunks)):current_chunk = neighbor_chunks[i].page_contentoverlap_start = max(0, len(concatenated_text) - chunk_overlap)concatenated_text = concatenated_text[:overlap_start] + current_chunkresult_sequences.append(concatenated_text)return result_sequences# Main class that encapsulates the RAG method
class RAGMethod:"""RAG方法的主类,封装了文档准备、检索器设置和查询执行。"""def __init__(self, chunk_size: int = 400, chunk_overlap: int = 200):self.chunk_size = chunk_sizeself.chunk_overlap = chunk_overlapself.docs = self._prepare_docs()self.vectorstore, self.retriever = self._prepare_retriever()def _prepare_docs(self) -> List[Document]:content = """人工智能(AI)的历史可以追溯到20世纪中叶。"人工智能"这一术语在1956年的达特茅斯会议上被提出,标志着该领域的正式开始。在20世纪50年代和60年代,AI研究专注于符号方法和问题解决。1955年由艾伦·纽厄尔和赫伯特·西蒙创建的逻辑理论家,通常被认为是第一个AI程序。20世纪60年代见证了专家系统的发展,这些系统使用预定义规则来解决复杂问题。1965年创建的DENDRAL是最早的专家系统之一,专门用于分析化学化合物。然而,20世纪70年代带来了第一个"AI寒冬",这是一个AI研究资金减少和兴趣下降的时期,主要是由于过度承诺的能力和未能兑现的结果。20世纪80年代随着专家系统在企业中的普及而复苏。日本政府的第五代计算机项目也刺激了全球对AI研究的投资增加。神经网络在20世纪80年代和90年代获得了突出地位。反向传播算法虽然早期就被发现,但在这个时期被广泛用于训练多层网络。20世纪90年代末和2000年代标志着机器学习方法的兴起。支持向量机(SVM)和随机森林在各种分类和回归任务中变得流行。深度学习,一种使用多层神经网络的机器学习子集,在2010年代初开始显示出有希望的结果。突破出现在2012年,当时一个深度神经网络在ImageNet竞赛中显著超越了其他机器学习方法。从那时起,深度学习已经革命性地改变了许多AI应用,包括图像和语音识别、自然语言处理和游戏。2016年,谷歌的AlphaGo击败了世界冠军围棋选手,这是AI的一个里程碑式成就。当前的AI时代的特点是深度学习与其他AI技术的整合、更高效和强大硬件的发展,以及围绕AI部署的伦理考虑。2017年引入的Transformer已成为自然语言处理中的主导架构,使得像GPT(生成式预训练Transformer)这样的模型能够生成类似人类的文本。随着AI的不断发展,新的挑战和机遇不断涌现。可解释AI、鲁棒和公平的机器学习,以及通用人工智能(AGI)是该领域当前和未来研究的关键领域。"""return split_text_to_chunks_with_indices(content, self.chunk_size, self.chunk_overlap)def _prepare_retriever(self):"""准备检索器,使用文档和嵌入模型创建向量存储。:return: 向量存储对象和检索器对象"""embeddings = DashScopeEmbeddings(model=EMBEDDING_MODEL_NAME,dashscope_api_key=os.getenv('DASHSCOPE_API_KEY'))vectorstore = FAISS.from_documents(self.docs, embeddings)retriever = vectorstore.as_retriever(search_kwargs={"k": 1})return vectorstore, retrieverdef run(self, query: str, num_neighbors: int = 1):"""运行RAG方法,执行查询并返回基线块和增强块。:param query: 检索查询:param num_neighbors: 要检索的邻居文档数量:return: 基线块和增强块的元组"""baseline_chunk = self.retriever.get_relevant_documents(query)enriched_chunks = retrieve_with_context_overlap(self.vectorstore, self.retriever, query, num_neighbors,self.chunk_size, self.chunk_overlap)return baseline_chunk[0].page_content, enriched_chunks[0]# Argument parsing function
def parse_args():import argparseparser = argparse.ArgumentParser(description="Run RAG method on a given PDF and query.")parser.add_argument("--query", type=str, default="深度学习何时在AI中变得突出?",help="用于测试检索器的查询(默认:'深度学习何时在AI中变得突出?')。")parser.add_argument('--chunk_size', type=int, default=400, help="文本块的大小。")parser.add_argument('--chunk_overlap', type=int, default=200, help="块之间的重叠。")parser.add_argument('--num_neighbors', type=int, default=1, help="用于上下文的相邻块数量。")return parser.parse_args()# Main execution
if __name__ == "__main__":args = parse_args()# Initialize and run the RAG methodrag_method = RAGMethod(chunk_size=args.chunk_size, chunk_overlap=args.chunk_overlap)baseline, enriched = rag_method.run(args.query, num_neighbors=args.num_neighbors)print("基线块:")print(baseline)print("\n增强块:")print(enriched)

调用方式:

 python context_enrichment_window_around_chunk.py --query "什么是人工智能?" --chunk_size 300 --num_neighbors 3

文章转载自:

http://Qzv2zzvS.mrckk.cn
http://PlPjsfMg.mrckk.cn
http://VZtJ3zQS.mrckk.cn
http://YbR3pCVu.mrckk.cn
http://sWf7kyta.mrckk.cn
http://i6bukTiT.mrckk.cn
http://70oSnIZj.mrckk.cn
http://Ycl7eVGY.mrckk.cn
http://hYrHJiea.mrckk.cn
http://LrOwebIl.mrckk.cn
http://pTLm9po0.mrckk.cn
http://4hZl1ZuG.mrckk.cn
http://kbDaopXg.mrckk.cn
http://mffrXpuQ.mrckk.cn
http://AqC0UMUO.mrckk.cn
http://wBQiieW5.mrckk.cn
http://l5STdcAZ.mrckk.cn
http://9jHPeNVf.mrckk.cn
http://nS1oJvST.mrckk.cn
http://HtjuW9wY.mrckk.cn
http://W10xZA8O.mrckk.cn
http://RtamNJTo.mrckk.cn
http://wA4EACAG.mrckk.cn
http://NfEjCcLN.mrckk.cn
http://xr0zJdcO.mrckk.cn
http://3iIR1aeU.mrckk.cn
http://3HuCTuXu.mrckk.cn
http://R5DXdKeW.mrckk.cn
http://Js8jA2CJ.mrckk.cn
http://KtizAwpt.mrckk.cn
http://www.dtcms.com/a/368991.html

相关文章:

  • 如何通过AI进行数据资产梳理
  • 跨平台超低延迟RTSP播放器技术设计探究
  • 一文了解大模型推理优化
  • 嵌入式单片机---串口通信及相关通信技术
  • k8s基础练习环境搭建
  • AiPPT生成的PPT内容质量怎么样?会不会出现逻辑混乱或数据错误?
  • 系统架构思考20241204
  • GPU版Pytorch的安装
  • 飞算JavaAI炫技赛:在线图书借阅平台的设计与实现
  • Bing 搜索引擎检索语法
  • 14 C++ STL 容器实战:stack/list 模拟实现指南 + priority_queue 用法及避坑技巧
  • ElasticSearch新角色的创建及新用户的创建
  • 【运维】Linux inotify watches 限制问题解决方案
  • ES模块(ESM)、CommonJS(CJS)和UMD三种格式
  • centos下gdb调试python的core文件
  • 计算机网络2 第二章 物理层——用什么方式传输邮件
  • 使用深度Q网络(DQN)算法实现游戏AI
  • 深度学习优化框架(DeepSpeed)
  • Java 8 终于要被淘汰了!带你速通 Java 8~24 新特性 | 又能跟面试官吹牛皮了
  • 操作系统重点
  • 安全运维-云计算系统安全
  • HTML 各种标签的使用说明书
  • BYOFF (Bring Your Own Formatting Function)解析(80)
  • MySQL源码部署(rhel7)
  • HashMap多线程下的循环链表问题
  • 企业微信AI怎么用?食品集团靠它砍掉50%低效操作,答案就是选对企业微信服务商
  • 企业微信AI怎么用才高效?3大功能+5个实操场景,实测效率提升50%
  • Arduino Nano33 BLESense Rev2【室内空气质量检测语音识别蓝牙调光台灯】
  • 无人机小目标检测新SOTA:MASF-YOLO重磅开源,多模块协同助力精度飞跃
  • 本地 Docker 环境 Solr 配置 SSL 证书