RAG 中你是怎么做分块的?块大小怎么选的?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 分块策略认知:面试官不只是想知道你用了哪种分块方式,更想知道你理解不同策略的适用场景和 trade-off——为什么选这个策略而不是那个?
  2. 块大小的调优经验:块大小不是拍脑袋决定的,面试官想知道你有没有实际调参的经验,知不知道块太大或太小分别会带来什么问题。
  3. 工程实践意识:有没有遇到过因为分块不合理导致检索不准的问题?知道怎么优化吗?这块能直接区分 "做过 RAG" 和 "真正调过 RAG" 的候选人。

核心答案

分块(Chunking)是 RAG 系统的地基——地基没打好,后面 Embedding 模型再好、LLM 再强,效果都会打折。

分块策略上,我通常根据文档类型选择不同方案:

文档类型 推荐策略 原因
结构化文档(PDF、Word) 按段落/递归分块 保留语义完整性
代码文件 按函数/类分块 保持逻辑单元完整
FAQ、短文本 按行/整条分块 每条本身就是一个完整语义
HTML/Markdown 按标题层级分块 利用文档结构信息

块大小选择上,我的经验值是:

  • 通用场景:200~500 tokens,重叠 50~100 tokens
  • 精确问答场景:偏小,200~300 tokens
  • 需要上下文理解的场景:偏大,500~800 tokens

但具体数值一定要基于自己的数据集做实验验证,没有万能的银弹。

深度解析

一、为什么分块这么重要?

RAG 的核心链路是 "检索 → 生成",分块直接影响的是检索环节的质量。想象一下:

  • 块太大:一个 Chunk 里塞了太多内容,检索时可能把不相关的内容也带上了,导致 LLM 生成时被无关信息干扰,甚至产生幻觉。而且块太大,Embedding 向量是整段的 "平均表示",语义反而不够精确
  • 块太小:语义被截断,比如一个关键信息的前半段在 Chunk A、后半段在 Chunk B,检索时可能只捞到一半,LLM 拿到残缺的上下文,回答质量可想而知

上图直观展示了分块大小的三种情况:

  • 块太小:信息被切碎,上下文残缺,检索到的片段无法提供完整语义
  • 块适中:语义完整、检索精度高,是我们要追求的 "甜点区"
  • 块太大:一个 Chunk 里塞了太多无关内容,检索时语义被稀释,相当于大海捞针

二、主流分块策略详解

1. 固定大小分块(Fixed-size Chunking)

最简单粗暴的方式——按固定字符数或 Token 数切分,加上一定的重叠(Overlap)。

优点:实现简单、可预测。缺点:完全不考虑语义边界,可能把一句话从中间劈开。

2. 递归字符分块(Recursive Character Text Splitting)

这是实际项目中最常用的策略。LangChainLangChain4j 都有对应的实现。

原理:按照一组分隔符优先级依次尝试切分,先按段落切,段落太大就按句子切,句子太大就按字符切。这样能在保证块大小的同时,尽量保持语义完整。

LangChain4j 中的使用方式:

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.model.Tokenizer;

// 创建递归分块器
DocumentSplitter splitter = DocumentSplitters.recursive(
    300,    // maxSegmentSize:每个块的最大大小(单位取决于是否传入 tokenizer)
    50,     // maxOverlapSize:块之间的重叠大小
    tokenizer  // 可选,传入则按 token 计数,不传则按字符计数
);

// 使用分块器
List<TextSegment> segments = splitter.split(document);

这里的关键参数是 maxSegmentSizemaxOverlapSize

  • maxSegmentSize 控制每个块的上限
  • maxOverlapSize 控制相邻块之间的重叠,保证跨块的语义不会丢失

3. 按段落/句子/行分块

LangChain4j 提供了多种结构感知的分块器:

// 按段落分块 —— 适合结构化文档
DocumentSplitter paragraphSplitter = DocumentSplitters.documentByParagraph(
    500,       // maxSegmentSize
    50,        // maxOverlapSize
    tokenizer  // 可选
);

// 按句子分块 —— 适合细粒度场景
DocumentSplitter sentenceSplitter = DocumentSplitters.documentBySentence(
    400, 40, tokenizer
);

// 按行分块 —— 适合 CSV、日志等
DocumentSplitter lineSplitter = DocumentSplitters.documentByLine(
    300, 30, tokenizer
);

这些分块器会尽量把完整的段落/句子塞进同一个块里,只有当单个段落超过 maxSegmentSize 时才会进一步切分。

4. Spring AI 的分块方式

Spring AI 走的是 ETL 管线的设计思路,分块是 Transform 阶段:

import org.springframework.ai.transformer.splitter.TokenTextSplitter;

// 使用默认配置
TokenTextSplitter splitter = new TokenTextSplitter();

// 自定义配置
TokenTextSplitter customSplitter = new TokenTextSplitter(
    800,    // defaultChunkSize:默认块大小
    200,    // minChunkSizeChars:最小块大小
    50,     // minChunkLengthToEmbed:最小可嵌入长度
    1000,   // maxNumChunks:最大块数量
    true    // overlap:是否启用重叠
);

// 在 ETL 管线中使用
List<Document> documents = textReader.get();
List<Document> splitDocuments = customSplitter.apply(documents);

说实话,Spring AI 目前的分块能力相比 LangChain4j 还偏弱一些——它缺少递归字符分块器(RecursiveCharacterTextSplitter),社区已经有 feature request 了。如果你用 Spring AI 做生产级 RAG,可能需要自己实现一些定制化的分块逻辑。

三、块大小到底怎么选?

这是面试官最关心的部分。我总结了一套 "三步法":

第一步:根据场景定初始值

场景 初始块大小 重叠 理由
FAQ 问答 100~200 tokens 0~20 每条 FAQ 本身就是完整语义
文档问答 300~500 tokens 50~100 需要足够的上下文
长文档总结 500~1000 tokens 100~200 保留更多上下文
代码检索 按函数/类 保持逻辑单元完整

第二步:基于 Embedding 模型的上下文窗口调整

你的块大小不能超过 Embedding 模型的最大输入长度。比如 text-embedding-3-small 最大支持 8191 tokens,bge-large-zh 通常支持 512 tokens。块大小要留有余量,别顶着上限用。

第三步:实验验证

这是最关键的一步。建议搭一个评估集,用不同块大小跑一遍,对比检索准确率和最终回答质量。

Reddit 上有个开发者做过实验,结论是:256 tokens 的小块在精确度上明显优于 384 tokens 的块。当然这只是他的数据集结论,你的场景可能不一样,所以一定要自己验证。

四、生产环境的进阶优化

如果你面试的是高级岗位,面试官可能会追问你怎么进一步优化分块效果。几个方向:

  • 语义分块(Semantic Chunking):不是机械地按长度切,而是根据语义相似度来决定切分点。相邻句子的 Embedding 相似度突然下降的地方,就是天然的切分边界
  • 父子块策略(Parent-Child Chunking):检索时用小块(精确匹配),生成时用大块(提供完整上下文)。LangChainParentDocumentRetriever 就是这个思路
  • 元数据增强:给每个 Chunk 附上文档标题、章节信息等元数据,检索时能提供额外的匹配维度
  • 自适应分块(Adaptive Chunking):2026 年的前沿方向——根据每篇文档的结构特点,自动选择最合适的分块策略,而不是一刀切

面试高频追问

  1. 追问一:分块后检索效果不好,你怎么排查和优化?

    先查 Embedding 模型是否合适(中文场景有没有用中文优化的模型),再看是否需要加 Rerank,然后尝试 Hybrid Search(稠密检索 + 稀疏检索融合),最后考虑调整块大小和重叠策略。

  2. 追问二:有没有遇到过块跨页/跨章节导致上下文丢失的问题?怎么解决的?

    用结构化分块(按标题层级切分),或者给每个 Chunk 加上章节标题等元数据,检索和生成时带上这些上下文。

  3. 追问三:不同类型的文档混在一起(PDF、Word、网页),分块策略怎么统一管理?

    根据文档类型路由到不同的分块器,配置一个分块策略工厂。也可以用自适应分块框架(如 2026 年 arXiv 上的 Adaptive Chunking 论文),让系统自动选择最优策略。

常见面试变体

  • "RAG 分块策略有哪些?各自的优缺点是什么?"
  • "块大小对 RAG 检索效果有什么影响?你是怎么调优的?"
  • "如何保证分块后不丢失上下文信息?"
  • "你在项目中用的什么分块方案?为什么选它?"

记忆口诀

分块三原则:语义完整第一,大小适中第二,重叠兜底第三。记住 "小块精度高、大块上下文好、重叠防截断"。

总结

分块是 RAG 的地基,策略选择取决于文档类型和应用场景。递归字符分块是最通用的方案,块大小从 300~500 tokens 起步,根据 Embedding 模型窗口和实际效果调整。面试中重点展示你的调优思路和踩坑经验,这比背一个具体数字有价值得多。