Skip to content

从原文到可替换的索引 ​

检索单位不是磁盘上的文件,而是携带来源的片段。过长片段浪费窗口,过短片段丢失主语和条件;重叠缓解边界断裂,却增加重复、成本和版本清理责任。

一个保留偏移量的切分器 ​

用字符切分只是观察边界,真实模型预算应按对应 tokenizer 的 token 数计算。下面 start/end 是 Python 字符串下标,不是 PDF 坐标或 UTF-8 字节偏移。参数要求 0 <= overlap < size,否则步长非正无法前进。

python
def chunks(text, size=6, overlap=2):
    if size <= 0 or not 0 <= overlap < size:
        raise ValueError('invalid window')
    out = []
    for start in range(0, len(text), size - overlap):
        end = min(start + size, len(text))
        out.append({'start': start, 'end': end, 'text': text[start:end]})
        if end == len(text):
            break
    return out
parts = chunks('ABCDEFGHIJ')
assert [p['text'] for p in parts] == ['ABCDEF', 'EFGHIJ']
assert all(p['text'] == 'ABCDEFGHIJ'[p['start']:p['end']] for p in parts)
assert chunks('') == []
print(parts)

第二片段从 E 开始,因为第一片段结束位置是6、重叠2,所以前进4。重叠可保留跨片段的词,却不能保证一句政策完整;“不得”落在上片、“报销”落在下片仍可能误解。标题、段落、表格、代码函数应优先按结构切分;检索小片段、回填父段落时还要重新检查父段落权限与 token 预算。

文档身份、内容版本与索引版本 ​

文档稳定 ID 是业务身份;内容哈希识别是否变化;片段 ID 可以由文档 ID、内容版本、切分配置和位置组成。若只对文本内容取哈希,不同租户恰好相同的文字会撞到同一业务记录。若只用“第3片”,切分参数变化后原第3片就不再是原内容。

更新应先构造一个完整新版本,确认所有片段可检索,再把活动指针切到新版本。删除旧片段、向量、摘要、缓存属于同一血缘清理过程。并发摄取需要版本比较,晚完成的旧作业不能覆盖新版本。失败后保留旧版本可用;部分写入的新版本不可对查询开放。

Embedding 模型、维度、归一化方法、输入前缀、切分配置都属于索引契约。同维度不意味着同一空间;更换模型要重建文档向量,再用相同模型编码查询。迁移时建新索引并用同一测试集对比召回、延迟和成本,再切流量;直接覆盖原向量会失去回滚依据。

练习 ​

为 chunks 写三个边界断言:长度小于窗口、刚好一个窗口、非法重叠。再解释重叠为何不应该跨租户。

完成后查看本章答案。

下一章:召回、重排与上下文预算。

用心记录每一步成长