RAG 两阶段检索:摘要做索引,原文做答案
我们的电子书产品有一个 AI 增值功能:用户把自己账号下的书籍(PDF/PPT/Word 转换而来)构建成私有知识库,然后向它提问。日活 1-2 万,稳定后每天约 1 千次提问。这篇文章记录检索链路的设计思路,和一次印象深刻的真实调优。
先说结论
整条检索链路长这样:
用户提问 → 攻击性内容识别(LLM 前置拦截)
→ ES 粗排:multi_match 查摘要+关键字,取 top10(十几毫秒)
→ 向量精排:在 top10 候选集内做向量相似度匹配,取 top3(十几毫秒)
→ top3 原文段落交大模型流式生成(1 秒左右开始输出)
一句话概括设计哲学:摘要做索引、原文做答案来源——索引层管召回精度,原文层管答案完整性,两层各干各的。
为什么是两阶段,不是一路检索
单用 ES 不够。用户提问是自然语言,"怎么导出"和"如何保存成文件"是一个意思,纯关键词匹配容易漏——语义召回不足。手册类文本里还有大量同义表述,关键词的召回率天花板明显。
单用向量也不够。书籍里大量专有名词、型号、参数、错误码,向量对这种精确术语反而不敏感——关键词匹配才是强项。而且全库向量检索在大数据量下要么慢、要么走 ANN 索引有精度损失。
所以两阶段:ES 用摘要+关键字做粗排,十几毫秒把字面相关的前 10 条捞出来;向量匹配只在这 10 条的小候选集内做精排取 top3——语义精排放在小集合上,又快又准,还避开了全库向量检索的性能问题。
为什么只给大模型 top3 而不是 top10?上下文越多 token 越贵、首包响应越慢,无关段落还会稀释模型注意力。3 段高质量上下文足够支撑回答。
ES 粗排的细节
{
"query": {
"bool": {
"must": [
{ "multi_match": {
"query": "<用户问题>",
"fields": ["summary^2", "keywords"],
"type": "best_fields",
"minimum_should_match": "60%"
}}
],
"filter": [{ "term": { "agent_id": "<当前知识库>" } }]
}
},
"size": 10
}
几个设计点:
- best_fields 取单字段最高分而不是求和——避免"摘要和关键字都沾边"的段落靠累分挤掉"摘要强命中"的段落。粗排要的是"某个维度上像"
- summary 加权 2 倍:摘要承语义主信号,关键字承术语精确信号
- minimum_should_match 60% 挡弱相关:粗排宁缺勿滥,噪音会占掉向量精排的候选位
- filter 按知识库 ID 隔离:多租户,filter 不算分还走缓存
一次真实的调优:摘要替代原文向量
最初版本是原段落直接算向量。上线后发现长段落召回经常不准——明明书里有答案,检索就是捞不到。
我从问答落库里把低分案例捞出来做归因,发现正确答案基本都不在命中的 top3 里——问题出在检索,不在生成。再往下拆,定位到一个一开始没想到的点:
我们的分段按自然段落切,大的段落有两千字上下。这种长段落里往往混着好几个主题——步骤、参数、注意事项都在同一段。直接对原文算向量,得到的是所有主题的"平均值",语义被稀释了。而用户提问短而聚焦——一个聚焦的向量去跟一个"平均"的向量比相似度,天然不占优。
想明白之后改法就很自然:入库时让大模型给每段先提炼摘要和关键字,向量改用摘要来算——摘要把一段话的主题重新聚焦,跟用户提问的语言分布也更接近。改完之后召回明显改善,好评率也上来了。
摘要有损怎么办
主动承认:摘要是有损压缩——LLM 认为不重要的细节(具体参数值、顺带的警告)在向量里不可见了;摘要质量差则检索差,且入库时就冻结了。
系统为什么扛得住?因为精确术语由 ES 关键字一路兜底——关键字字段保留原文术语,"摘要向量管语义、关键字管精确",两路互补。这是整个架构的隐性优势。
还有一层收益:敢用大分段。top3 给大模型的是原文,上下文完整、步骤表格不被切碎。如果分段很小(几百字),稀释问题消失,我会直接用原文做向量省掉摘要成本——"大分段+摘要索引"和"小分段+原文索引"是两种自洽方案,我们选前者是因为答案完整性优先。
业界同源思路可以参考 Anthropic 的 Contextual Retrieval:给 chunk 加上下文描述再做 embedding,检索失败率降约 35%——本质都是"改造文本再入向量"。
效果怎么度量
三层评测体系:问答全量落库(问题+命中段落 ID+回答)、用户打分、人工抽检。关键是归因方法:打分低的案例,看正确答案在不在命中 top3 里——在,是生成问题;不在,是检索问题,再往上是入库摘要质量问题。每次"回答不好"都能定位到具体环节。
前面那次摘要调优,就是这套体系跑出来的:低分案例集中表现为"答案不在 top3",才把矛头指向向量稀释。
收尾的账
- 性能:ES、向量检索各十几毫秒,LLM 流式响应 1 秒左右开始出答案
- 成本:单次问答约 0.15 元(输入 3 段原文占大头);入库约 0.05 元/段,30 页小书约 0.5 元/本
- 效果:用户打分好评率 80% 以上
这次调优让我对 RAG 的理解深了一层:检索质量的瓶颈往往不在算法,而在"你拿什么文本去算向量"。