Markdown 标题未分块
MarkdownTextSplitter 把标题粘到上一块,检查语言正则是否被转义。1.1.3 显式开启 is_separator_regex,保留 chunk 预算与索引边界。
用 LangChain 的 MarkdownTextSplitter 切文档,二级标题却粘在上一块末尾,标题后面的正文落到下一块。没有异常、chunk 大小也符合配置时,可以检查语言分隔符是不是被当成普通字符串转义了。
这个问题已在 langchain-text-splitters 1.1.3 的实现中核对:Markdown 子类拿到了正则形式的语言分隔符,但没有默认启用 is_separator_regex。正式 1.1.3 仍有这条路径,不能把升级到该版本写成修复。
正则列表与字面匹配混在一起
Markdown 的语言分隔符包含 \n#{1,6} ,意思是换行后的一到六个 # 和空格。基础递归切分器的 is_separator_regex 默认却是 false,会先对分隔符执行 re.escape。
这样一来,它要找的是文本中真实存在的 #{1,6} 字符串,普通 ## 标题 不会匹配。切分继续退到空行、换行或空格,仍能产出结果,却没有按预期使用标题边界。
先保留原文中的标题行、切分器实际类名和完整参数,比较标题及相邻正文落在哪个 chunk。RAG 检索结果看着不连贯,也可能来自召回、重排或后续截断;只有确认标题规则没有参与切分,才符合这里的处理路径。
MarkdownHeaderTextSplitter 是另一种类,按指定标题生成结构与元数据。它没有走这里的子类构造路径,不能把两者的选项混用。
把语言分隔符按正则使用
1.1.3 的 Markdown 子类会把额外参数交给基础递归切分器,因此可以明确开启正则。下面只构造切分器,不请求模型或写入向量库:
from langchain_text_splitters import MarkdownTextSplitter
splitter = MarkdownTextSplitter(
chunk_size=500,
chunk_overlap=50,
is_separator_regex=True,
)
大小与重叠是示例值,应保留应用原来审定的预算、长度计量和分隔符保留方式。另一种入口是 RecursiveCharacterTextSplitter.from_language(Language.MARKDOWN, ...),它已明确开启正则;没有必要复制整份内部语言列表。
同一版的 LatexTextSplitter 也没有默认开启这个选项。但切换正则解释会影响它的整组语言分隔符,数学、环境和命令边界仍需用实际 LaTeX 内容核对,不能只修一个 Markdown 例子就承诺所有格式都正确。
启用正则后,标题规则能参与选择,并不表示每个标题必定生成一个独立 Document。递归切分还会按 chunk 大小合并短片段,长度过大又会继续使用更细的分隔符。需要严格按标题分组的应用,应选与那个目标相符的结构切分器。
原索引也要检查切分边界
这项调整会改变已有文档的 chunk 边界。先在受控的原文样本上对比标题、正文、重叠和元数据,再按应用的索引更新流程处理受影响文档;不要直接清空共享向量库。
旧 chunk ID、页码或引用位置如果依赖切分结果,需要一起核对。构造器不再误用分隔符,只说明切分路径改变,不能证明检索召回或回答质量已经提升。
若标题仍与正文分开,继续检查原文是否包含标准标题语法、文本是否已经被转换,以及大小与合并规则。没有换行的首行标题、代码块内的井号或另类标题格式,不应一律按这个语言正则解释。


