引言
在RAG系列:解析优化 - 不同文件类型统一转换成Markdown一文中我们介绍了将不同文件类型统一解析转换成 Markdown 文件的好处。本文我们接着这篇文章解析转换后的 Markdown 文件,介绍下基于 Markdown 语法的文档切分方法。
关于指标
在RAG系列:系统评估 - 五个主流评估指标详解一文中我们介绍了评估 RAG 系统的五个主流指标,从本文开始,我会根据不同优化阶段来选择要重点关注的指标,不必要每次都关注五个指标的表现,这样可以让我们的优化更聚焦,通过优化每个阶段的重点指标,从而逐步优化系统的各个环节。
在不同优化阶段需要重点关注的指标:
- 问题优化:上下文召回率、答案正确性;
- 切分优化:上下文召回率、上下文相关性、答案正确性;
- 检索优化:上下文召回率、上下文相关性、答案正确性;
- 生成优化:答案忠实度、答案相关性、答案正确性。
代码实践
本文完整代码地址[1]
基于换行符&空格等字符切分
在之前的示例中,我们用的切分方法是 langchainjs 的 RecursiveCharacterTextSplitter,这是默认推荐(常用)的切分方法。与简单的基于字符计数或固定分隔符的分割方法不同,RecursiveCharacterTextSplitter 使用一种递归的方法来尝试在多个级别的分隔符上进行分割,从而尽量保持文本的语义完整性。
RecursiveCharacterTextSplitter 默认分隔符序列是 ["\n\n", "\n", " ", ""],意味着它会先尝试按段落分割,然后是句子,接着是单词,最后是逐字符处理。
代码实现:
复制async function splitDocuments_v50(docs) { const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 500, // 文本切分大小 chunkOverlap: 50, // 文本切分重叠大小 }); const documents = await textSplitter.splitDocuments(docs); return documents; }
使用该方法切分出来的文档块大小相对比较均匀,都比较接近设置的 chunkSize(500):
[379,425,396,376,425,206,495,400,248,299,304,335,314,484,485,425,474,479,352,378,441,443,460,400,398,211,481,346,307,476,414,358,494,480,412,367,383,485,421,407,494,487,334,448,493,397,443,410,400,388,460,492,423,55,484,498,488,422,485,414,382,361,431,157,482,485,192,271,332,424,150,456,410,427,491,477,379,202,461,456,48,39,494,235,474,407,405,248,472,473,134,351,407,102,491,413,486,478,210,495,476,163,383,421,406,486,431,224,488,488,93,487,442,388,496,169,487,485,361,412,468,357,421,362,489,445,468,251,495,477,101,202,498,211,494,328,470,422,332,246,295,393,264]
以下是我们用该方法对 《2024少儿编程教育行业发展趋势报告.md》文件进行切分后的结果:
复制[ { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n\n\n多鲸教育研究院/ 2024 年 1 月\n\n## 少儿编程教育行业图谱\n\n### To B / To G\n\n", }, { "pageContent":"\n\n## 专家观点\n\n### 【童程童美 CEO/孙滢】\n\n• 当前,素质教育市场比较分散,但包括少儿编程教育在内的市场规模仍将继续扩大。人工智能浪潮越大,人们越可能认识到通过素质教育发展孩子其他能力的重要性,包括体育、艺术、编程等,因此素质教育将会继续蓬勃发展。 \n• 鉴于提升学科成绩的刚需长期持续存在,优质的教师资源将始终都是稀缺资源。有实力的大品牌和大公司会在素质教育发展的过程当中获得更大的优势。与此同时,市场里具有个性化特点的小公司也会蓬勃发展,最终呈现巨头和小而美并存的共荣局面。\n\n### 【点猫科技创始人兼 CEO/李天驰】", }, { "pageContent":"### 【点猫科技创始人兼 CEO/李天驰】\n\n• 点猫科技将以“为下一代提供更有价值的教育”为使命,持续聚焦工具和内容研发,培养青少年的计算思维以及用数字化的方法和手段解决实际问题的能力,帮助更多地区和学校开展人工智能编程教育服务,不断为科技教育事业添砖加瓦,并为振兴乡村教育、助力教育公平贡献自己的力量。\n\n### 【核桃编程创始人兼 CEO/曾鹏轩】\n\n• 未来教育的趋势是以实操为主的教育方式。少儿编程是学习的工具,一种能够实现主动学习的教育方式,也是一种未来教育的理念。核桃编程让孩子通过编程来学习,而不是学习编程,而实操是对编程最有效的学习方式,也是核桃编程的核心理念。我们希望通过核桃编程带来一种正向的学习理念,不只是为了学而学,而是为了用而学。我们要培养的孩子,是对科学充满好奇、拥有无限创造力,最重要的是具有独立思考与自主学习的能力。\n\n### 【斯坦星球 CEO/崔显耿】", } ... ]
然后对此进行评测,将该评测结果(v5.0)作为本文的基准:
基于 Markdown 语法切分
要基于 Markdown 语法进行切分,我们采用的是 langchainjs 提供的MarkdownTextSplitter。MarkdownTextSplitter是RecursiveCharacterTextSplitter的子类,也就是在RecursiveCharacterTextSplitter 默认分隔符序列的基础上添加了一些 Markdown 特有的分隔符['\n## ', '\n### ','\n#### ', '\n##### ','\n###### ', '```\n\n','\n\n***\n\n', '\n\n---\n\n','\n\n___\n\n' ],这样就意味着它会先尝试按 Markdown 标题 分割,然后是代码块、接着是分割线,最后是按 RecursiveCharacterTextSplitter 方法切分,这样就能够识别并考虑 Markdown 文本中的不同元素(如标题、段落、列表项、代码块等),从而进行更加合理的切分。
代码实现:
复制async function splitDocuments_v51(docs, config) { const textSplitter = new MarkdownTextSplitter({ chunkSize: 500, // 文本切分大小 chunkOverlap: 50, // 文本切分重叠大小 }); const documents = await textSplitter.splitDocuments(docs); return documents; }
使用该方法切分出来的文档块大小分布的比较分散,最小的文档块大小只有 10:
[192,340,402,378,387,400,495,381,225,273,275,314,281,435,485,245,350,302,387,411,378,383,445,451,32,463,398,143,359,359,484,359,39,490,271,437,420,308,28,441,443,436,465,25,407,494,487,334,14,432,148,343,182,437,145,72,410,400,388,172,29,255,492,423,55,484,498,28,458,168,252,451,32,477,225,419,392,37,118,482,485,192,19,250,332,424,150,19,27,481,242,487,491,175,300,36,462,35,10,461,456,48,27,10,494,235,14,458,32,206,380,387,27,22,472,473,134,339,10,407,102,412,407,27,13,486,478,210,14,467,129,412,30,31,350,170,206,29,492,71,263,378,253,488,88,440,487,353,38,279,432,170,34,133,487,485,361,392,468,357,243,31,474,203,28,484,203,127,32,460,37,31,12,495,477,101,14,142,30,10,498,211,327,404,36,393,183,453,448,277,393,264]
我们再看下用该方法对 《2024少儿编程教育行业发展趋势报告.md》文件进行切分的结果:
复制[ { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n\n\n多鲸教育研究院/ 2024 年 1 月", }, { "pageContent":"## 少儿编程教育行业图谱\n\n### To B / To G\n\n\n\n", }, { "pageContent":"### 【极客晨星创始人、CTO/张军彪】\n\n• 2023 年以 AIGC 和大模型为代表的 AI 技术迅猛发展,如何为中小学生提供更加优质的科学教育、全面提高科学素质,成为一个急迫的时代命题,编程作为链接 AI 时代和创新人才的“钥匙”越来越刚需,整个青少年编程市场也进入活跃期。编程教育的目标是要培养个性化、创新性人才。 \n• 编程教育最根本的是要立足课程体系,用科技加持的方法教好科技的课程,打造好的平台和工具,为孩子们提供更加优质的教育资源,激发好奇心、培养创造力,实现个性化、创新性人才的培养,在助力科学教育做加法中的作用越来越突出。", }, ... { "pageContent":"#### 各地积极响应国家政策号召,推动编程教育与人工智能的融合及普及国家战略政策利好,少儿编程教育重要性逐步提升\n\n国家政策支持\n\n从教育部到各地方教育主管部门陆续出台多项政策支持少儿编程教育普及推广\n\n- 2018 -2019 年\n\n• 河南省建议在中小学开设 Scratch、Python 等程序设计课程,培养编程思维,普及编程教育。 \n• 天津市政府招生办发布的《 2018 年天津科技特长生招生计划》中,多所中学将信息学奥赛、信息技术、人工智能等纳入了招生范畴。 \n• 重庆市教委发布《关于加强中小学编程教育的通知》,将编程列为重庆中小学必修课,要求小学三年级开始学编程。 \n• 北京市教育委员会将人工智能纳入北京中小学社会实践,明确了人工智能与教育融合发展在教育各学段主要任务。 \n• 山东省教育厅整合人工智能、编程教育、机器人教育等创客资源,着力打造创客教育课程体系。\n\n- 2020 -2021 年", }, { "pageContent":"- 2020 -2021 年\n\n• 新疆乌鲁木齐市教育局通知将在小学初中和高中年级里分别开展图形化编程和 Python 课程,并附对课程及课时安排的具体要求,要求对老师进行线上与线下结合的编程培训。 \n• 浙江省发布消息,八年级新增 Python 内容,五六年级按照教材规划开始接触大数据、人工智能、程序设计与算法。 \n• 北京市海淀区明确指出:将信息技术(包含编程)纳入初中学业水平测试,考试不通过不予毕业。 \n• 上海市教委提出推进人工智能、编程技术等课程进中小学课堂,支持高校人工智能相关专业建设,提升师生信息素养。 \n• 长沙市发改委等部门指出,中小学新增“人工智能教育”和“编程教育”。 \n• 广州市将“信息技术”列入初中学业水平考试录取参考科目之一,示范性普通高中投档考生的录取参考科目成绩均须达到 C 级及以上。\n\n- 2022 年", } ]
该切分方法的评测结果(v5.1)如下:
从评测结果来看,该方法对各项指标有提升但不是很明显,因此我们进一步分析下原因并优化。
合并过小的文档块
在使用 MarkdownTextSplitter 进行切分文档的时候,我们可以看到切分后的文档块大小分布是比较分散的,最小的文档块大小只有 10,由于 MarkdownTextSplitter 切分方法的特性,这些小的文档块可能就是一个标题,比如:["##### 业务模式", "##### 课程体系", "##### 产品优势"]。由于我们是根据向量距离检索相关文档的,由于更大的文本块,其所涵盖的信息量也增大,可能导致向量表示变得更加稀疏,往往我们检索出来的是这些更小的文档,但这些文档往往不包含或包含很少答案所需的关键信息,从而影响到上下文召回率。
因此我们需要将这些过小的文档块进行合并,由于这些过小的文档块是标题,所以简单的方法就是将过小的文档块直接合并到下一个文档块上,代码实现:
复制async functionsplitDocuments_v52(docs) { // 在 splitDocuments_v51 的结果上进行处理 const documents = awaitsplitDocuments_v51(docs); for (let i = 0; i < documents.length; i++) { const doc = documents[i]; // 长度小于100的文档 if (doc.pageContent.length < 100) { // 如果不是最后一个元素,则合并到下一个元素 if (i < documents.length - 1) { console.log('合并内容:', doc.pageContent, '到下一个'); documents[i + 1].pageContent = doc.pageContent + '\n' + documents[i + 1].pageContent; } // 删除当前元素 documents.splice(i, 1); i--; // 回退索引以适应数组缩短的情况 } } return documents; }
通过这样的优化,我们看下评测结果(v5.2)如下:
此时我们可以看到,上下文召回率较 v5.1 有了比较明显的提升。
给文档块补充标题
之所有要把不同的文件转换成 Markdown 文件,其中的好处是 Markdown 文件能够以纯文本形式保留文档的基本结构(标题、列表、代码块、表格),其中的标题提供了文档的关键词和上下文信息,能来用来帮助快速理解文档内容,并在检索时更准确地定位到与用户提问相关的信息,这可以提高上下文相关性,因此给每个文档块补充标题是非常有必要的。
MarkdownTextSplitter切分后的结果:
复制[ { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n\n\n多鲸教育研究院/ 2024 年 1 月", }, { "pageContent":"## 少儿编程教育行业图谱\n\n### To B / To G\n\n\n\n", }, { "pageContent":"### 【极客晨星创始人、CTO/张军彪】\n\n• 2023 年以 AIGC 和大模型为代表的 AI 技术迅猛发展,如何为中小学生提供更加优质的科学教育、全面提高科学素质,成为一个急迫的时代命题,编程作为链接 AI 时代和创新人才的“钥匙”越来越刚需,整个青少年编程市场也进入活跃期。编程教育的目标是要培养个性化、创新性人才。 \n• 编程教育最根本的是要立足课程体系,用科技加持的方法教好科技的课程,打造好的平台和工具,为孩子们提供更加优质的教育资源,激发好奇心、培养创造力,实现个性化、创新性人才的培养,在助力科学教育做加法中的作用越来越突出。", }, ... { "pageContent":"#### 各地积极响应国家政策号召,推动编程教育与人工智能的融合及普及国家战略政策利好,少儿编程教育重要性逐步提升\n\n国家政策支持\n\n从教育部到各地方教育主管部门陆续出台多项政策支持少儿编程教育普及推广\n\n- 2018 -2019 年\n\n• 河南省建议在中小学开设 Scratch、Python 等程序设计课程,培养编程思维,普及编程教育。 \n• 天津市政府招生办发布的《 2018 年天津科技特长生招生计划》中,多所中学将信息学奥赛、信息技术、人工智能等纳入了招生范畴。 \n• 重庆市教委发布《关于加强中小学编程教育的通知》,将编程列为重庆中小学必修课,要求小学三年级开始学编程。 \n• 北京市教育委员会将人工智能纳入北京中小学社会实践,明确了人工智能与教育融合发展在教育各学段主要任务。 \n• 山东省教育厅整合人工智能、编程教育、机器人教育等创客资源,着力打造创客教育课程体系。\n\n- 2020 -2021 年", }, { "pageContent":"- 2020 -2021 年\n\n• 新疆乌鲁木齐市教育局通知将在小学初中和高中年级里分别开展图形化编程和 Python 课程,并附对课程及课时安排的具体要求,要求对老师进行线上与线下结合的编程培训。 \n• 浙江省发布消息,八年级新增 Python 内容,五六年级按照教材规划开始接触大数据、人工智能、程序设计与算法。 \n• 北京市海淀区明确指出:将信息技术(包含编程)纳入初中学业水平测试,考试不通过不予毕业。 \n• 上海市教委提出推进人工智能、编程技术等课程进中小学课堂,支持高校人工智能相关专业建设,提升师生信息素养。 \n• 长沙市发改委等部门指出,中小学新增“人工智能教育”和“编程教育”。 \n• 广州市将“信息技术”列入初中学业水平考试录取参考科目之一,示范性普通高中投档考生的录取参考科目成绩均须达到 C 级及以上。\n\n- 2022 年", } ]
我们通过看 MarkdownTextSplitter切分后的结果,可以看到文档块所在的每一层级的标题是有缺失的,理想状态下,该文档块需要有它所在的一级标题、二级标题、三级标题等等,所以在这里我们将对每个文档块的标题进行补充,实现代码如下:
复制async functionsplitDocuments_v53(docs, config) { // 在 splitDocuments_v52 的结果上进行处理 const documents = awaitsplitDocuments_v52(docs, config); // 获取每个文档块的标题并添加到元数据中 for (let i = 0; i < documents.length; i++) { const doc = documents[i]; const lines = doc.pageContent.split('\n'); const headers = [ { key: 'header5', value: '##### ', }, { key: 'header4', value: '#### ', }, { key: 'header3', value: '### ', }, { key: 'header2', value: '## ', }, { key: 'header1', value: '# ', }, ]; headers.forEach((header) => { const { key, value } = header; doc.metadata[key] = []; // 提取每一行的标题 for (const line of lines) { if (line.startsWith(value)) { doc.metadata[key].push( line.replace(newRegExp(`/^${value}/`), '').trim() ); } } // 如果当前文档没有对应标题,则取前一个文档的对应标题的第一个,并加入到当前文档中 if (i > 0) { if (doc.metadata[key].length === 0) { const preHeader = documents[i - 1].metadata[key][0]; if (preHeader) { doc.pageContent = preHeader + '\n\n' + doc.pageContent; doc.metadata[key] = [preHeader]; } } } }); } return documents; }
这样处理后,我们可以看到每个文档块都补充了每一级的标题:
复制[ { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n\n\n多鲸教育研究院/ 2024 年 1 月", }, { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n## 少儿编程教育行业图谱\n\n### To B / To G\n\n\n\n", }, { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n## 专家观点\n\n### 【极客晨星创始人、CTO/张军彪】\n\n• 2023 年以 AIGC 和大模型为代表的 AI 技术迅猛发展,如何为中小学生提供更加优质的科学教育、全面提高科学素质,成为一个急迫的时代命题,编程作为链接 AI 时代和创新人才的“钥匙”越来越刚需,整个青少年编程市场也进入活跃期。编程教育的目标是要培养个性化、创新性人才。 \n• 编程教育最根本的是要立足课程体系,用科技加持的方法教好科技的课程,打造好的平台和工具,为孩子们提供更加优质的教育资源,激发好奇心、培养创造力,实现个性化、创新性人才的培养,在助力科学教育做加法中的作用越来越突出。", }, ... { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n## 少儿编程教育行业 十大趋势\n\n### 01 国家战略政策利好,少儿编程教育重要性逐步提升\n\n#### 各地积极响应国家政策号召,推动编程教育与人工智能的融合及普及国家战略政策利好,少儿编程教育重要性逐步提升\n\n国家政策支持\n\n从教育部到各地方教育主管部门陆续出台多项政策支持少儿编程教育普及推广\n\n- 2018 -2019 年\n\n• 河南省建议在中小学开设 Scratch、Python 等程序设计课程,培养编程思维,普及编程教育。 \n• 天津市政府招生办发布的《 2018 年天津科技特长生招生计划》中,多所中学将信息学奥赛、信息技术、人工智能等纳入了招生范畴。 \n• 重庆市教委发布《关于加强中小学编程教育的通知》,将编程列为重庆中小学必修课,要求小学三年级开始学编程。 \n• 北京市教育委员会将人工智能纳入北京中小学社会实践,明确了人工智能与教育融合发展在教育各学段主要任务。 \n• 山东省教育厅整合人工智能、编程教育、机器人教育等创客资源,着力打造创客教育课程体系。\n\n- 2020 -2021 年", }, { "pageContent":"# 少儿编程教育行业发展趋势报告\n\n## 少儿编程教育行业 十大趋势\n\n### 01 国家战略政策利好,少儿编程教育重要性逐步提升\n\n#### 各地积极响应国家政策号召,推动编程教育与人工智能的融合及普及国家战略政策利好,少儿编程教育重要性逐步提升\n\n- 2020 -2021 年\n\n• 新疆乌鲁木齐市教育局通知将在小学初中和高中年级里分别开展图形化编程和 Python 课程,并附对课程及课时安排的具体要求,要求对老师进行线上与线下结合的编程培训。 \n• 浙江省发布消息,八年级新增 Python 内容,五六年级按照教材规划开始接触大数据、人工智能、程序设计与算法。 \n• 北京市海淀区明确指出:将信息技术(包含编程)纳入初中学业水平测试,考试不通过不予毕业。 \n• 上海市教委提出推进人工智能、编程技术等课程进中小学课堂,支持高校人工智能相关专业建设,提升师生信息素养。 \n• 长沙市发改委等部门指出,中小学新增“人工智能教育”和“编程教育”。 \n• 广州市将“信息技术”列入初中学业水平考试录取参考科目之一,示范性普通高中投档考生的录取参考科目成绩均须达到 C 级及以上。\n\n- 2022 年", } ]
通过这样的优化,我们看下评测结果(v5.3)如下:
此时我们可以看到,上下文相关性较 v5.2 有了比较明显的提升。
结语
通过对比基于通用字符递归切分方法与基于 Markdown 语法的文档切分方法,我们可以看到基于 Markdown 语法的文档切分方法对 RAG 系统的各项指标是有一定的提升作用,然后我们通过合并过小的文档块提升了上下文召回率,通过给文档块补充标题提升了上下文相关性。
引用链接
[1] 本文完整代码地址: https://github.com/laixiangran/ai-learn/blob/main/src/app/rag/05_document_split_optimize/route.ts