RAG的文档拆分策略

目录

Langchain支持的文档拆分

智谱AI采用的文档拆分策略

Meta KDD Cup'24

Qanything

总结

Langchain支持的文档拆分

名字具体教程分割字符是否添加metadata描述
递归式RecursiveCharacterTextSplitter、RecursiveJsonSplitter用户自定义的字符递归拆分文本。这种拆分是试图将相关的文本片段彼此相邻。这是开始拆分文本的方法。recommended way
HTMLHTMLHeaderTextSplitter、HTMLSectionSplitterHTML字符根据特定于 HTML 的字符拆分文本。值得注意的是,这添加了有关该块来自何处的相关信息(基于 HTML)
MarkdownMarkdownHeaderTextSplitterMarkdown字符根据特定于 Markdown 的字符拆分文本。值得注意的是,这添加了有关该 chunk 来源的相关信息(基于 Markdown)
代码many languages代码 (Python、JS)字符根据特定于编码语言的字符拆分文本。有 15 种不同的语言可供选择。
Tokenmany classesToken在标记上拆分文本。有几种不同的方法来测量代币。
字符CharacterTextSplitter用户定义的字符根据用户定义的字符拆分文本。更简单的方法之一。
语义分块SemanticChunker句子首先对句子进行拆分。然后,如果它们在语义上足够相似,则将它们彼此相邻地合并。摘自 Greg Kamradt
AI21 语义文本拆分器AI21SemanticTextSplitter识别形成连贯文本片段的不同主题,并沿这些主题进行拆分。

智谱AI采用的文档拆分策略

详见https://mp.weixin.qq.com/s/DC0-so_8pVUfcz7lGhYwlQ,记录的是24年6月的方式

采用small to big 的策略,即在原始文档切片基础上,扩展了更多粒度更小的文档切片。检索文档时如果检索到粒度细致的切片,会递归检索到其原始大切片,然后再将原始节点做为检索结果提交给 LLM

类似于ParentDocumentRetriever策略,首先利用CharacterTextSplitter将文档拆分成短chunks,用于向量化和检索。在短chunk之上,有一个长chunk(父chunk),query检索命中小chunk时,仅将父chunk返回给LLM。优势在于

  1. 文本向量更容易建模短文本的语义含义。例如query是苹果,短文本是“张三爱吃苹果”,长文本是“李四爱吃梨,张三爱吃苹果,他们都爱吃树上的水果”,很明显,query更容易命中短文本

  2. 长文本能包含更多上下文关联,将长文本给到LLM,能回答更复杂问题。例如query是“有谁和张三一样,也爱苹果”,短文本是“张三爱吃苹果”,长文本是“张三爱吃苹果,李四和张三一样,也爱吃”。很明显,query更容易命中短文本,但短文本不足以回答问题

Meta KDD Cup'24

ACM SIGKDD (Knowledge Discovery and Data Mining,简称 KDD)是数据挖掘领域的国际顶级会议。KDD Cup比赛由SIGKDD主办,自1997年开始每年举办一次,是目前数据挖掘领域最具影响力的赛事。

本次比赛共包含WEB-BASED RETRIEVAL SUMMARIZATION、KNOWLEDGE GRAPH AND WEB AUGMENTATION、END-TO-END RAG,冠军方案采用的文档拆分策略和智谱AI一致,详见https://openreview.net/forum?id=oWNPeoP1uC

Qanything

采用ParentDocumentRetriever策略,代码详见https://github.com/netease-youdao/QAnything/blob/qanything-v2/qanything_kernel/core/retriever/parent_retriever.py

class ParentRetriever:def __init__(self, vectorstore_client: VectorStoreMilvusClient, mysql_client: KnowledgeBaseManager, es_client: StoreElasticSearchClient):self.mysql_client = mysql_clientself.vectorstore_client = vectorstore_client# This text splitter is used to create the parent documentsinit_parent_splitter = RecursiveCharacterTextSplitter(separators=SEPARATORS,chunk_size=DEFAULT_PARENT_CHUNK_SIZE,chunk_overlap=0,length_function=num_tokens_embed)# # This text splitter is used to create the child documents# # It should create documents smaller than the parentinit_child_splitter = RecursiveCharacterTextSplitter(separators=SEPARATORS,chunk_size=DEFAULT_CHILD_CHUNK_SIZE,chunk_overlap=int(DEFAULT_CHILD_CHUNK_SIZE / 4),length_function=num_tokens_embed)self.retriever = SelfParentRetriever(vectorstore=vectorstore_client.local_vectorstore,docstore=MysqlStore(mysql_client),child_splitter=init_child_splitter,parent_splitter=init_parent_splitter,)self.backup_vectorstore: Optional[Milvus] = Noneself.es_store = es_client.es_storeself.parent_chunk_size = DEFAULT_PARENT_CHUNK_SIZE@get_time_asyncasync def insert_documents(self, docs, parent_chunk_size, single_parent=False):insert_logger.info(f"Inserting {len(docs)} documents, parent_chunk_size: {parent_chunk_size}, single_parent: {single_parent}")if parent_chunk_size != self.parent_chunk_size:self.parent_chunk_size = parent_chunk_sizeparent_splitter = RecursiveCharacterTextSplitter(separators=SEPARATORS,chunk_size=parent_chunk_size,chunk_overlap=0,length_function=num_tokens_embed)child_chunk_size = min(DEFAULT_CHILD_CHUNK_SIZE, int(parent_chunk_size / 2))child_splitter = RecursiveCharacterTextSplitter(separators=SEPARATORS,chunk_size=child_chunk_size,chunk_overlap=int(child_chunk_size / 4),length_function=num_tokens_embed)self.retriever = SelfParentRetriever(vectorstore=self.vectorstore_client.local_vectorstore,docstore=MysqlStore(self.mysql_client),child_splitter=child_splitter,parent_splitter=parent_splitter)# insert_logger.info(f'insert documents: {len(docs)}')ids = None if not single_parent else [doc.metadata['doc_id'] for doc in docs]return await self.retriever.aadd_documents(docs, parent_chunk_size=parent_chunk_size,es_store=self.es_store, ids=ids, single_parent=single_parent)async def get_retrieved_documents(self, query: str, partition_keys: List[str], time_record: dict,hybrid_search: bool, top_k: int):milvus_start_time = time.perf_counter()expr = f'kb_id in {partition_keys}'# self.retriever.set_search_kwargs("mmr", k=VECTOR_SEARCH_TOP_K, expr=expr)self.retriever.set_search_kwargs("similarity", k=top_k, expr=expr)query_docs = await self.retriever.aget_relevant_documents(query)for doc in query_docs:doc.metadata['retrieval_source'] = 'milvus'milvus_end_time = time.perf_counter()time_record['retriever_search_by_milvus'] = round(milvus_end_time - milvus_start_time, 2)if not hybrid_search:return query_docstry:# filter = []# for partition_key in partition_keys:filter = [{"terms": {"metadata.kb_id.keyword": partition_keys}}]es_sub_docs = await self.es_store.asimilarity_search(query, k=top_k, filter=filter)es_ids = []milvus_doc_ids = [d.metadata[self.retriever.id_key] for d in query_docs]for d in es_sub_docs:if self.retriever.id_key in d.metadata and d.metadata[self.retriever.id_key] not in es_ids and d.metadata[self.retriever.id_key] not in milvus_doc_ids:es_ids.append(d.metadata[self.retriever.id_key])es_docs = await self.retriever.docstore.amget(es_ids)es_docs = [d for d in es_docs if d is not None]for doc in es_docs:doc.metadata['retrieval_source'] = 'es'time_record['retriever_search_by_es'] = round(time.perf_counter() - milvus_end_time, 2)debug_logger.info(f"Got {len(query_docs)} documents from vectorstore and {len(es_sub_docs)} documents from es, total {len(query_docs) + len(es_docs)} merged documents.")query_docs.extend(es_docs)except Exception as e:debug_logger.error(f"Error in get_retrieved_documents on es_search: {e}")return query_docs

总结

当前(20240925)的时间节点下,效果比较好的文档拆分策略为CharacterTextSplitter+ParentDocumentRetriever,能够较好得协调检索的精度和LLM的效果

CharacterTextSplitter:https://python.langchain.com/v0.1/docs/modules/data_connection/document_transformers/character_text_splitter/

ParentDocumentRetriever:https://www.langchain.com.cn/modules/data_connection/retrievers/parent_document_retriever

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 UWA Gears 测试小游戏性能

UWA Gears 是UWA最新发布的无SDK性能分析工具。针对移动平台,提供了实时监测和截帧分析功能,帮助您精准定位性能热点,提升应用的整体表现。 随着小游戏的规模和用户量持续增长,玩家对于小游戏的性能要求也越来越高。为了能够给玩…

【大数据】元数据是解锁数据价值的关键

在信息爆炸的数字时代,数据无处不在,它以多种形式存在,从文本文档到数字图片,从交易记录到科学测量。然而,如果没有合适的数据管理和理解,这些数据的价值就会大打折扣。如何提高数据价值呢?这就…

力扣 简单 206.反转链表

文章目录 题目介绍题解 题目介绍 题解 法一:双指针 在遍历链表时,将当前节点的 next 改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引…

C# CS1612 尝试修改集合中值类型的情况

在C#中,发现尝试直接修改集合中值类型的中的值发生报错 提示“它不是变量”,通过官方索引的链接可知,尝试修改某一值类型,但是该值类型作为中间表达式的结果生成但不存储在变量中,会发生报错。 正确做法是将其赋值给局…

【软考】传输层协议TCP与UDP

目录 1. TCP1.1 说明1.2 三次握手 2. UDP3. 例题3.1 例题1 1. TCP 1.1 说明 1.TCP(Transmission Control Protocol,传输控制协议)是整个 TCP/IP 协议族中最重要的协议之一。2.它在IP提供的不可靠数据服务的基础上为应用程序提供了一个可靠的、面向连接的、全双工的…

芝法酱学习笔记(0.3)——SpringBoot下使用mybatis做增删改查和报表

零、前言 书接上回,我们搭建了windows下的开发环境,并给出了一个hello world级别的多模块SpringBoot项目。 毕竟java后端开发,离不开数据库的操作,为方便后面内容的讲解,这里再做一期铺垫,core模块下新增一…

安卓13去掉下拉菜单的Dump SysUI 堆的选项 android13删除Dump SysUI 堆

总纲 android13 rom 开发总纲说明 文章目录 1.前言2.问题分析3.代码分析3.1 位置13.2 位置24.代码修改5.编译6.彩蛋1.前言 客户需要去掉下拉菜单里面的Dump SysUI 堆图标,不让使用这个功能。 2.问题分析 android的下拉菜单在systemui里面,这里我们只需要定位到对应的添加代…

跟王道学c记录

scanf int a; scanf("%d",&a); 一定要有取地址符 printf 用%f精度修饰符指定想要的小数位数。例如,%5.2f会至少显示5位数字并带有2位小 数的浮点数 用%s精度修饰符简单地表示一个最大的长度,以补充句点前的最小字段长度 printf 数的所有输出都是右对齐的,除非…

Jetpack02-LiveData 数据驱动UI更新(类似EventBus)

前提 LiveData使用了Lifecycle的生命周期,阅读本文前,请先了解Lifecycle源码。 简介 LiveData本质是数据类型,当改变数据的时候,会通知观察者,且只在界面可见的时候才会通知观察者。只能在主线程注册观察者&#xf…

WebRTC编译后替换libwebrtc.aar时提示找不到libjingle_peerconnection_so.so库

Loading native library: jingle_peerconnection_so 问题原因:编译的时候只编译了armeabi-v7a的版本,但是应用程序是arm64-v8a,所以无法运行 解决方法:更新编译脚本,加上arm64-v8a进行编译 ./tools_webrtc/android/bu…

【Docker】如何让docker容器正常使用nvidia显卡

首先确保宿主机正常安装了显卡驱动 nvidia-smi打印显卡信息如下: 安装nvidia-container-toolkit工具 sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit sudo systemctl restart docker运行如下命令测试显卡是否在容器内可用 …

Maya学习笔记:物体的层级关系

文章目录 父子关系设置父子关系同时显示两个大纲视图 组 父子关系 设置父子关系 设置父子物体: 方法1 先选择子物体,按住shift再选中父物体,按P或者G键 方法2 在大纲视图中按住鼠标中间,拖动一个物体到另一个物体上 取消父子关…

TON生态系统开发指南:从零开始构建你的Web3应用

随着Web3的不断发展,TON(The Open Network)生态系统逐渐成为备受瞩目的区块链平台。依托其与Telegram的深度整合,TON生态为开发者提供了一个极具潜力的开发环境,特别是在社交、支付和金融这三个核心领域。本文旨在帮助…

基于STM32的Zeta型数控电源设计

本设计基于STM32F103C6T6为主控芯片,基于Zeta型DC/DC电源的拓扑结构设计一种数控电源。系统包含单片机主控模块、Zeta型升降压模块、驱动模块、电流采样模块、电压采样模块、OLED显示模块、电源模块及按键模块。用电流采样模块采集电流,电压采样模块采集…

【图灵完备 Turing Complete】游戏经验攻略分享 Part.5 编程

编程部分的话,第一关会让你输入机器码,这一章节还是比较简单的,因为操作码是固定给出的,只需要根据题意去编写,完成这章目的是为了解锁下面的关卡。 输入,移动COPY之后进行运算,然后输出。 激光…

【MySql】在ubuntu下安装MySql数据库

目录 查看操作系统版本 添加 MySql APT源 访问下载页面并下载发布包 安装发布包 执行安装命令 从MySql APT源更新包信息 安装MySql 执行安装命令 查看MySql状态 开启自启动 登录MySql 查看操作系统版本 rootVM-24-2-ubuntu:~# lsb_release -a No LSB modules are ava…

stm32 的UART串口波特率115200bps,一秒钟能发多少个数据包,实测给出结论

问题描述 之前觉得串口波特率115200bps,算下来115.2kbps,一秒钟发1k个数据包很容易就实现。 但是实际应用的时候,就发现不一样了。 每个数据包格式如下: 16进制:0A 55 55 00 0D 0A 55 06 24 05 4C 05 5F 05 CE 05 …

AR传送门+特定区域显示内容+放大镜 效果着色器使用

AR传送门特定区域显示内容放大镜 效果 关键词:Portal Mask 1、教程链接: AR 传送门教程 Unity - Portal Mask Implementation - Part 4_哔哩哔哩_bilibili 应用案例效果: 2、案例下载地址:使用unity 2021.3.33f1 obi 工具…

vue echarts tooltip动态绑定模板,并且处理vue事件绑定

先上代码: tooltip: {// 这里是车辆iconshow: true,// trigger: "item",// backgroundColor: "transparent",appendToBody: true,textStyle: {color: "#ffffff" //设置文字颜色},formatter: (params) > {const TruckTooltip Vue.…

全新热门电商API接口,实现闲鱼商品详细搜索功能

近年来,电商行业蓬勃发展,API(Application Programming Interface)接口已经成为电商平台的重要组成部分。API接口不仅可以实现平台间的数据交互,还可以为开发者提供丰富的功能,满足用户多样化的需求。在这个…