想象你是一位聚会摄影师。第一种做法:守在门口,每位客人一到就咔嚓拍一张,然后立刻换下一位。照片拍得飞快,但你完全不知道谁和谁是朋友、谁刚聊过什么。有位客人端着一盒惊喜蛋糕——可“她”到底是谁,得翻前面那张照片才明白。传统 AI 处理文档就像这位门口摄影师:先把文章切成互不相关的小块,再一块一块地编码成向量。块与块之间的故事,全部丢失了。 Late Chunking(末段嵌入)换了种拍法:先不急着按快门,认真观察整场聚会,等对每个人的背景、关系、来意都了然于心,再挨个拍特写。放到文档处理上,就是先用长文本模型把整篇文档一次性编码,让每个词都带着全文的“记忆”,然后沿着原边界切片、聚合向量。这样一来,藏在段落之间的指代、前后呼应,全都留在切片里。 这篇文章用贯穿始终的“聚会摄影师”比喻,把末段嵌入的原理、关键模型、工程流水线、性能代价与因果评估方法讲清楚,让刚入门 AI 的读者也能立刻抓住“先全文编码再切片”这件事为什么重要。
掌握 Late Chunking,能让你在 RAG 检索、长文档问答等场景中显著提升跨段召回准确率——这是构建高质量知识库系统的关键工程能力。
目录
可视化演示
本节配有交互动效(进入视口后自动加载,约 3KB)。
1. 切片之痛:门口拍照的摄影师
门口摄影师每拍一张就立刻换下一位,照片之间毫无联系。这位摄影师不知道苏珊和莉莉是闺蜜,不知道莉莉刚答应要带惊喜蛋糕来。传统 RAG 系统处理文档也这样:把文档按固定长度切成小块,每个小块单独送进嵌入模型,得到一个向量,存入数据库。快,但“谁认识谁”的关系全丢了。
在检索增强生成(RAG)里,文档切片(Chunking)是第一步。工程师通常会按 256 或 512 个 token 切块,然后逐块编码成向量。问题在于:切块时,边界处的句子会被硬生生截断,后一段开头的“它”“该方案”“上述条款”等指代词,在前一段里明明有明确对象,却被切到了看不见的位置。检索“Lily 的惊喜”时,包含 Lily 的块与包含“惊喜”的块互不知情,系统只能返回残缺的匹配结果。这就是上下文割裂、代词悬空、检索偏差三位一体的“切片之痛”。
用数学表达,传统方式下第 $k$ 个切片的向量 $e_k$ 只由切片本身 $c_k$ 决定,与整篇文档 $D$ 无关:
快照只拍下了表情,没拍下关系。等用户拿着“莉莉的惊喜”来找照片时,摄影师翻遍相册也拼不出完整故事。
- 定义变量$D$ 表示完整文档,$c_k$ 表示第 $k$ 个切片,$e_k$ 是该切片编码出的向量。
- 条件独立含义公式左边以整篇 $D$ 为条件,右边只以 $c_k$ 为条件;两边相等说明 $e_k$ 与其他切片无关。
- 后果$e_k$ 无法携带跨段信息,边界处的指代失效,检索系统面对“它”只能盲猜。
2. 末段嵌入原理:先观察,后按快门
Late Chunking 换了一种思路:摄影师先不着急按快门,而是完整地参加聚会,听谈话、看互动、记关系,最后才把每个客人叫到镜头前。所谓“late“,就是”切片”这个动作被推迟到全文编码之后——先把整篇文档读完,再去切块。
实现上,它依赖一个能输出词元级向量的长文本编码器。假设文档有 $N$ 个词元 $t_1, t_2, \dots, t_N$,编码器一次性读完整篇文档,为每个词元生成一个向量 $h_i$。关键在于,这个向量不仅由词元本身决定,还“看到了”全文:
拿到全文词元向量后,再按照预先定好的边界做切片池化。第 $k$ 个切片从位置 $a_k$ 到 $b_k$,它的最终向量就是这段区间内所有词元向量的均值:
特写虽然只拍一个人,但背景里带着整场聚会的温度——“它”的照片里,Lily 的影子还在。
- 边界变量$a_k$ 与 $b_k$ 是第 $k$ 个切片在原文中的起止位置。
- 区间求和把切片内每个词元的向量 $h_i$ 全部相加,信息从词元层汇聚到切片层。
- 均值归一化除以长度 $b_k - a_k + 1$,得到与词元数量无关的固定维度向量 $e_k$。
3. 关键模型:什么样的相机看得见全场
不是所有相机都能“先观察再拍特写”。普通手机按下快门只能拍到眼前的东西;同样,不是所有嵌入模型都能做 Late Chunking。它要求模型具备两个能力:一是输出每个词元(token)的向量,而不是只输出整段文字的一个句子向量;二是能一次性处理足够长的文本。
目前工程上常用的是 BGE-M3 和 ColBERT 这类模型。BGE-M3 支持长文档、多语言,能输出词元级向量;ColBERT 则天生使用“后期交互”(late interaction)架构,查询与文档的相似度在词元级计算。ColBERT 的得分函数可以写成:
另一边,早期的双编码器(如第一代 Sentence-BERT)只能把整句话压成一个向量,没有逐词信息可供切片,天然无法用于 Late Chunking。好摄影师的眼力等于模型的全文注意力;眼力不够,看得再久也白搭。
- 查询词元向量$q_i$ 是查询中第 $i$ 个词的向量,例如查询“Lily 惊喜”中的"Lily"。
- 取最大相似度对每个查询词,在切片全部词元 $j$ 中找内积最大的那个。
- 累加求和对所有查询词元的结果求和,得到最终相关度得分。
4. 实践流程:从现场到相册的四步
摄影师的工作流很清晰:到场 → 观察 → 开拍 → 入册。Late Chunking 的工程流程也正好四步。第一步是文档预处理:清洗文本、去除噪声,并按句子或段落确定切片边界,同时确保整篇文档长度不超过模型窗口。
第二步是整文编码。把整篇文档一次性送入模型,前向传播一次,得到所有词元的向量序列。这一步对应“认真观察全场”。第三步是切片池化:按第一步定好的边界,把每个切片内的词元向量做均值池化,得到该切片最终向量。第四步是把向量和原文一起写入向量数据库,并在查询阶段使用余弦相似度检索:
相册里的每张照片都带着上下文注解,客户拿一句话来,马上能翻到“那一页”。
- 点积$\mathbf{q}\cdot e_k$ 把两个向量对应维度相乘再求和,衡量“共鸣”程度。
- 模长归一化$\|\mathbf{q}\|$ 和 $\|e_k\|$ 分别是两个向量的长度,除以后消除文本长短的影响。
- 得分区间归一化后余弦值落在 $[-1, 1]$ 之间,检索时按分数降序返回 Top-K 切片。
5. 效果与成本:全场观察的代价
全程盯着聚会观察,很费体力、也很费时间。但照片里的人物关系更完整。Late Chunking 的收益与代价同样对称。收益方面,在 Jina 团队提出的 LoCo 等跨段检索基准上,Late Chunking 相比传统预切片显著提升了跨段查询的召回率——特别是“代词指代”“首尾呼应”类问题。成本方面,它需要一次性编码整篇文档,显存和时延都会上升。
注意力机制的计算量能直观说明问题。假设文档被切成 $K$ 个切片,每个切片平均 $l$ 个词元。传统预切片只做块内注意力:
而 Late Chunking 让所有词元两两互动,注意力范围是整篇文档:
面对这些成本,工程上常见的对策包括:限制单次编码的文档长度、用长文本模型分章编码、缓存词元向量、文档小幅更新时只重算受影响切片。场景适配的原则是:企业知识库、法律合同、论文综述这类跨段指代密集的长文档,值得付出成本;短文本、实时聊天等场景,传统预切片仍然更划算。大型宴会值得全程观察;三五好友小聚,门口拍一张就够了。
- 预切片开销$K$ 个切片各做 $l^2$ 的注意力运算。
- 末段嵌入开销全文长度是 $Kl$,所有词元两两注意。
- 对比结论当 $K=10$ 时,末段嵌入的注意力开销是预切片的 10 倍,显存峰值与延迟随之上升。
6. 因果视角:换一种拍法真的更好吗
假设同一场聚会,两组摄影师分别用“门口快拍”和“全场观察”两种拍法,最后拿同样一组问题去考验哪组照片能回答。这就是一场对照实验。它背后正是因果推断中的潜在结果框架:切片策略被视为一种干预,检索质量被视为结果,而“如果当时用了另一种切片方式,这次检索会是什么结果”就是反事实。
在这个框架下,把预切片记为干预 $P$,末段嵌入记为干预 $L$。在相同文档集合与查询集合上分别运行,得到两个潜在结果 $Y(P)$ 与 $Y(L)$。两者的期望差就是平均处理效应:
工程上落实这种因果思维有三种做法:一是同一组文档同一组查询做 A/B 对比;二是反事实分析——对检索失败的样本追问“换成另一种切片方式是否就能命中”;三是消融实验——去掉全文上下文、换成其他池化方式,逐一验证每个模块的贡献。不要只夸“照片好看”,而要问:换一种拍法是不是真的让客户更容易找到照片?把直觉变成可验证、可归因的因果结论。
---
- 潜在结果$Y(P)$ 表示同一批数据在预切片干预下的检索质量。
- 对比干预$Y(L)$ 表示末段嵌入干预下的检索质量。
- 期望差$\mathbb{E}$ 对多次实验取平均,消除随机波动,得到稳定的处理效应。
AI 应用场景
企业知识库 RAG 问答
阶段:文档解析与索引(检索增强生成)
解决的问题:员工提问常带指代,如“这个提案的预算上限是多少”,“这个提案”指向前文标题。传统预切片把标题和正文切到不同块,导致召回错块、回答张冠李戴。Late Chunking 让正文切片携带标题信息,跨段指代被完整保留。
具体案例:某咨询公司知识库收录数百份长方案,上线 Late Chunking 后,“上述方案”“该客户”“这个预算”类查询的首段命中率提升约二十个百分点。
法律与合同文档检索
阶段:法律文档向量化与条款检索
解决的问题:合同条款常以“第 3 条所述义务”“上述违约情形”进行跨条引用。预切片把引述主体与定义条款拆散,律师检索时找不到完整逻辑链。
具体案例:一家 LegalTech 产品对五十页合同做跨条款问答,使用 BGE-M3 加 Late Chunking 后,“上述违约情形”类问题能正确关联前文定义条款,人工复核效率明显提升。
科研文献综述助手
阶段:论文长文本索引与跨节问答
解决的问题:论文常先定义简称(如“本文方法(LCE)”),后续用简称展开叙述。学生提问“LCE 的损失函数是什么”时,需要把简称定义片段与后文解释片段同时召回。Late Chunking 让后文切片带着定义信息,跨节问答正确率明显高于预切片。
具体案例:某高校文献综述机器人用 ColBERT 风格模型实现 Late Chunking,跨节问答准确率从 62% 提升到 81%。
Python 演示
import numpy as np class TinyEncoder: """极简编码器:每个词一个独热向量,加上上下文均值模拟全文注意力。""" def __init__(self, vocab): self.dim = max(len(vocab), 1) self.index = {tok: i for i, tok in enumerate(vocab)} def base(self, token): vec = np.zeros(self.dim) vec[self.index[token]] = 1.0 return vec def encode(self, tokens, context): context_vec = np.mean([self.base(tok) for tok in context], axis=0) return np.stack([self.base(tok) + 0.8 * context_vec for tok in tokens]) def cosine(a, b): return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b) + 1e-12)) def chunks_of(tokens, size): return [tokens[i:i + size] for i in range(0, len(tokens), size)] def chunk_vectors(tokens, chunks, mode, encoder): vectors = [] for chunk in chunks: context = tokens if mode == "late" else chunk token_vecs = encoder.encode(chunk, context) vectors.append(np.mean(token_vecs, axis=0)) return vectors def main(): doc = ["Lily", "bakes", "a", "cake", "for", "the", "party", ".", "It", "is", "the", "surprise", "she", "promised", "last", "week", "."] chunks = chunks_of(doc, 4) vocab = sorted(set(doc)) encoder = TinyEncoder(vocab) pre_vecs = chunk_vectors(doc, chunks, "pre", encoder) late_vecs = chunk_vectors(doc, chunks, "late", encoder) c0, c2 = 0, 2 print("第0块:", chunks[c0]) print("第2块:", chunks[c2]) print("跨块相似度 传统预切片:", round(cosine(pre_vecs[c0], pre_vecs[c2]), 4)) print("跨块相似度 末段嵌入:", round(cosine(late_vecs[c0], late_vecs[c2]), 4)) lily = encoder.base("Lily") sur_pre = encoder.encode(["surprise"], chunks[c2])[0] sur_late = encoder.encode(["surprise"], doc)[0] print("surprise 与 Lily 关联 传统预切片:", round(cosine(sur_pre, lily), 4)) print("surprise 与 Lily 关联 末段嵌入:", round(cosine(sur_late, lily), 4)) if __name__ == "__main__": main()
context = tokens if mode == "late" else chunk这是两种模式唯一的区别:late 模式把全文作为上下文,pre 模式只把当前切片作为上下文。encoder.encode(chunk, context)模拟编码器:每个词的向量 = 自身独热向量 + 上下文均值,上下文范围决定了能否“看到”远处的人名。np.mean(token_vecs, axis=0)切片池化:把片段内词元向量平均成切片向量。cosine(pre_vecs[c0], pre_vecs[c2])计算第 0 块与第 2 块的相似度,pre 模式结果为 0,说明两块毫无关联。cosine(sur_late, lily)检查“surprise“这个词的向量是否认识”Lily”,late 模式下关联度大于 0。
常见误区
误区我以为 Late Chunking 就是调整切片大小,把每块切得更长,让一块里包含更多文字,上下文自然就丰富了。
正解切片大小和编码顺序是两回事。Late Chunking 的“晚”指的是切片动作发生在全文编码之后,关键是每个词元先见过全文再被切走。即使切片很小,每个切片仍然携带全文上下文。切大块只会增加计算量,并不会带来“全文记忆”。
为何常见:直觉上“更多文本 = 更多上下文”,容易把改进归因到块大小,而忽略了编码顺序这一真正变量。
误区我觉得只要把现有嵌入模型输入整篇文档,拿到输出后再按文本位置切出几段向量就行了。
正解普通双编码器只输出整段文字的一个句向量,根本没有逐词向量可供切片。必须使用能输出词元级向量、且支持长文本的模型(如 BGE-M3、ColBERT 类)。否则“先编码再切片”无从谈起。
为何常见:许多教程只强调“先编码再切片”的流程,没有说明模型需要词元级输出能力这个硬前提。
误区我听说它要一次性编码整篇文档,显存和时延都很高,觉得生产环境肯定用不了。
正解它的成本真实存在,注意力开销随块数近似平方增长。但在跨段指代密集的长文档场景,收益往往远超成本。正确的做法是用因果对比实验量一量:同一份数据、同一组查询,分别测两种方案的召回与延迟,再决定是否启用。
为何常见:只看到了成本数字,没有量化收益,被“贵”这个印象吓退。
误区我觉得反正切片向量是池化出来的,边界切在句子中间也没关系,模型会自己理解。
正解边界切在句内会拆散完整语义单元,把一个词从它的搭配中剥离,池化时还会把残缺词元的噪声平均进去。工程上应沿用句子、段落边界切分,保证每个切片内部语义完整。
为何常见:Late Chunking 强调“先编码再切”,让人误以为切分步骤已经不重要了。
误区我觉得 mean pooling 会把关键词的信息平均掉,Late Chunking 的效果全靠池化方式,越复杂越好。
正解均值池化是默认且有效的选择,它把全文上下文均匀注入每个切片并保持维度稳定。长切片里可以试验加权池化或结合词元权重,但应先通过消融实验确认池化是否真的是瓶颈——多数时候主要矛盾在于上下文有没有被编码。
为何常见:深入高维向量的细节后,容易陷入“调池化”的局部优化,忽略了整体流程的主干。
面试题
Q1. 关于“预切片”(传统 Chunking)的编码方式,正确的是?
- A.每个切片独立编码,互不可见← 正确答案
- B.所有切片拼接后统一编码
- C.先编码全文再切出各块
- D.仅用标题参与每个切片
答案:A。传统预切片把文档切成互不相干的小块,每个切片单独送入编码器,块与块之间没有信息流动。选 A。B 是整文一次编码,C 正是 Late Chunking,D 不是常规做法。
Q2. 为什么预切片会出现“代词悬空”问题?
- A.分词器误把代词截断
- B.切片编码时看不到前文← 正确答案
- C.向量维度设置得偏低
- D.查询没有用余弦相似度
答案:B。代词(如“它”)的指代对象往往在前一个切片中,预切片让每个块只看自己,“它”在编码时找不到 Lily。这属于编码视野受限,与分词、维度、相似度计算方式无关。
Q3. 下列哪些能力是支持 Late Chunking 的模型必须具备的?
- A.输出每个词元的向量表示← 正确答案
- B.支持一次性编码长文本← 正确答案
- C.参数量必须超过七十亿
- D.词元间存在全文注意力← 正确答案
答案:ABD。Late Chunking 需要先对全文一次性编码,再按切片池化词元向量,所以 A、B、D 都是必要条件。模型参数量没有硬性门槛,BGE-M3 等中等规模模型即可胜任,C 错误。
Q4. Late Chunking 中,一个切片对应的最终向量通常如何得到?
- A.直接取该片段的 CLS 向量
- B.对片段内词元向量做均值池化← 正确答案
- C.取片段内最后一个词元向量
- D.对所有词元向量做随机采样
答案:B。全文编码完成后,按原边界把切片内所有词元向量做平均(mean pooling),得到该切片向量。CLS 向量通常代表整篇文档,取末尾词元会丢失切片内部信息,随机采样更不可靠。
Q5. Late Chunking 最核心的收益是什么?
- A.显著降低内存和显存开销
- B.保留跨段的上下文语义← 正确答案
- C.彻底省略切片的步骤
- D.让模型参数量变少
答案:B。先全文编码再切片,让每个切片携带全文上下文,从而保留跨段指代与语义关联。它不会降低资源开销,也没有省略切片,更不改变模型参数量。
Q6. 下列哪些属于 Late Chunking 的代价或限制?
- A.长文档一次编码显存增加← 正确答案
- B.相比逐块编码时延变高← 正确答案
- C.文档更新需要重新编码← 正确答案
- D.一定降低检索召回率
答案:ABC。全文一次性编码使注意力与显存开销近似平方增长,时延上升;文档更新后相关切片的向量需要重算。但它通常提升召回率,D 的说法与事实相反。
Q7. 用因果视角评估两种切片策略时,“切片方式”扮演什么角色?
- A.协变量,只是背景属性
- B.干预,直接影响检索结果← 正确答案
- C.混淆因子,干扰对比结果
- D.结果变量,随检索变化
答案:B。在潜在结果框架中,我们主动施加“用哪种切片方式”这个动作,并观察它对检索质量的影响,因此切片方式是干预。协变量是被动观测的属性,混淆因子是同时影响干预和结果的第三变量,均不对应。
Q8. 关于“消融实验”用于 Late Chunking 的说法,正确的有?
- A.去掉全文上下文,验证其贡献← 正确答案
- B.更换池化方式,检查敏感性← 正确答案
- C.用上线 A/B 测试替代所有消融
- D.帮助定位收益的来源模块← 正确答案
答案:ABD。消融实验通过逐项移除或替换组件(如去掉全文上下文、换池化方式)来定位收益来源。A/B 测试是整体效果对比,不能替代逐组件归因,C 错误。
Q9. ColBERT 使用的相似度计算方式是?
- A.双编码器最后一层点积
- B.词元级最大相似度累加← 正确答案
- C.交叉编码器的全连接层
- D.随机森林排序打分
答案:B。ColBERT 采用 late interaction:查询每个词元与文档词元逐对计算相似度,取最大再累加(MaxSim)。它介于双编码器与交叉编码器之间,不是简单点积、全连接层或树模型。
Q10. 对于经常小幅更新的文档集合,最合理的做法是?
- A.缓存整文向量,增量重算受影响块← 正确答案
- B.每次修改都全量重新编码
- C.放弃向量检索改用词频统计
- D.把切片长度缩短到最小
答案:A。小幅更新时,可缓存文档词元向量,只对受影响的切片重新池化,避免全量重编码。全量重编码成本过高,词频统计丢失语义,盲目缩短切片长度会加剧上下文割裂。