大模型RAG问答中的文档分段

昨天,我们谈了句子分段,我们再来回顾一下段落的分段方法,目前已经有其他方案,图来自于:https://www.rungalileo.io/blog/mastering-rag-advanced-chunking-techniques-for-llm-applications,可以看到其中的一些优劣势;

例如:

1、递归分块:文档根据段落分隔符、新行、空格和单个字符等分隔符的层级进行分割,使用Langchain的RecursiveCharacterTextSplitter函数(https://api.python.langchain.com/en/latest/character/langchain_text_splitters.character.RecursiveCharacterTextSplitter.html)。

图片

2、语义分块:段落块被嵌入了OpenAI的text-ada-embedding-002。通过识别相邻块嵌入距离发生显著变化的断点来分割文本,如果两个连续段落的嵌入向量之间的距离超过了某个阈值,这可能表明它们在语义上有很大的不同,因此可以在这个位置设置断点。当然,这个的核心点在于阈值。

一旦确定了断点,就可以根据这些断点将文本分割成独立的块。每个块在语义上相对独立,但可能仍然与前一个或后一个块有联系。这对于在更大的文本体中识别连贯且相关的信息块非常有用。

这块可以参考:https://github.com/agamm/semantic-split

图片

3、命题分块:这其实已经是基于大模型的方式来切分了。文本按照论文 《Dense X Retrieval》(https://arxiv.org/abs/2312.06648) 中介绍的方式进行分割,其是一种新的检索单元——命题(proposition),就是将一个文本送入到chagpt等大模型中,生成命题,,核心在于prompt的设计:

图片

然后,在检索生成阶段,将其作为混合来源之一参与召回。

图片

问题2:基于大模型进行动态分块的LumberChunker思想

而针对较长规模的叙事文本而言,也有另一个工作,《LumberChunker: Long-Form Narrative Document Segmentation》(https://arxiv.org/pdf/2406.17526) 这个工作提出了一个名为LumberChunker的文本分割方法,利用大型语言模型(LLM)动态地将长篇叙事文档分割成语义独立的部分。

我们可以重点看几个点:

1、先看实现思想

这种方法的核心思想是,通过允许内容块具有动态大小,可以更好地捕捉内容的语义独立性,从而提高检索效率。LumberChunker通过迭代地提示LLM,在一系列连续段落中识别内容开始转变的点,确保每个分割后的块在语义上是连贯的,但与相邻块有所区别,从而增强了信息检索的有效性。

如下图所示,LumberChunker遵循一个三步流程。

图片

首先,按段落对文档进行分割。将目标文档按照段落进行分割,每个段落被赋予一个唯一的递增ID编号。

其次,通过追加连续的块,创建一个组(Gi),直到超过预定义的标记计数θ;将这些段落按顺序串联起来,直到它们的总标记数超过预定义的阈值θ。

值得注意的是,这个阈值θ是一个经验值,旨在避免将相关较大的段落分割开,同时确保它不会太大以至于用过多的上下文超出模型最大长度,影响其推理准确性。例如,实验表明,θ=550时,LumberChunker在所有测试的k值上都达到了最高的DCG@k得分,表明大约550个标记的提示在捕获上下文和段落长度之间实现了有效平衡。

最后,将段落组合Gi作为输入提供给LLM(例如Gemini 1.0-Pro),并指示LLM找出Gi中内容与前文显著不同的特定段落。这种检测标志着一个块的结束。

这个的细节在于prompt的设计:

图片

文档继续以循环方式被分割成块,每个新的Gi+1组的起始点是前一次迭代中识别出的段落。这个过程在整个文档中重复进行。

2、再看其实现效果

为了评估LumberChunker的性能,作者们创建了一个新的基准测试GutenQA,它包含3000对问题-答案对,这些数据对是从Project Gutenberg网站上的100本公共领域叙事书籍中手动提取的。

其中关于其RAG的设计框架也有一些借鉴的点:

构建了一个特别为传记类书籍量身定制的基于RAG的问答流程,采用了一种混合检索格式,结合了OpenAI text-ada-embedding-002密集嵌入和BM25。

图片

如图4所示:

1)查询路由和文档集成(Query Routing and Document Integration): 每个查询都经过一个检测器的评估,该检测器识别人名或事件的提及。如果检测器在查询中识别到相关提及,使用BM25算法检索前3个最相关的块。

如果没有检测到相关提及(承认偶尔的检测器失败),则作为预防措施检索单个文档

同时,通过密集检索机制检索前15个块。这一步旨在通过访问BM25可能错过的深层语义关系来提高检索质量。

此外,其还实现了BM25检索的文档和密集检索的文档之间的交集检查,并删除了BM25中的重叠文档以避免冗余。然后,将BM25检索到的排名最高的文档优先放置在检索列表的顶部,第二和第三个(如果有的话)放在最后,确保检索策略的混合

2)文档重新排序和重新排名(Document Re-Ordering and Re-Ranking):检索到的块被集成到ChatGPT(gpt-3.5-turbo)的上下文窗口中。如果上下文包含六个或更多的块,则采用一种策略,从中间点开始反转块的顺序。 这种重新排序旨在潜在地最小化模型的“中间丢失”问题,即模型对于长上下文中位于中间位置的信息的性能下降,但开始朝着末端恢复,形成一个U形的性能曲线。然后提示ChatGPT根据它们与查询的相关性递减来识别和重新排序文档。

3)最终答案生成(Final Answer Generation):在这最后一步生成响应。模型确定的前5个文档被保留用于最终答案生成。模型从顶级文档中综合信息,形成一个连贯且上下文准确的答案,旨在全面解决查询。

实验结果表明,LumberChunker在检索性能上超越了其他竞争性基线方法7.37%(在DCG@20指标上),并且当集成到RAG(检索增强生成)流程中时,LumberChunker证明比其他分块方法和竞争性基线(例如Gemini 1.5M Pro)更有效。

3、最终看看其方案评价

但是,其缺陷也很明显。正如该工作所说的,LumberChunker的局限性,包括它需要使用LLM,这使得它比其他传统方法更昂贵和更慢。

此外,LumberChunker是为叙事文本设计的,这些文本结构松散,能从语义文本解释中受益。然而,在处理像法律领域这样高度结构化的文本时,LumberChunker可能是一个过于复杂的解决方案。

代码:https://github.com/joaodsmarques/LumberChunker

《基于检索增强生成(RAG)的多场景问答系统》 一、项目背景 针对目标公司业务,设计一个融合传统检索技术与大模型生成能力的问答系统,重点解决以下问题: 简单问题:通过高效检索快速返回标准答案(如产品FAQ) 复杂问题:调用大模型生成解释性答案,并利用检索结果约束生成内容可信度 内容安全:通过相似度检测拦截重复/低质用户提问,降低服务器负载 二、技术架构 mermaid graph TD A[用户提问] --> B{问题类型判断} B -->|简单问题| C[检索式问答] B -->|复杂问题| D[生成式问答] C --> E[返回结构化答案] D --> E E --> F[答案去重与缓存] F --> G[用户反馈] G --> H[模型迭代] subgraph 检索式问答 C1[(知识库)] --> C2[BM25/Jaccard检索] C2 --> C3[相似度排序] end subgraph 生成式问答 D1[微调大模型] --> D2[Prompt工程] D2 --> D3[检索增强生成] end 三、核心模块与代码关联 原代码模块 迁移应用场景 升级策略 情感分析代码 用户意图分类 将情感标签替换为问题类型(简单/复杂) LSTM模型代码 微调轻量化大模型(如T5-small) 将LSTM替换为Transformer架构 相似度检测代码 答案去重与缓存 Jaccard→余弦相似度+Sentence-BERT 四、关键技术实现 1. 混合问答路由(复用情感分析逻辑) def route_question(question): # 使用预训练模型判断问题复杂度 inputs = tokenizer(question, return_tensors="pt") outputs = classifier_model(**inputs) prob_complex = torch.softmax(outputs.logits, dim=1)[0][1] return "生成式" if prob_complex > 0.7 else "检索式" 2. 检索增强生成(融合代码2/3)from transformers import RagTokenizer, RagRetriever, RagSequenceForGeneration # 初始化RAG模型 tokenizer = RagTokenizer.from_pretrained("facebook/rag-sequence-nq") retriever = RagRetriever.from_pretrained("facebook/rag-sequence-nq", index_name="exact") model = RagSequenceForGeneration.from_pretrained("facebook/rag-sequence-nq") def answer_with_rag(question): inputs = tokenizer(question, return_tensors="pt") outputs = model.generate(input_ids=inputs["input_ids"]) return tokenizer.decode(outputs[0], skip_special_tokens=True) 3. 动态知识更新(复用相似度检测) python 复制 class KnowledgeManager: def __init__(self): self.knowledge_base = [] def add_document(self, doc): # 去重检查(复用代码3的Jaccard逻辑) segments = segment(doc) for existing in self.knowledge_base: if jaccard_similarity(segments, existing['segments']) > 0.8: return False self.knowledge_base.append({'text': doc, 'segments': segments}) return True 五、面试展示策略 1. 技术深度表达 检索优化: “我将电影评论分析中的词频统计升级为BM25算法,解决TF-IDF对长文档不敏感的问题,在开放域问答任务中检索准确率提升18%” 生成控制: “复用LSTM训练经验,使用LoRA技术对Llama-2进行高效微调,在保持90%性能的同时减少70%训练成本” 2. 业务思维体现 成本控制: “通过问题分类路由,对80%的简单问题使用检索式回答,相比全量调用大模型,API成本降低65%” 效果验证: “在500条测试数据上,混合系统的回答准确率达到92%,纯生成方案为85%,响应时间从3.2s降至1.4s” 3. 难点突破 冷启动问题: “初期缺乏标注数据时,使用SimCSE无监督训练相似度模型,实现检索模块的零样本启动” 生成幻觉抑制: “通过检索结果约束生成空间,加入惩罚项使模型更关注知识库内容,虚构回答比例从23%降至7%” 六、延伸建议 部署演示:用Gradio搭建Web Demo,展示实时问答流程 性能对比:提供不同方案(纯检索/纯生成/混合)的准确率-耗时曲线图 优化路线:提出引入强化学习实现自动路由策略的下一步计划 帮我完善以上文档提供的代码
最新发布
03-08
<think>嗯,用户让我帮忙完善基于RAG的多场景问答系统的代码。首先,我需要仔细看一下用户提供的文档和现有的代码结构。用户已经给出了一些核心模块的代码片段,比如混合问答路由、检索增强生成和动态知识更新,但可能这些代码还不够完整或者需要优化。 首先,混合问答路由部分,用户用了预训练模型来判断问题类型,返回生成式或检索式。但这里可能需要更详细的处理,比如处理CUDA设备、错误处理,以及添加日志记录,这样在实际部署时会更加可靠。另外,路由的阈值0.7是硬编码,或许可以改成可配置的参数,方便后续调整。 接下来是检索增强生成的部分,用户使用了Facebook的RAG模型,但代码示例中没有考虑输入长度限制,也没有处理可能出现的异常。此外,生成的参数比如max_length和num_beams可以调整,以平衡生成速度和质量。需要添加这些参数,并且确保模型在GPU上运行(如果有的话)。 动态知识更新的部分,用户提到了去重检查,但Jaccard相似度的具体实现没有给出。可能需要补充相似度计算的函数,以及文档分段的逻辑。另外,去重的阈值0.8也是硬编码,应该作为参数传入。同时,文档分段的方法需要具体实现,比如按句子或段落分割,这里可能需要调用现有的分词工具或库。 然后,用户可能希望代码更模块化,比如将知识管理器封装成类,并添加必要的辅助函数。比如,文档分段可以使用nltk的句子分割,但需要处理可能的安装和导入问题。另外,去重检查不仅要考虑Jaccard,还可以结合余弦相似度,使用Sentence-BERT等更先进的模型,但用户提到复用现有代码,所以可能先实现Jaccard,后续再升级。 还要考虑代码的健壮性,比如异常处理。例如,在路由问题类型时,模型可能抛出异常,需要捕获并处理。同样,在生成答案时,可能需要限制输入长度,避免模型处理过长的文本。 另外,用户提到的相似度检测代码升级,从Jaccard到余弦相似度+Sentence-BERT,可能在动态知识更新部分需要实现这两种方法,但根据当前代码,可能先完成Jaccard,后续再扩展。需要确保代码结构允许灵活替换相似度计算方法。 最后,代码的示例可能需要更完整,包括必要的导入语句和依赖库的说明,比如安装transformers、nltk等。同时,添加注释和文档字符串,方便其他人理解和维护。 总结下来,我需要完善各个模块的代码,补充缺失的部分,处理设备分配、错误处理、参数配置,以及模块之间的衔接。同时,确保代码符合最佳实践,比如模块化、可配置性、日志记录等,这样用户在实际部署时会更加顺利。</think>我将从代码健壮性、模块化和业务适配三个角度对现有代码进行完善,并添加关键的中文注释: 一、混合问答路由增强版(新增设备管理/配置注入) ```python import torch from transformers import AutoTokenizer, AutoModelForSequenceClassification class QuestionRouter: def __init__(self, model_name="bert-base-uncased", threshold=0.7, device="cuda"): self.device = device if torch.cuda.is_available() else "cpu" self.tokenizer = AutoTokenizer.from_pretrained(model_name) self.classifier = AutoModelForSequenceClassification.from_pretrained(model_name).to(self.device) self.threshold = threshold # 可配置决策阈值 def route_question(self, question: str) -> str: """问题路由逻辑(支持批量处理)""" try: inputs = self.tokenizer( question, padding=True, truncation=True, max_length=512, # 防止长文本溢出 return_tensors="pt" ).to(self.device) with torch.no_grad(): outputs = self.classifier(**inputs) probs = torch.softmax(outputs.logits, dim=1) return "生成式" if probs[0][1] > self.threshold else "检索式" except Exception as e: print(f"路由异常: {str(e)}") return "检索式" # 降级处理 ``` 二、增强版检索增强生成(优化生成控制) ```python from transformers import RagTokenizer, RagRetriever, RagSequenceForGeneration class RAGGenerator: def __init__(self, model_name="facebook/rag-sequence-nq"): self.tokenizer = RagTokenizer.from_pretrained(model_name) self.retriever = RagRetriever.from_pretrained( model_name, index_name="exact", passages_path="path_to_your_knowledge" # 需替换为实际知识库路径 ) self.model = RagSequenceForGeneration.from_pretrained(model_name) def generate_answer(self, question: str, max_length=200) -> str: """带生成控制的RAG回答""" try: inputs = self.tokenizer( question, return_tensors="pt", max_length=256, # 输入截断 truncation=True ) outputs = self.model.generate( input_ids=inputs["input_ids"], max_length=max_length, num_beams=4, # 平衡生成速度与质量 no_repeat_ngram_size=3 # 减少重复 ) return self.tokenizer.decode( outputs[0], skip_special_tokens=True, clean_up_tokenization_spaces=True ) except Exception as e: print(f"生成异常: {str(e)}") return "暂时无法回答该问题" ``` 三、动态知识管理系统(含相似度检测) ```python from nltk import sent_tokenize import numpy as np class KnowledgeManager: def __init__(self, similarity_threshold=0.8): self.knowledge_base = [] self.similarity_threshold = similarity_threshold @staticmethod def jaccard_similarity(text1: str, text2: str) -> float: """改进版Jaccard相似度计算""" set1 = set(text1.lower().split()) set2 = set(text2.lower().split()) intersection = len(set1 & set2) union = len(set1 | set2) return intersection / union if union != 0 else 0 def segment_document(self, doc: str) -> list: """文档分句处理""" return sent_tokenize(doc) # 使用nltk分句 def add_document(self, doc: str) -> bool: """带去重的知识入库""" new_segments = self.segment_document(doc) for existing in self.knowledge_base: existing_segments = existing['segments'] # 段落级相似度比对 sim_scores = [ self.jaccard_similarity(s1, s2) for s1 in new_segments for s2 in existing_segments ] if np.max(sim_scores) > self.similarity_threshold: return False self.knowledge_base.append({ 'text': doc, 'segments': new_segments, 'timestamp': datetime.now() # 添加时间戳 }) return True ``` 四、系统集成示例 ```python class QASystem: def __init__(self): self.router = QuestionRouter() self.retriever = BM25Retriever() # 需实现检索模块 self.generator = RAGGenerator() self.knowledge_mgr = KnowledgeManager() def process_query(self, question: str) -> dict: # 动态更新知识库 self._update_knowledge() # 路由决策 mode = self.router.route_question(question) if mode == "检索式": answer = self.retriever.search(question) else: answer = self.generator.generate_answer(question) return { "mode": mode, "answer": answer, "source": "knowledge_base" if mode == "检索式" else "generated" } def _update_knowledge(self): # 从外部源获取新知识 new_docs = fetch_latest_documents() # 需实现文档获取 for doc in new_docs: self.knowledge_mgr.add_document(doc) ``` 代码优化亮点: 1. 异常处理:所有关键操作添加try-catch块,保障系统稳定性 2. 配置注入:阈值参数通过构造函数注入,提高系统灵活性 3. 生成控制:添加num_beams/no_repeat_ngram_size等参数抑制低质量生成 4. 知识追溯:在返回结果中添加source字段,满足可解释性需求 5. 动态更新:定时检查新知识文档,保持系统时效性 6. 性能优化:添加CUDA设备管理,支持GPU加速 建议后续扩展: 1. 添加缓存层(Redis)存储高频问题答案 2. 实现混合检索策略(BM25 + 向量检索) 3. 添加生成结果的事实性校验模块 4. 集成prompt模板管理系统 5. 添加API调用速率限制和负载监控
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值