【Langchain】RAG 优化:提高语义完整性、向量相关性、召回率--从字符分割到语义分块 (SemanticChunker)
RAG 优化:提高语义完整性、向量相关性、召回率–从字符分割到语义分块 (SemanticChunker)
背景:提升 RAG 检索质量
在构建基于知识库的问答系统(RAG)时,如何有效地将原始文档分割成合适的文本块(Chunks)是影响检索召回率和最终答案质量的关键步骤之一。最初,我们的项目采用了 Langchain 提供的 RecursiveCharacterTextSplitter
。
RecursiveCharacterTextSplitter
的原理相对简单:它根据预设的字符列表(如换行符、空格)递归地分割文本,并尝试维持指定的块大小 (chunk_size
) 和重叠量 (chunk_overlap
)。这种方法的优点是实现简单、速度快。然而,它的主要缺点在于 缺乏对文本语义的理解。它可能会在句子中间或者一个语义完整的段落内部进行切割,导致生成的文本块语义不完整,影响后续向量检索的相关性。当用户提问时,如果相关的上下文被分割到了不同的块中,模型可能无法获取足够的信息来生成准确的答案。
具体问题案例
- prompt:你是一个检索助手,你将根据检索到的上下文信息回答简明扼要地用户问题,接着说“以下是依据的检索信息:”,附带上你依据的上下文信息。如果根据检索到的上下文信息不足以回答用户的问题,请你直接告知:“根据检索到的上下文信息不足以回答您的问题”,并且附带上检索到的上下文信息。
- 检索文件:RAG-QA-PRD.pdf
- Q:RAG是为了解决什么问题?
- AI根据检索内容回复了两点。
!而实际原本有三点内容
[!NOTE] RAG-QA-PRD.pdf 原文本相关片段
大语言模型(后简称 LLM)是一种基于深度学习技术的自然语言处理模型,它能够理解、生成、推理和扩展文本。它可以帮助用户快速理解文本信息,并根据用户的需求生成相应的答案,它的诞生促进了新一轮的生产力解放。越来越多的人尝试将 LLM 技术应用于日常生活,而当人们将 LLM 应用于实际业务场景时会发现,通用的基础大模型基本无法满足我们的实际需求,主要有以下几方面原因:
LLM 的知识不是实时的,不具备知识更新的能力。
LLM 可能不知道你私有的领域、业务知识,无法回答私人问题。
LLM 有时会在回答中生成看似合理但实际上是错误的信息,这就是典型的"幻觉"现象。
为了解决以上问题 RAG 由此诞生,RAG 即 Retrival-Augmented Generation,是一种基于检索技术的对话系统,它可以帮助用户快速理解文本信息,并根据用户的需求生成相应的答案。RAG 具有以下优势:
这是因为RecursiveCharacterTextSplitter
的局限性,它将原本相关的文本切成了两个部分,第一个部分被召回,而第二个部分因为包含的信息更少,其向量相关性也下降了,没有被召回。
这就导致了检索质量不理想,因为RecursiveCharacterTextSplitter
既影响了语块完整性,也影响了语块的向量相关性。
探索:寻找更优的文本分割方案
为了克服 RecursiveCharacterTextSplitter
的局限性,提升检索质量,我开始调研 Langchain 提供的其他文本分割器。查阅官方文档后,我考虑了以下几种方案:
-
基于句子边界的分割器 (
NLTKTextSplitter
,SpacyTextSplitter
): 利用 NLP 工具包识别句子边界进行分割。这能保证句子完整性,但可能产生过细的粒度。 -
基于文档结构的分割器 (
MarkdownHeaderTextSplitter
,HTMLHeaderTextSplitter
): 利用 Markdown 或 HTML 的标题结构。效果好但仅适用于特定格式文档。 -
语义分块 (
SemanticChunker
): 这是 Langchain 实验性功能中的一个分割器。它利用嵌入模型 (Embeddings) 计算句子间的语义相似度,在语义关联较弱的地方进行切分。其核心目标是创建语义上内聚的文本块。
决策: SemanticChunker
考虑到我们的核心目标是 最大化文本块的语义相关性 以提升 RAG 效果,SemanticChunker
成为了最具吸引力的选项。尽管它处于实验阶段,但其设计理念与我们的需求高度契合。我们决定尝试引入它,接受其可能带来的挑战。
测试效果
我们先来看看改造效果。
-
完成了
SemanticChunker
配置与代码集成后 -
重新上传文件,这次使用
SemanticChunker
进行分块
-
新建一个会话,避免历史会话的影响
-
重新发送完全一致的问题和配置项
这次我们可以看到AI回复了完整的三个点,甚至还附带了原文中的RAG解决问题的优势。
因为它们语义相似,SemanticChunker
将它们分割在一个块中。这样就保证了语块的完整性,提高了语块的向量相关性,从而提高了召回率和检索质量。
实施:配置与代码集成
让我们来看详细的实践
1. 环境配置与依赖
SemanticChunker
依赖一些额外的库。我们需要通过包管理工具 (pdm) 安装它们:
pdm add langchain_experimental sentence-transformers bert_score
langchain_experimental
: 包含SemanticChunker
本身。sentence-transformers
: 常用于计算文本嵌入,SemanticChunker
底层依赖它。bert_score
:SemanticChunker
在某些配置或计算中断点时可能需要。
2. 核心代码修改
关键的改动发生在 src/utils/DocumentChunker.py
和 src/utils/Knowledge.py
中。
a) DocumentChunker
的改造
我们修改了 DocumentChunker
的 __init__
方法,使其能够接受 splitter_type
和 embeddings
参数:
# src/utils/DocumentChunker.py
from typing import Optional
from langchain_core.embeddings import Embeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
try:from langchain_experimental.text_splitter import SemanticChunkerLANGCHAIN_EXPERIMENTAL_AVAILABLE = True
except ImportError:LANGCHAIN_EXPERIMENTAL_AVAILABLE = FalseSemanticChunker = None
class DocumentChunker(BaseLoader):# ... (其他代码)def __init__(self,file_path: str,chunk_size: int = 300,chunk_overlap: int = 30,splitter_type: str = "recursive", # 'recursive' 或 'semantic'embeddings: Optional[Embeddings] = None, # 用于 semantic) -> None:# ... (加载器初始化代码)self.splitter_type = splitter_typeif self.splitter_type == "semantic":print("选择 SemanticChunker 进行分割。")if not LANGCHAIN_EXPERIMENTAL_AVAILABLE:raise ImportError("langchain_experimental 未安装。")if embeddings is None:raise ValueError("必须为 'semantic' 分割器提供 embeddings 参数。")if SemanticChunker is None:raise RuntimeError("SemanticChunker 未成功导入。")try:# 使用传入的 embeddings 初始化 SemanticChunkerself.text_splitter = SemanticChunker(embeddings=embeddings,breakpoint_threshold_type="percentile" # 或其他策略)print("使用 SemanticChunker 进行文本分割。")except Exception as e:print(f"初始化 SemanticChunker 时出错: {e}")raiseelif self.splitter_type == "recursive":self.text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)print(f"使用 RecursiveCharacterTextSplitter ...")else:raise ValueError(f"不支持的 splitter_type: '{self.splitter_type}'")def load(self) -> list:print(f"开始使用 '{self.splitter_type}' 分割器加载并分割文档...")# ... (调用 self.loader.load_and_split(self.text_splitter))
这个改动使得 DocumentChunker
可以根据传入的 splitter_type
选择初始化 RecursiveCharacterTextSplitter
或 SemanticChunker
。关键在于,当选择 semantic
时,它需要一个 Embeddings
对象的实例。
b) Knowledge
类传递 Embeddings
SemanticChunker
需要的 Embeddings
对象从哪里来?在我们的架构中,Knowledge
类负责处理知识库的创建和文档添加,并且它本身就持有用于向量化的 _embeddings
实例。因此,我们在 Knowledge.add_file_to_knowledge_base
方法中,将这个 _embeddings
传递给 DocumentChunker
:
# src/utils/Knowledge.py
class Knowledge:def __init__(self, _embeddings=None, reorder=False, splitter="semantic"): # 可以增加 splitter 参数控制默认行为self.reorder = reorderself._embeddings = _embeddingsself.splitter = splitter # 存储选择的分割器类型# ...async def add_file_to_knowledge_base(self, kb_id: str, file_path: str, file_name: str, file_md5: str) -> None:# ...if not self._embeddings:raise ValueError("无法处理文件,因为缺少 embedding 函数。")# --- 1. 加载和分块文档 ---try:print(f"使用 DocumentChunker (类型: {self.splitter}) 加载和分块: {file_path}")# 根据 self.splitter 决定如何实例化 DocumentChunkerloader = DocumentChunker(file_path,splitter_type=self.splitter, # 使用类实例的 splitter 配置embeddings=self._embeddings if self.splitter == "semantic" else None, # 仅在 semantic 时传递 embeddings)documents: List[Document] = loader.load()# ...except ImportError as e:print(f"错误:缺少 SemanticChunker 所需库: {e}")raiseexcept ValueError as e:print(f"配置错误: {e}")raiseexcept Exception as e:print(f"加载/分块时出错: {e}")raise# --- 2. 准备并注入元数据 ---# ...# --- 3. 添加到 ChromaDB ---# ...
这样,Knowledge
类在初始化时就可以决定使用哪种分割器(可以通过参数传入或硬编码),并在处理文件时将必要的 embeddings
对象传递给 DocumentChunker
。
关于RAG
你可能关心
- 你知不知道像打字机一样的流式输出效果是怎么实现的?AI聊天项目实战经验:流式输出的前后端完整实现!图文解说与源码地址(LangcahinAI,RAG,fastapi,Vue,python,SSE)-CSDN博客
- 如何让你的RAG-Langchain项目持久化对话历史\保存到数据库中_rag保存成数据库-CSDN博客
- 分享开源项目oneapi的部分API接口文档【oneapi?你的大模型网关】-CSDN博客
关于作者
- Github 更多开源项目
- CSDN 更多实用攻略
相关文章:

【Langchain】RAG 优化:提高语义完整性、向量相关性、召回率--从字符分割到语义分块 (SemanticChunker)
RAG 优化:提高语义完整性、向量相关性、召回率–从字符分割到语义分块 (SemanticChunker) 背景:提升 RAG 检索质量 在构建基于知识库的问答系统(RAG)时,如何有效地将原始文档分割成合适的文本块(Chunks&a…...

深入剖析扣子智能体的工作流与实战案例
前面我们已经初步带大家体验过扣子工作流,工作流程是 Coze 最为强大的功能之一,它如同扣子中蕴含的奇妙魔法工具,赋予我们的机器人处理极其复杂问题逻辑的能力。 这篇文章会带你更加深入地去理解并运用工作流解决实际问题 目录 一、工作流…...
C++----模拟实现string
模拟实现string,首先我们要知道成员变量有哪些: class _string{private:char* _str;size_t capacity;//空间有多大size_t size;//有效字符多少const static size_t npos;};const size_t _string::npos-1;//static在外面定义不需要带static,np…...
CodeMeter Runtime 安装失败排查与解决指南
CodeMeter Runtime 是威步提供的核心运行服务组件,用于加密授权的识别与管理。如果安装过程中出现异常或中断,常见原因包括系统冲突程序、数字签名校验失败、安全软件干扰或权限不足。 以下为推荐的完整排查步骤: 1. 检查并清理冲突程序或驱动…...

基于K8s日志审计实现攻击行为检测
K8s日志审计以一种事件溯源的方式完整记录了所有API Server的交互请求。当K8s集群遭受入侵时,安全管理员可以通过审计日志进行攻击溯源,通过分析攻击痕迹,找到攻击者的入侵行为并还原攻击者的攻击路径,修复安全问题。 在本篇文章中…...

【Linux网络编程】应用层协议HTTP(实现一个简单的http服务)
目录 前言 一,HTTP协议 1,认识URL 2,urlencode和urldecode 3,HTTP协议请求与响应格式 二,myhttp服务器端代码的编写 HTTP请求报文示例 HTTP应答报文示例 代码编写 网络通信模块 处理请求和发送应答模块 结…...

短视频+直播商城系统源码全解析:音视频流、商品组件逻辑剖析
时下,无论是依托私域流量运营的品牌方,还是追求用户粘性与转化率的内容创作者,搭建一套完整的短视频直播商城系统源码,已成为提升用户体验、增加商业变现能力的关键。本文将围绕三大核心模块——音视频流技术架构、商品组件设计、…...

STM32定时器---基本定时器
目录 一、定时器的概述 二、时基单元 三、基本定时器的的时序 (1)预分频器时序 (2)计数器时序 四、基本定时器的使用 一、定时器的概述 在没有定时器的时候,我们想要延时往往都是写一个Delay函数,里面…...
mysql快速在不同库中执行相同的sql
目录 背景 解决方案 方式一:利用变量拼接好sql,复制出来执行(简单,推荐) 方式二:使用存储过程和游标实现(比较复杂,脚本需要拼接一个完整的,也比较麻烦,不…...

大模型微调 - transformer架构
什么是Transformer Transformer 架构是由 Vaswani 等人在 2017 年提出的一种深度学习模型架构,首次发表于论文《Attention is All You Need》中 Transformer 的结构 Transformer 编码器(Encoder) 解码器(Decoder) …...
【器件专题1——IGBT第1讲】IGBT:电力电子领域的 “万能开关”,如何撑起新能源时代?
一、IGBT 是什么?重新认识这个 “低调的电力心脏” 你可能没听过 IGBT,但一定用过它驱动的设备:家里的变频空调、路上的电动汽车、屋顶的光伏逆变器,甚至高铁和电网的核心部件里,都藏着这个 “电力电子开关的瑞士军刀”…...
文件IO(Java)
注:此博文为本人学习过程中的笔记 1.概念 狭义上的文件是指保存在硬盘上的文件,广义上指操作系统进行资源管理的一种机制,很多软件/硬件资源都可以抽象成文件,这里我们针对的是狭义上的文件。 在硬盘里还有文件夹,这…...
常见缓存淘汰算法(LRU、LFU、FIFO)的区别与实现
一、前言 缓存淘汰算法主要用于在内存资源有限的情况下,优化缓存空间的使用效率。以确保缓存系统在容量不足时能够智能地选择需要移除的数据。 二、LRU(Least Recently Used) 核心思想:淘汰最久未被访问的数据。实现方式&#x…...

Sentinel数据S2_SR_HARMONIZED连续云掩膜+中位数合成
在GEE中实现时,发现简单的QA60是无法去云的,最近S2地表反射率数据集又进行了更新,原有的属性集也进行了变化,现在的SR数据集名称是“S2_SR_HARMONIZED”。那么: 要想得到研究区无云的图像,可以参考执行以下…...

HTMLCSS模板实现水滴动画效果
.container 类:定义了页面的容器样式。 display: flex:使容器成为弹性容器,方便对其子元素进行布局。justify-content: center 和 align-items: center:分别使子元素在水平和垂直方向上居中对齐。min-height: 100vh:设…...
Cesium实现地形可视域分析
Cesium实现可视化分析 一、地形可视域主要实现技术(Ray + 地形碰撞检测) Cesium 本身的 Ray 类可以用来执行非常精确的射线检测,我们可以结合地形高度(sample)来逐点检测光线是否与 terrain 相交,从而判断是否可见。 1.1 优势 实时判断每条射线是否被 terrain 遮挡地形…...
前端如何获取文件的 Hash 值?多种方式详解、对比与实践指南
文章目录 前言一、Hash 值为何重要?二、Hash 值基础知识2.1 什么是 Hash?2.2 Hash 在前端的应用场景2.3 常见的 Hash 算法(MD5、SHA 系列) 三、前端获取文件 Hash 的常用方式3.1 使用 SparkMD5 计算 MD5 值3.2 使用 Web Crypto AP…...

【数据可视化艺术·应用篇】三维管线分析如何重构城市“生命线“管理?
在智慧城市、能源管理、工业4.0等领域的快速发展中,地下管线、工业管道、电力通信网络等“城市血管”的复杂性呈指数级增长。传统二维管理模式已难以应对跨层级、多维度、动态变化的管线管理需求。三维管线分析技术应运而生,成为破解这一难题的核心工具。…...
蓝桥杯 16.对局匹配
对局匹配 原题目链接 题目描述 小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。 小明发现,网站的自动对局系统在匹配对手时,只会将积分差恰好是 K 的两名用户匹配在一起。如果两人分差小…...

【MinerU】:一款将PDF转化为机器可读格式的工具——RAG加强(Docker版本)
目录 创建容器 安装miniconda 安装mineru CPU运行 GPU加速 多卡问题 创建容器 构建Dockerfile文件 开启ssh服务,设置密码为1234等操作 # 使用官方 Ubuntu 24.04 镜像 FROM ubuntu:24.04# 安装基础工具和SSH服务 RUN apt-get update && \apt-get ins…...
DeepSeek回答过于笼统,提示词如何优化
针对DeepSeek回答过于笼统的问题,可通过以下方法优化,使输出更具体、详细: 一、优化提示词设计 明确具体要求 在提问中嵌入「背景限制示例」,例如: “作为跨境电商运营新手,请详细说明如何优化亚马逊产品标…...
C语言实现贪心算法
一、贪心算法核心思想 特征:在每一步选择中都采取当前状态下最优(局部最优)的选择,从而希望导致全局最优解 适用场景:需要满足贪心选择性质和最优子结构性质 二、经典贪心算法示例 1. 活动选择问题 目标:…...
全球碳化硅晶片市场深度解析:技术迭代、产业重构与未来赛道争夺战(2025-2031)
一、行业全景:从“材料突破”到“能源革命”的核心引擎 碳化硅(SiC)作为第三代半导体材料的代表,凭借其宽禁带(3.26eV)、高临界击穿场强(3MV/cm)、高热导率(4.9W/cmK&…...
FreeRTOS学习笔记【10】-----任务上下文切换
1 概念性内容 开机到调度需要经历的步骤有: 系统初始化任务创建启动调度器上下文切换时间分片任务执行 1.1 任务本质 FreeRTOS 的 任务(Task)本质上就是一个运行在任务自己的栈区中无限循环的函数 一段上下文(context&#x…...

Appium自动化开发环境搭建
自动化 文章目录 自动化前言 前言 Appium是一款开源工具,用于自动化iOS、Android和Windows桌面平台上的本地、移动web和混合应用程序。原生应用是指那些使用iOS、Android或Windows sdk编写的应用。移动网页应用是通过移动浏览器访问的网页应用(appum支持iOS和Chrom…...

C++学习-入门到精通-【1】C++编程入门,输入/输出和运算符
C学习-入门到精通-【1】C编程入门,输入/输出和运算符 C编程入门,输入/输出和运算符 C学习-入门到精通-【1】C编程入门,输入/输出和运算符第一个C程序:输出一行文本算术运算 第一个C程序:输出一行文本 // 文本打印程序…...
UOJ 228 基础数据结构练习题 Solution
Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1,a2,⋯,an),有 m m m 个操作分三种: add ( l , r , k ) \operatorname{add}(l,r,k) add(l,r,k):对每个 i ∈ [ l , r ] i\in[l,r] i∈[l,r] 执行 …...

面向高性能运动控制的MCU:架构创新、算法优化与应用分析
摘要:现代工业自动化、汽车电子以及商业航天等领域对运动控制MCU的性能要求不断提升。本文以国科安芯的MCU芯片AS32A601为例,从架构创新、算法优化到实际应用案例,全方位展示其在高性能运动控制领域的优势与潜力。该MCU以32位RISC-V指令集为基…...

某地农产品交易中心钢网架自动化监测项目
1. 项目简介 本项目规划建设现代物流产业园,新建6万平方米仓库,具体为新建3栋钢构仓库2万平方米,2栋砖混结构仓库1万平方米,3栋交易中心2万平方米,改造现有3栋3层砖混结构仓库1万平方米,配备智能化仓库物流…...

【无人机】无人机位置估计出现偏差的原因分析
目录 #0、原因分析 #1、过度振动的测定 #2、确定过度陀螺仪偏差 #3、偏航精度差的测定 #4、确定 GPS 精度差 #5、确定 GPS 数据丢失 #6、气压计地面效应补偿 #0、原因分析 位置背离的最常见原因是: 参考:Using the ECL EKF | PX4 Guide (v1.15)…...