当前位置: 首页 > news >正文

我的 AI 知识库开源!RAG命中率从60%到78% 经验分享

我花了1个月搭了一套[自建 RAG 知识库],向量库跑起来一切正常。

然后我用"Redis Cluster 怎么扩容"搜了一下。

返回的结果前三篇分别是:《Redis 为什么快》、《Go 语言的并发模型》、《我们团队的代码规范》。每一篇回答了我的问题。

问题不在 embedding 模型,不在向量数据库。问题出在一个大多数教程一笔带过、很多工程师压根没认真对待的环节——分块策略。

同样的向量数据库、同样的 embedding 模型,分块策略不同,检索命中率能差 20% 以上。这不是夸大,是我实打实测出来的。

三种分块策略核心差异速览

先给答案:三种策略怎么选

不想看完整分析的话,这张表够用了:

你的内容类型推荐策略预期命中率一句话理由
技术教程、专栏、架构文档策略 A:结构感知分块~78%保留标题层级,chunk 自带上下文
设计模式、最佳实践、精炼总结策略 B:模板/段落分块~72%轻量快速,适合内容本身已结构化
FAQ、社区问答、踩坑笔记策略 C:Q&A 对分块规划中保证问题和答案不被拆散

如果你用的还是固定 500 字符硬切,命中率大概率在 60% 左右徘徊——这跟没做 RAG 区别不大。

上面这张表看起来简洁,但背后有一个很多人不知道的事实:策略 A 和策略 B 本质上是在「内容入库时」就已经决定了检索质量的上下限。chunk 边界一旦写入索引,之后所有下游操作——换 embedding 模型、加 reranker、调检索参数——都只能在这组已切好的候选上重新排序。如果你的 chunk 本身就是残缺的(一句话被腰斩、代码块被截断、Q&A 被拆散),后面做再多优化也是白搭。

换句话说:分块定义了「检索对象是什么」,embedding 只决定「怎么找到它」。想通这一层,你就知道为什么分块策略值得花时间细调。

如果你对自己的内容类型拿不准,直接看后面的详细对比。如果内容结构比较复杂——比如既有一堆 Markdown 教程又有一批 FAQ 问答——可以考虑进阶方案:Small-to-Big(父子文档)。用小块做精准召回(chunk_size=300-500),召回后自动扩展到父级大块喂给 LLM。社区 benchmark 显示这套方案的 recall 能达到 0.86,是目前生产环境实测的最优策略之一。

为什么分块策略被低估了

大部分 RAG 教程的流程是:装向量库 → 选 embedding 模型 → 调检索参数 → 上线。分块这一步,教程里通常就一行chunk_size=500,随手一设就过了。

但实际上,分块决定了 embedding 向量的信息密度。

一个 chunk 太大(比如 2000 字符),包含多个不相关的技术概念,embedding 向量就会变成一个"平均脸"——什么都像,什么都检索不准。一个 chunk 太小(比如 200 字符),信息被切碎,检索时会召回一堆零散片段,LLM 得自己拼图。

说白了,分块策略在 RAG 系统里的地位相当于数据库的索引设计。索引没建对,换什么存储引擎都是白搭。

换一个角度想:你写技术文章的时候会先搭标题结构,读者阅读时会根据标题找自己关心的章节。那为什么给向量库灌内容的时候,要把标题结构扔掉、当成一坨平文本切?

这就是结构感知分块的核心思路。

还有一个反直觉的数据点:Adobe 在 2025 年做了一个大规模实验,在他们内部数千工程师使用的技术支持系统上对比了多种分块策略。结果让很多人意外——400-token 的固定分块(加 20% overlap)在某些场景下反而击败了语义分块和分层分块,准确率提升了 20%。这说明一个道理:分块策略没有银弹,内容类型决定一切。如果文档本身格式规整、段落清晰,简单的段落切分可能比复杂的语义分块更有效。

策略 A:结构感知分块(我的主力策略)

原理:按 Markdown 的标题层级(H2 → H3 → H4)逐级切分,保证每个 chunk 是一个完整的"小节",而不是一段被腰斩的文字。

关键点有两条:

  1. 保留父级标题作为上下文前缀。比如一篇讲 Redis Cluster 的文章,H3 小节"3.4.1 Redis Cluster 是什么"下面的内容在切分时,chunk 的 metadata 会带上Redis Cluster 集群 > 3.4.1 Redis Cluster 是什么。检索时不仅能匹配内容本身,还能利用标题路径做语义锚定。

  2. max_size 不是一刀切,而是"软上限"。我的设置是 800 字符——大部分 H3 小节本身就在 500-800 字之间,少数超长的会自动在 H4 级别进一步拆分,但绝对不会在一段代码或一个完整概念中间断开。

来看一下实现代码:

from langchain.text_splitter import MarkdownHeaderTextSplitter headers_to_split_on = [ ("##", "h2"), ("###", "h3"), ("####", "h4"), ] splitter = MarkdownHeaderTextSplitter( headers_to_split_on=headers_to_split_on, strip_headers=False, # 保留标题文本在 chunk 中 ) # 实际使用:读取 markdown 文件后切分 with open("redis-cluster-deep-dive.md", "r") as f: markdown_content = f.read() chunks = splitter.split_text(markdown_content) for chunk in chunks: # metadata 中保留了完整的标题路径 # 例如:{'h2': 'Redis Cluster 集群', 'h3': '3.4.1 Redis Cluster 是什么'} print(f"标题路径: {chunk.metadata}") print(f"内容长度: {len(chunk.page_content)} 字符") print(f"内容预览: {chunk.page_content[:100]}...") print("---")

但这只是第一步。MarkdownHeaderTextSplitter 按标题切完后,如果某个 H3 下的内容有 3000 多字怎么办?单个 chunk 直接塞进向量库,embedding 质量会严重下降。所以需要加一层多级回退:

from langchain.text_splitter import RecursiveCharacterTextSplitter # 多级回退拆分器:优先按 /n/n(段落)切,不行再按 /n(句子),最后按字符 fallback_splitter = RecursiveCharacterTextSplitter( chunk_size=800, chunk_overlap=80, separators=["/n/n", "/n", "。", ".", " ", ""], length_function=len, ) # 对每个 header split 的结果再做尺寸检查 final_chunks = [] for chunk in header_chunks: if len(chunk.page_content) <= 800: # 内容在阈值内,直接保留 final_chunks.append(chunk) else: # 超过阈值,用回退拆分器继续切,但保留原始 metadata sub_chunks = fallback_splitter.split_text(chunk.page_content) for i, sub in enumerate(sub_chunks): sub_chunk = type(chunk)( page_content=sub, metadata={chunk.metadata, "sub_chunk_index": i} ) final_chunks.append(sub_chunk) print(f"最终生成 {len(final_chunks)} 个 chunk")

这套策略在我的 240 篇文章上实测下来,命中率稳定在 78% 左右。而且有个额外的好处:每个 chunk 自带标题路径,LLM 在生成回答时能明确知道信息来自哪篇文章的哪个章节,幻觉明显减少。

适用内容:技术教程、专栏文章、架构文档——任何有清晰标题层级的长文。

不适用:自然语言为主的随笔、没有标题结构的纯文本流水账。这种情况建议直接用策略 B。

策略 B:模板/段落分块

原理:优先按##标题切,如果单个##下内容超过阈值,则按段落边界切分。本质上是一个"先尊重结构,再尊重语义边界"的思路。

策略 B 比策略 A 简单,但比固定字符硬切聪明得多。它的 max_size 我设成了 600 字符——比策略 A 的 800 更小,因为适用内容本身信息密度就高,不需要那么大的窗口。

实现代码:

from langchain.text_splitter import RecursiveCharacterTextSplitter # 针对已精炼的结构化内容 splitter = RecursiveCharacterTextSplitter( chunk_size=600, chunk_overlap=60, separators=["## ", "/n/n", "/n", "。", " "], length_function=len, ) chunks = splitter.create_documents( texts=[article_content], metadatas=[{"source": "best-practices-microservices-2025.md"}] )

对比策略 A 和策略 B 的差异,最核心的一点是:策略 B 不会主动构建标题层级路径。它把##当作一个分隔标记,但如果内容本身没有##标题(或者标题层级很浅),它会直接退化到按段落切分。

实测命中率在 72% 左右,比策略 A 低约 6 个百分点。这 6 个点的差距主要来自"缺乏标题上下文"——当用户搜索一个特定的技术概念时,策略 B 的 chunk 里可能没有足够的层次信息帮助 embedding 模型精准定位。

但策略 B 的优势在于快且省。不需要解析 Markdown 标题树,不需要构建 metadata 路径,对内容格式的要求也低得多。如果你的知识库内容本身就是短小精悍的结构化总结(比如设计模式清单、API 最佳实践卡片),策略 B 完全够用。

适用内容:日常摘要、设计模式文档、最佳实践列表——内容本身已经很精炼,不需要额外的结构解析。

策略 C:Q&A 对分块(规划中)

这部分是我踩过的一个坑,还在填。

我的知识库里有一部分内容来自社区问答——读者问"Redis 哨兵模式和 Cluster 模式怎么选",我写了一篇详细的回复。这篇回复里既有问题又有答案,如果按标题切分或段落切分,问题和答案大概率会被拆到不同的 chunk 里。

后果就是:用户搜"哨兵模式和 Cluster 怎么选"时,向量库可能只召回答案的那一半 chunk——语义上匹配,但缺少了问题上下文,LLM 不知道这段话在回答什么。

策略 C 的思路很简单:一个问答对 = 一个 chunk。不按字数切,不按标题切,就按 Q&A 的边界切。

# 策略 C 示意代码(规划中,非生产级) import re def split_qa_pairs(text: str) -> list[dict]: """按 Q&A 模式切分文本""" # 匹配 Q: 或 问: 开头的段落 pattern = r'(?:Q[::]|问[::]|#### Q/d+)(.*?)(?=Q[::]|问[::]|#### Q/d+|$)' matches = re.findall(pattern, text, re.DOTALL) qa_pairs = [] for match in matches: parts = re.split(r'(?:A[::]|答[::])', match, maxsplit=1) if len(parts) == 2: qa_pairs.append({ "question": parts[0].strip(), "answer": parts[1].strip(), }) return qa_pairs

这个策略在 v3.0 里才规划落地,目前只覆盖了我知识库中约 5% 的内容(那批 FAQ 类文章)。但效果已经能看出来——Q&A 类内容的检索命中率提升非常明显,因为问题本身就是一个天然的检索 query,chunk 里同时包含问题和答案,embedding 向量能更好地捕捉这种"提问-回答"语义模式。

适用内容:FAQ 页面、社区问答、踩坑笔记——所有以"问题-回答"形式组织的内容。

我的迭代过程:三个版本的教训

说实话,我在分块策略上踩的坑,比在 embedding 模型选型上踩的还多。

v1.0:固定 500 字符硬切

这是绝大多数教程推荐的"最佳实践"——设个chunk_size=500,overlap 给个 50,跑起来就行。

结果惨不忍睹。一段讲 Go 并发模型的中文段落,500 字符可能刚好卡在"goroutine 的调度基于 GMP 模型,G 代表"——后面就没了。G 代表什么?读者想知道,向量库也不知道。

更致命的是代码块被拦腰截断。一个完整的设计模式示例,前半段是问题描述,后半段是实现代码,被切到两个 chunk 里之后,两个 chunk 都变成了语义残片。

命中率 60% 出头——跟随机抽差不多。

v2.0:按 H2 标题切

意识到问题之后,我改成了按 H2 标题切分——每个大章节一个 chunk。

效果好了一些,但马上遇到新问题:有些 H2 下面的内容多达 3000+ 字。比如我有一篇讲 Redis 持久化的文章,H2"RDB 快照原理"下面光代码注释就有 1500 字。这么大一个 chunk 塞进向量库,embedding 向量的语义焦点完全散了。

v3.0:结构感知 + 多级回退

v3.0 就是现在这套方案——H2 → H3 → H4 → 段落 → 句子,逐级回退。每个 chunk 自带标题上下文,尺寸控制在 800 字符左右。

这套方案上线后,命中率稳定在 75%+,少数优化过的专题能达到 80% 以上。

结构感知分块的多级回退流程

四个核心原则(比参数更重要)

扒完三种策略,我得出的结论是:参数(chunk_size、overlap)只是表象,核心原则决定了你的分块策略能不能用。

原则一:保持语义完整性

宁可 chunk 稍大,不要打断一个完整的技术概念。切在段落中间、切在代码块中间、切在"首先……其次……"的并列结构中间——这些都是致命伤。一段被腰斩的技术解释,embedding 质量远不如稍长但完整的段落。

原则二:保留上下文锚点

每个 chunk 要知道自己属于哪篇文章、哪个章节。这不是为了好看——当 LLM 同时拿到 5 个相关 chunk 时需要回答"这 5 段来自 3 篇不同的文章,它们之间是什么关系"时,上下文锚点是 LLM 拼接信息的唯一线索。

原则三:内容类型决定分块策略

不要一刀切。技术教程和 FAQ 问答天然需要不同的分块方式。我现在的做法是:在内容入库时就打上类型标签,分块器根据标签自动选择策略。

原则四:max_size 不是越短越好,也不是越长越好

太短丢失上下文(500 字以下的 chunk,embedding 质量下降明显),太长检索精度下降(超过 1200 字的 chunk,容易出现"什么都沾边但什么都不准"的问题)。800 字符是我反复实验后找到的甜点——当然,这个数字对英文内容可能不同,英文的分块甜点通常在 256-512 tokens 之间。

综合评分

评估维度策略 A:结构感知策略 B:模板段落策略 C:Q&A 对
检索命中率★★★★★ (78%)★★★★ (72%)规划中
实现复杂度★★★ (需解析标题树)★ (仅需配置拆分器)★★
内容格式要求必须有 Markdown 标题有段落结构即可必须有 Q&A 格式
长文处理能力★★★★★ (多级回退)★★★ (超大段落会退化)★★ (不适用长文)
LLM 回答质量★★★★★ (自带上下文)★★★ (上下文依赖检索)★★★★ (Q&A 语义完整)
适用范围80% 的常见内容15%(精炼内容)5%(FAQ/问答)

我的实际数据:240 篇文章产生了 5877 个 chunk,平均每篇约 24 个 chunk。策略 A 覆盖了其中 80% 的内容,策略 B 覆盖 15%,策略 C 只占了 5%(还在扩展中)。

有两点值得单独拉出来说——

第一,语义分块为什么不在我的策略列表里? 语义分块(按相邻句子 embedding 相似度的骤降点切分)听起来很高级,但社区真实 benchmark 给出了一个扎心的数据:它在某些测试集上以 50 倍的计算成本,只换来了和递归切分几乎持平的 recall(0.72 vs 0.74)。花 50 倍成本换不到 2 个点的提升,这性价比不适合大多数生产环境。除非你的文档是纯自然语言长文(法律合同、科研论文),语义分块才有明显优势——因为这类文档没有标题结构可依赖。

第二,overlap 可能被高估了。 几乎所有教程都在推荐设 10-20% 的 overlap,但我最近读到了一篇 2025 年的系统分析论文(arXiv:2601.14123),结论是:overlap 在 Natural Questions 这类数据集上没有显著收益,反而让索引体积膨胀了约 25%。我的经验是 10% 的 overlap 有个保底作用——防止关键句恰好落在边界上被截断——但不要无脑往上加。overlap 不是越大越好。

常见问题

Q:chunk_overlap 设多少合适?

先说一个反常识的结论:有学术分析(arXiv 2025)表明 10-20% 的 overlap 在某些 benchmark 上没有显著收益,反而让索引体积膨胀了 25%。我的折中做法是 chunk_size 的 10%——chunk_size=800 时 overlap 设 80。这更多是一个"保底"策略,防止关键句恰好落在边界上被截断,但不用无脑往上加。如果你的文档本身就是按标题/段落组织的,overlap 甚至可以降到 5%。

Q:什么叫 Context Cliff?它和分块策略有什么关系?

Context Cliff 是一个 2025 年被反复验证的现象:LLM 在上下文超过约 2500-3000 tokens 后,对中间位置信息的提取能力会显著下降。这和分块策略直接相关——如果你为了"多给上下文"把 chunk 设得过大(>1500 tokens),或者一次召回太多 chunk 全塞给 LLM,反而会触发这个退化效应。正确的思路是:小块精准召回 + 只把最相关的 3-5 个 chunk 喂给 LLM。这也是为什么 Small-to-Big(父子文档)方案值得关注——它在检索阶段用小 chunk 保证精度,生成阶段才扩展到大 chunk,避开了 Context Cliff 的风险。

Q:中文和英文的分块策略有区别吗?

有。中文字符本身的信息密度比英文 token 低——一个中文字符通常对应 1.5-2 个 token,一个英文单词也是 1-2 个 token。所以中文的 chunk_size 可以设大一些(我用的 800 字符),英文通常在 256-512 tokens 之间。另外,中文分词对句子边界识别更敏感,分块器中的separators建议加入中文标点()。

Q:我的文章很短,每篇只有几百字,还需要分块吗?

如果单篇文章本身不超过 chunk_size 上限,可以整篇作为一个 chunk 入库,不需要额外拆分。但建议在 metadata 中保留文章标题,方便后续检索时做来源标注。

Q:三种策略能混用吗?

能,而且推荐混用。我的做法是在内容入库时,先判断文章类型标签,然后路由到对应的分块策略。比如一篇 Markdown 技术教程走策略 A,一篇设计模式清单走策略 B,一篇 FAQ 走策略 C。不要试图用一套参数覆盖所有内容——这是最常见的错误。

Q:怎么客观评估分块策略的效果?

我用的方法是用一组标准测试问题(覆盖不同内容类型),对每种策略分别评估 Top-3 检索命中率。注意要用真实用户会问的问题,而不是你自己写的问题——后者往往和你的 chunk 语义分布高度匹配,测出来的命中率不准。

💬 你现在用的什么分块策略?

  • A. 固定 size 硬切,设个 chunk_size 就完了
  • B. 按标题/章节切分,保留结构信息
  • C. 上了 LangChain/LlamaIndex 的默认配置,没细调
  • D. 还没在生产环境真正用过 RAG

评论区说说,码哥猜选 A 和 C 的最多 😂

我的建议

分块策略是 RAG 系统里最容易被低估、同时也是性价比最高的优化点。换 embedding 模型可能需要重新入库、重新调参,改向量数据库可能需要数据迁移,但优化分块策略只需要改几行 Python 代码——效果立竿见影。

如果你现在用的是固定字符硬切,建议优先迁移到策略 A(结构感知分块)。如果你的内容本身没有 Markdown 标题结构,策略 B(段落切分)是更好的起点。如果你是做 FAQ/客服知识库的,策略 C 的 Q&A 对思路值得一试。

说到底,分块策略的本质是让每一段文字在脱离原文后仍然能被理解。技术文章的标题结构是为人类阅读设计的,而分块策略的职责就是把这套结构翻译成向量库能理解的语言。

这篇文章里提到的方法和代码,我自己在生产环境的知识库上跑了几个月。实际效果比我预想的要好——在不需要换 embedding 模型、不需要加 reranker 的情况下,只靠优化分块策略就把命中率从 60% 提到了 78%。如果你的知识库还在为检索精度头疼,不妨先检查一下分块策略——大概率问题就出在这。

这里给大家精心整理了一份全面的AI大模型学习资源包括:AI大模型全套学习路线图(从入门到实战)、精品AI大模型学习书籍手册、视频教程、实战学习、面试题等,资料免费分享

👇👇扫码免费领取全部内容👇👇

1. 成长路线图&学习规划

要学习一门新的技术,作为新手一定要先学习成长路线图方向不对,努力白费

这里,我们为新手和想要进一步提升的专业人士准备了一份详细的学习成长路线图和规划。可以说是最科学最系统的学习成长路线。

2. 大模型经典PDF书籍

书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础(书籍含电子版PDF)

3. 大模型视频教程

对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识

4. 2026行业报告

行业分析主要包括对不同行业的现状、趋势、问题、机会等进行系统地调研和评估,以了解哪些行业更适合引入大模型的技术和应用,以及在哪些方面可以发挥大模型的优势。

5. 大模型项目实战

学以致用,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。

6. 大模型面试题

面试不仅是技术的较量,更需要充分的准备。

在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。

7. 资料领取:全套内容免费抱走,学 AI 不用再找第二份

不管你是 0 基础想入门 AI 大模型,还是有基础想冲刺大厂、了解行业趋势,这份资料都能满足你!
现在只需按照提示操作,就能免费领取:

👇👇扫码免费领取全部内容👇👇

http://www.cnnetsun.cn/news/3630084.html

相关文章:

  • 从NCM到MP3:ncmdumpGUI帮你重获音乐所有权
  • 61.嵌入式OTA远程升级:原理、流程与实战代码
  • 基于matlab交通限速标志识别系统【源码51期】
  • C语言--排序算法
  • AMC1311精密隔离放大器评估板深度解析:从电路设计到PCB布局实战
  • FastAPI分层架构实战:高效API开发指南
  • 灵境1911 短剧工业化系统:AI短剧量产工作流全解析
  • HarmonyOS开发实战:小分享-深浅色主题切换——基于 resources/dark 适配
  • Switch游戏安装终极指南:3分钟学会所有格式兼容方法
  • 3步解锁网易云音乐限制:ncmdump让NCM格式音乐重获播放自由
  • AI 芯片简报 07.21-07.24:NVIDIA Vera 亮剑、AMD 2nm GPU、Google 叛逃 CoWoS
  • 如何快速掌握CTF流量分析:5分钟上手CTF-NetA终极指南
  • 宿舍投影仪测评:哪款宿舍投影仪值得买?2026宿舍投影仪推荐
  • 魔兽争霸3终极助手:WarcraftHelper完整配置与性能优化指南
  • AI论文写作工具:提升学术效率的智能解决方案
  • 单细胞测序AI注释:大语言模型革新生物医学研究
  • 深入解析ADS892xB系列ADC:转换器模块、接口协议与高速数据采集实战
  • 水木清华联手滑铁卢大学:让AI编程助手“看懂“工具返回值
  • 计算机基础·计算机组成原理
  • TLV320ADC3101低功耗音频ADC:集成miniDSP的硬件设计与配置实战
  • AI应用开发四层技术栈:MCP协议与模块化实践
  • Apache Doris 实战教程:手把手实现 ClickHouse 表结构迁移与数据校验
  • Linear Loops功能详解:简化循环工程操作与自动化工作流
  • Claude Code v2.1.216性能优化:解决长会话卡顿与Agent行为异常
  • QKeyMapper:Windows游戏手柄键盘鼠标全能映射的终极解决方案
  • 大模型时代NLP技术演进与实战指南
  • 程序员转型大模型:核心技能与实战路线图
  • Centos7 编译安装Cmake+ffmpeg
  • windows系统下搭建Python开发环境
  • REFramework终极指南:如何为RE引擎游戏安装模组和脚本平台