← All writing大模型:从第一个 token 开始 · 07

Embedding、RAG 与评估:会检索不等于会回答

把检索召回、重排、上下文构造和答案归因分开测量,用失败类型推动系统改进。

Read the English edition →

Embedding、RAG 与评估:会检索不等于会回答

从零开始的完整讲解

为什么模型知道很多,仍然需要检索

预训练把统计规律写进参数,但它不是随时更新的企业文档库。某项退款政策昨天改了,模型参数不会自动同步。RAG 在回答前先找相关材料,把材料作为本次输入的一部分。它改变的是模型可见的证据,不一定改变模型权重。

一个完整链路至少包含文档解析、分块、索引、查询编码、候选检索、重排、上下文组织与生成。每一环都可能丢失信息。若 PDF 表格在解析时把金额和产品名错配,后面的优秀模型也可能基于错误材料给出流畅答案。

用一个小例子拆开检索和回答

用户问:“购买后第 20 天还能退吗?”库里有两段:“普通商品 30 天内可退”和“定制商品不支持无理由退货”。主题相关的两段都可能被召回,但答案还需要知道商品类型。合理输出可能是先问商品是否定制,而不是机械选相似度最高的一段回答“可以”。

Dense retrieval 用向量找语义接近内容,关键词检索擅长精确实体、型号与稀有词。混合检索结合不同信号;重排器通常对 query-document 对做更细判断,但多了一次计算成本。不要在没有错误分析时直接堆更多模块。

分块不是随便每 500 字切一刀

块太短可能失去主语、条件和表格标题,块太长又可能稀释检索信号,并占据更多上下文预算。重叠可以保留边界信息,却增加索引冗余和重复召回。文档结构、段落关系与业务问题类型应共同决定分块策略。

对多模态文档,截图中的文字、表格单元格和图片说明可能互相依赖。最好保存来源页码、标题路径与原始坐标,使答案能追溯。索引更新时也要处理删除和过期版本,否则检索到旧政策仍会导致错误。

用可计算指标定位失败环节

设某个问题有 2 个已标注相关块,top-5 找到了其中 1 个,Recall@5 是 0.5。若第一个相关块排第 3,reciprocal rank 是 1/3。它们衡量检索列表,不衡量最后一句答案是否正确。答案即使碰巧正确,也不能证明检索可靠。

可以做“oracle context”实验:直接把人工确认的正确证据交给生成模型。如果仍然答错,问题更可能在提示、推理或答案约束;如果这样能答对但实际 RAG 答错,则重点检查召回、重排和上下文截断。这是隔离变量,不是生产系统的额外能力。

建立一个小而可信的评测集

从真实问题出发,记录可接受答案、必要证据、是否需要澄清、是否允许拒答。按时间或文档来源划分训练和评测,避免同一答案的近重复出现在两边。单看平均正确率会掩盖退款金额、日期、权限等高风险子集,应单独报告。

LLM judge 能提高评测效率,但会受措辞、长度和答案顺序影响。用人工样本校准,对争议案例复核,保留判分依据。证据忠实性和世界事实正确性也要区分:答案可以忠实复述一份过期文档,同时在现实中已经错误。

浏览器里可以怎样练习

先在模型输入输出页计算几段文本的真实向量与相似度,再人为修改否定词或型号,观察 dense retrieval 的盲点。自己构造五份文档与三个问题,用 Python 排序并算 Recall@k。下一步再接真实索引,保留这个小测试集作为回归检查;不要一开始就用庞大数据库掩盖每一步的作用。

一个完整 RAG 请求

用户问“这个服务的超时是多少?”系统先把问题转成向量,从文档块里召回候选,再用 reranker 判断相关性,把选中文本交给生成模型,最后给出有出处的答案。不同环节可能失败:文档没收录、切块割裂条件、召回偏离、重排遗漏、上下文截断,或生成模型忽略证据。

把所有失败都称为 hallucination 会让修复失去方向。先记录每一步输入输出,再问正确证据首次在哪里消失。RAG 的目标是给模型提供可更新依据,而不是保证一切回答正确。

向量检索的形状与边界

文档矩阵为 [N,D],query 为 [B,D],点积得 [B,N]。归一化后点积等于 cosine,但 index 的距离度量必须与训练和归一化方式一致。ANN 用近似搜索换延迟与内存,Recall@k 还受索引参数影响。训练 embedding 用的相似性目标可能和实际任务不一致。

import torch
import torch.nn.functional as F
docs = F.normalize(torch.tensor([[1.,0.],[0.,1.],[1.,1.]]), dim=-1)
query = F.normalize(torch.tensor([[.9,.1]]), dim=-1)
scores = query @ docs.T
print(scores.topk(2, dim=-1).indices)

这是检索几何示例,不是真实语义模型。真实 embedding 请使用前一章。

给每层一个可失败的指标

层 指标 典型诊断
语料 证据覆盖率 答案是否存在于快照中
召回 Recall@k 正确块能否进入候选
重排 MRR / NDCG 正确块是否排在前面
生成 正确性、引用支持率 引用是否真的支持该主张
服务 p95 延迟、失败率 用户能否稳定拿到结果

比较系统时固定语料快照、问题集、token 预算与延迟约束。不能把更多上下文带来的收益归因于一个新检索器。LLM judge 应校准到人工样本,并检查位置偏差、长度偏好及与被测模型的相关错误。

自测

问题: Recall@20 上升,但最终正确率下降,可能吗?

展开答案可能。更多噪声可能挤掉上下文预算、放大冲突或让生成器选错证据。还可能是重排或 prompt 变化。按阶段记录命中与失败,才能定位。

原始资料与继续阅读

资料核对:2026-09-10。教学示例不代表生产基准;框架 API 和模型支持请以所链接版本为准。

NOTEBOOK · LIVE PYTHON

在文章里运行 Notebook

逐格改代码、运行,或者一键运行全部。变量在单元之间保留;离开页面或重启内核会清空变量。编辑上游单元后,请重新运行后续单元。

此版用真实 Python 实现可检查的小型计算与训练,不需要 GPU。PyTorch/CUDA 不在此浏览器内核中;完整预训练模型 Notebook 见另一个标签,大模型基础的 Notebook 还提供单独加载真实 MiniLM 的可编辑单元。

内核尚未加载;点击运行开始。

正在加载 Notebook…