← RAG 与知识检索 · 第 2 篇

文档怎么切:先保留语义,再讨论向量索引

理解切块、重叠、元数据与向量索引的职责,用窗口切分示例观察边界与证据完整性。

学完这一篇,你能做到

能说明块大小、重叠和索引参数分别影响什么,并保留可追踪的文档位置。

切块决定“一个可检索单元里保留多少意思”,向量索引决定“怎样更快找到候选单元”。两者相关,但不是同一个问题。

如果切块已经把条件和结论分开,换一个更快的索引也不会把证据自动修好。先观察文档结构,再调检索参数。

从文档结构开始

标题、段落、列表、表格和代码块往往带有语义边界。优先避免把表格表头与数据、操作前提与操作步骤、代码与解释拆到完全无法关联的块里。

每个块保留文档 ID、版本、标题路径和原文位置。检索结果可能需要回查相邻段落,引用也要依赖这些位置。

重复切分同一个版本,应得到稳定或可映射的块标识。更新时删除过期内容,不能只追加新块而让旧结论继续被召回。

块大小与重叠的取舍

块太小可能缺少上下文,太大可能把大量无关内容带入结果。重叠能缓和边界问题,却增加索引体积与重复证据。

没有适合所有资料的固定大小。技术说明、表格和长篇叙述需要分别观察。最终用代表性查询评估,而不是只看切出来的块是否整齐。

用窗口观察边界

下面按字符切分,只用于演示位置和重叠;它不是面向生产的语义切分器,也不能代替模型 token 计数。

def windows(text, size, overlap):
    if size <= 0 or not 0 <= overlap < size:
        raise ValueError("invalid window settings")
    chunks = []
    start = 0
    while start < len(text):
        end = min(start + size, len(text))
        chunks.append({"start": start, "end": end, "text": text[start:end]})
        if end == len(text):
            break
        start = end - overlap
    return chunks

text = "Keep the condition next to its conclusion."
chunks = windows(text, size=18, overlap=5)
assert chunks[1]["start"] == chunks[0]["end"] - 5
assert chunks[-1]["end"] == len(text)
assert all(c["text"] == text[c["start"]:c["end"]] for c in chunks)
for chunk in chunks:
    print(chunk)

把原文换成有标题和表格的说明,观察窗口是否留下了完整意思。这个观察通常比立即选一个向量库更能暴露问题。

向量和索引分别负责什么

Embedding 把输入映射到向量空间。向量距离是候选检索信号,不直接表示答案是否真实。查询与文档的编码方式要与所选模型一致。

HNSW 等近似索引通过图结构加快近邻搜索,通常需要在召回率、延迟和内存之间取舍。索引参数无法弥补错误的文档解析、过期资料或权限遗漏。

对于专有名词、标识符与精确关键词,词法检索也有价值。是否组合词法与向量召回,应由你自己的查询集决定。

下一步

挑选十个问题,使用两套切块方案检索。比较正确证据是否出现、是否完整,以及上下文中有多少重复资料。

保留输入、切分版本与结果 ID,进入下一篇,把这些观察变成可重复的检索评测。

资料来源与延伸阅读

资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。