
RAG 分块策略实测固定滑窗、段落感知怎么选80 行 Python 附自检之前写过最小 ReAct Agent和Transformer/解码系列里面的检索都是假占位——这篇把真东西补上RAG 的第一环分块。收藏榜上 RAG 进阶帖和向量数据库帖都在收收藏但进阶帖讲的是父子分块/层级索引那些大设计基础分块写对的人反而不多。本篇只做两件事三种策略的代码和一个能抓住丢字事故的自检。结论先放这儿策略怎么切优点缺点固定滑窗按长度 重叠保证不超长实现最简语义经常被腰斩段落感知段落聚合段内按句拆边界贴语义实现多几行硬切兜底超长单句直接切保证不超长语义最差只做最后手段两句话铁律分隔符优先级从大到小段落 句子 硬切分块代码必须有覆盖率自检。一、为什么分块是 RAG 第一坑块太大向量语义被稀释检索不精准块太小上下文残缺检到了也答不对。多数人死在第三种事故上分块代码把字符弄丢了——丢的往往是一句话的开头或结尾检索照常出结果答案悄悄变差不报错很难查。所以本篇的自检核心就一条分块结果拼回去必须还原原文。二、完整代码单文件直接跑# chunking.py — 固定滑窗 vs 段落感知分块 # 依赖无纯标准库 import re def fixed_chunks(text, size200, overlap20): 固定长度 重叠滑窗 if overlap size: raise ValueError(overlap 必须小于 size否则 step0 死循环) step size - overlap return [text[i:i size] for i in range(0, len(text), step)] or [] def split_long(text, max_size): 单段超长按句拆保留标点塞不下的单句硬切兜底 chunks, buf [], for s in re.split(r(?[。]), text): if not s: continue if len(buf) len(s) max_size: buf s else: if buf: chunks.append(buf) while len(s) max_size: # 单句超长硬切 chunks.append(s[:max_size]) s s[max_size:] buf s if buf: chunks.append(buf) return chunks def paragraph_chunks(text, max_size200): 段落感知\n\n 聚合段内按句拆相邻小块合并 chunks [] for para in text.split(\n\n): para para.strip() if not para: continue for c in split_long(para, max_size): if chunks and len(chunks[-1]) len(c) 2 max_size: chunks[-1] \n\n c # 相邻小块合并减少碎块 else: chunks.append(c) return chunks if __name__ __main__: text \n\n.join( f第{i}段。 这是测试内容 * (30 if i % 3 0 else 5) for i in range(20) ) 超长无标点句子 * 100 # 触发硬切兜底 # 1) 固定滑窗去重叠后拼回应原文丢字/重字当场报 fc fixed_chunks(text, size200, overlap20) rebuilt fc[0] .join(c[20:] for c in fc[1:]) assert rebuilt text, 滑窗分块丢字或重字 assert all(len(c) 200 for c in fc) # 2) 段落感知无丢字 不超长 sc paragraph_chunks(text, max_size200) assert .join(sc).replace(\n\n, ) text.replace(\n\n, ), 段落分块丢字 assert all(len(c) 200 for c in sc) print(f固定滑窗: {len(fc)} 块 段落感知: {len(sc)} 块 self-check ok)两条 assert 各管一类事故滑窗的覆盖率检查抓重叠偏移算错最容易写错的地方段落的无丢字检查抓分隔符切丢内容。块数差异也能看出来段落感知的块更少更整齐。三、三个踩坑都见过真实事故token ≠ 字符中文 1 字对应 1~3 tokenchunk_size按字符数设 200实际 token 可能是 400向量库的 token 上限直接报错。预算按 token 算字符数只是代理overlap ≥ size 死循环step size - overlap ≤ 0range 一步不走直接卡死。代码里 raise 掉别信调用方表格/代码块被腰斩Markdown 表格按行切后每块都是没头的几列检索命中了也没法用。结构化内容先按块整体提取再分块别让通用切分器碰它四、下一步检索块和上下文块拆开本篇三策略有个共同假设检索用什么块就返回什么块。进阶玩法是把两个职责拆开——子块做检索锚点短、准命中后沿映射返回父块完整上下文即父子分块/放大检索。这个需要稳定的 parent_id 映射映射不稳会出现“命中 A 返回 B”的事故值得单开一篇。总结铁律压成三句分隔符优先级从大到小硬切只做兜底块大小按 token 预算算字符数只是代理分块代码必须有覆盖率自检丢字是最隐蔽的 RAG 事故