【Langchain】RAG 优化:提高语义完整性、向量相关性、召回率--从字符分割到语义分块 (SemanticChunker)

news2025/4/27 14:43:09

RAG 优化:提高语义完整性、向量相关性、召回率–从字符分割到语义分块 (SemanticChunker)

背景:提升 RAG 检索质量

在构建基于知识库的问答系统(RAG)时,如何有效地将原始文档分割成合适的文本块(Chunks)是影响检索召回率和最终答案质量的关键步骤之一。最初,我们的项目采用了 Langchain 提供的 RecursiveCharacterTextSplitter
RecursiveCharacterTextSplitter 的原理相对简单:它根据预设的字符列表(如换行符、空格)递归地分割文本,并尝试维持指定的块大小 (chunk_size) 和重叠量 (chunk_overlap)。这种方法的优点是实现简单、速度快。然而,它的主要缺点在于 缺乏对文本语义的理解。它可能会在句子中间或者一个语义完整的段落内部进行切割,导致生成的文本块语义不完整,影响后续向量检索的相关性。当用户提问时,如果相关的上下文被分割到了不同的块中,模型可能无法获取足够的信息来生成准确的答案。

具体问题案例

  • prompt:你是一个检索助手,你将根据检索到的上下文信息回答简明扼要地用户问题,接着说“以下是依据的检索信息:”,附带上你依据的上下文信息。如果根据检索到的上下文信息不足以回答用户的问题,请你直接告知:“根据检索到的上下文信息不足以回答您的问题”,并且附带上检索到的上下文信息。
  • 检索文件:RAG-QA-PRD.pdf
  • Q:RAG是为了解决什么问题?
  • AI根据检索内容回复了两点。
    image.png

!而实际原本有三点内容

[!NOTE] RAG-QA-PRD.pdf 原文本相关片段
大语言模型(后简称 LLM)是一种基于深度学习技术的自然语言处理模型,它能够理解、生成、推理和扩展文本。它可以帮助用户快速理解文本信息,并根据用户的需求生成相应的答案,它的诞生促进了新一轮的生产力解放。越来越多的人尝试将 LLM 技术应用于日常生活,而当人们将 LLM 应用于实际业务场景时会发现,通用的基础大模型基本无法满足我们的实际需求,主要有以下几方面原因:

  1. LLM 的知识不是实时的,不具备知识更新的能力。

  2. LLM 可能不知道你私有的领域、业务知识,无法回答私人问题。

  3. LLM 有时会在回答中生成看似合理但实际上是错误的信息,这就是典型的"幻觉"现象。

为了解决以上问题 RAG 由此诞生,RAG 即 Retrival-Augmented Generation,是一种基于检索技术的对话系统,它可以帮助用户快速理解文本信息,并根据用户的需求生成相应的答案。RAG 具有以下优势:

这是因为RecursiveCharacterTextSplitter 的局限性,它将原本相关的文本切成了两个部分,第一个部分被召回,而第二个部分因为包含的信息更少,其向量相关性也下降了,没有被召回。
这就导致了检索质量不理想,因为RecursiveCharacterTextSplitter既影响了语块完整性,也影响了语块的向量相关性

探索:寻找更优的文本分割方案

为了克服 RecursiveCharacterTextSplitter 的局限性,提升检索质量,我开始调研 Langchain 提供的其他文本分割器。查阅官方文档后,我考虑了以下几种方案:

  1. 基于句子边界的分割器 (NLTKTextSplitter, SpacyTextSplitter): 利用 NLP 工具包识别句子边界进行分割。这能保证句子完整性,但可能产生过细的粒度。

  2. 基于文档结构的分割器 (MarkdownHeaderTextSplitter, HTMLHeaderTextSplitter): 利用 Markdown 或 HTML 的标题结构。效果好但仅适用于特定格式文档。

  3. 语义分块 (SemanticChunker): 这是 Langchain 实验性功能中的一个分割器。它利用嵌入模型 (Embeddings) 计算句子间的语义相似度,在语义关联较弱的地方进行切分。其核心目标是创建语义上内聚的文本块。

决策: SemanticChunker

考虑到我们的核心目标是 最大化文本块的语义相关性 以提升 RAG 效果,SemanticChunker 成为了最具吸引力的选项。尽管它处于实验阶段,但其设计理念与我们的需求高度契合。我们决定尝试引入它,接受其可能带来的挑战。

测试效果

我们先来看看改造效果。

  1. 完成了SemanticChunker配置与代码集成后

  2. 重新上传文件,这次使用SemanticChunker进行分块
    6d1805ed2e044200a7b00435e90a71e.png

  3. 新建一个会话,避免历史会话的影响

  4. 重新发送完全一致的问题和配置项
    image.png

这次我们可以看到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.pysrc/utils/Knowledge.py 中。
a) DocumentChunker 的改造
我们修改了 DocumentChunker__init__ 方法,使其能够接受 splitter_typeembeddings 参数:

# 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 SemanticChunker
    LANGCHAIN_EXPERIMENTAL_AVAILABLE = True
except ImportError:
    LANGCHAIN_EXPERIMENTAL_AVAILABLE = False
    SemanticChunker = 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_type
        if 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 初始化 SemanticChunker
                self.text_splitter = SemanticChunker(
                    embeddings=embeddings,
                    breakpoint_threshold_type="percentile" # 或其他策略
                )
                print("使用 SemanticChunker 进行文本分割。")
            except Exception as e:
                print(f"初始化 SemanticChunker 时出错: {e}")
                raise
        elif 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 选择初始化 RecursiveCharacterTextSplitterSemanticChunker。关键在于,当选择 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 = reorder
        self._embeddings = _embeddings
        self.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 决定如何实例化 DocumentChunker
            loader = 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}")
            raise
        except ValueError as e:
            print(f"配置错误: {e}")
            raise
        except 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 更多实用攻略

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2343208.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入剖析扣子智能体的工作流与实战案例

前面我们已经初步带大家体验过扣子工作流,工作流程是 Coze 最为强大的功能之一,它如同扣子中蕴含的奇妙魔法工具,赋予我们的机器人处理极其复杂问题逻辑的能力。 这篇文章会带你更加深入地去理解并运用工作流解决实际问题 目录 一、工作流…

基于K8s日志审计实现攻击行为检测

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

【Linux网络编程】应用层协议HTTP(实现一个简单的http服务)

目录 前言 一,HTTP协议 1,认识URL 2,urlencode和urldecode 3,HTTP协议请求与响应格式 二,myhttp服务器端代码的编写 HTTP请求报文示例 HTTP应答报文示例 代码编写 网络通信模块 处理请求和发送应答模块 结…

短视频+直播商城系统源码全解析:音视频流、商品组件逻辑剖析

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

STM32定时器---基本定时器

目录 一、定时器的概述 二、时基单元 三、基本定时器的的时序 (1)预分频器时序 (2)计数器时序 四、基本定时器的使用 一、定时器的概述 在没有定时器的时候,我们想要延时往往都是写一个Delay函数,里面…

大模型微调 - transformer架构

什么是Transformer Transformer 架构是由 Vaswani 等人在 2017 年提出的一种深度学习模型架构,首次发表于论文《Attention is All You Need》中 Transformer 的结构 Transformer 编码器(Encoder) 解码器(Decoder) …

Sentinel数据S2_SR_HARMONIZED连续云掩膜+中位数合成

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

HTMLCSS模板实现水滴动画效果

.container 类:定义了页面的容器样式。 display: flex:使容器成为弹性容器,方便对其子元素进行布局。justify-content: center 和 align-items: center:分别使子元素在水平和垂直方向上居中对齐。min-height: 100vh:设…

【数据可视化艺术·应用篇】三维管线分析如何重构城市“生命线“管理?

在智慧城市、能源管理、工业4.0等领域的快速发展中,地下管线、工业管道、电力通信网络等“城市血管”的复杂性呈指数级增长。传统二维管理模式已难以应对跨层级、多维度、动态变化的管线管理需求。三维管线分析技术应运而生,成为破解这一难题的核心工具。…

【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…

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程序:输出一行文本 // 文本打印程序…

面向高性能运动控制的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)…

element-plus(vue3)表单el-select下拉框的远程分页下拉触底关键字搜索实现

一、基础内核-自定义指令 1.背景 2.定义 3.使用 4.注意 当编辑时需要回显,此时由于分页导致可能匹配不到对应label文本显示,此时可以这样解决 二、升级使用-二次封装组件 三、核心代码 1.自定义指令 定义 ----------------selectLoadMoreDirective.…

轻松完成视频创作,在线视频编辑器,无需下载软件,功能多样实用!

小白工具的在线视频编辑https://www.xiaobaitool.net/videos/edit/ 功能丰富、操作简便,在线裁剪或编辑视频工具,轻松完成视频创作能满足多种视频编辑需求。 格式支持广泛:可编辑超百种视频格式,基本涵盖常见和小众视频格式&#…

豆瓣图书数据采集与可视化分析(三)- 豆瓣图书数据统计分析

文章目录 前言一、数据读取与保存1. 读取清洗后数据2. 保存数据到CSV文件3. 保存数据到MySQL数据库 二、不同分类统计分析1. 不同分类的图书数量统计分析2. 不同分类的平均评分统计分析3. 不同分类的平均评价人数统计分析4. 不同分类的平均价格统计分析5. 分类综合分析 三、不同…

c++进阶——类与继承

文章目录 继承继承的基本概念继承的基本定义继承方式继承的一些注意事项 继承类模板 基类和派生类之间的转换继承中的作用域派生类的默认成员函数默认构造函数拷贝构造赋值重载析构函数默认成员函数总结 不能被继承的类继承和友元继承与静态成员多继承及其菱形继承问题继承模型…

复杂地形越野机器人导航新突破!VERTIFORMER:数据高效多任务Transformer助力越野机器人移动导航

作者: Mohammad Nazeri 1 ^{1} 1, Anuj Pokhrel 1 ^{1} 1, Alexandyr Card 1 ^{1} 1, Aniket Datar 1 ^{1} 1, Garrett Warnell 2 , 3 ^{2,3} 2,3, Xuesu Xiao 1 ^{1} 1单位: 1 ^{1} 1乔治梅森大学计算机科学系, 2 ^{2} 2美国陆军研究实验室&…