很多人接完模型才发现,用户问的是自己内部资料,光靠关键词搜出来的东西答不上来;换成向量召回,又出现答非所问、引用错段落的毛病。这篇不重复原理课,只给一套对照和排查顺序:先看数据长什么样,再决定用关键词、向量还是两者混着来,以及接进生产后超时、并发、降级、日志、评测这几件事分别该怎么留口子。
两者到底差在哪:匹配的是字面还是语义
先把两种做法拆开看。关键词检索(BM25、倒排索引那一套)做的是:把查询切词、把文档切词,看哪些词撞上了,再按命中词的重要程度和出现次数打分。它看见的是「字一样不一样」。向量检索做的是:把整段话压成一串数字,再算两串数字离得近不近。它看见的是「意思近不近」。这个底层差异,决定了它们各自的强项和翻车点。
| 对比项 | 关键词检索 | 向量检索 |
|---|---|---|
| 看什么 | 词有没有原样撞上 | 两段意思离得近不近 |
| 算分依据 | 命中词权重 + 词频 + 文档长度 | 向量之间的距离/相似度 |
| 专有名词、错误码、型号 | 命中准,几乎不会漏 | 容易被语义稀释,反而召错 |
| 同义改写、口语长问句 | 换个说法就搜不到 | 通常能兜住 |
| 典型翻车 | 用户没写文档里的词 | 召回话题相近但答案不对的段落 |
结论不是谁更先进,而是看你的资料里「用户提问用词」和「文档用词」重合度高不高。重合高,比如客服查错误码、运维查日志编号、开发查函数名,关键词就够用,还更稳更省。用户习惯换说法、跨语言提问、一上来就是一大段口语长问句,才需要向量来补位。
- 用户提问常带编号、型号、错误码、专有名词 → 优先关键词
- 用户提问多是完整句子、带口语、爱换同义词 → 向量能明显补位
- 资料以表格数字、代码片段、短编号为主 → 谨慎上向量,切块容易把语义打散
- 资料是大段说明文、教程、政策条款 → 向量更适合,关键词容易因用词不同而漏
- 两种用户都有 → 混合检索,先关键词保底再向量补召回
想验证重合度,不用猜,拿你真实的历史提问去跑一遍就行:把用户原话丢进关键词检索,看命中率;再看漏掉的那些,是不是都因为换了说法。这个漏检比例,基本就决定了你要不要加向量。
先看手里这批资料长什么样,再定方案
选 RAG 还是传统检索,先别急着比技术高低,把手里的资料摊开看一眼更快。同一批资料里往往混着好几种东西:订单号、日志 ID、指标名这种结构化字段,长文档说明类,还有合同、手册这类既有术语又有长句的。把它们分开放,方案基本就自己浮出来了。
| 资料长什么样 | 典型例子 | 优先走哪条路 |
|---|---|---|
| 结构化字段 | 订单号、日志 ID、指标名、枚举状态 | 关键词或数据库精确查 |
| 长文档说明 | 产品说明、制度解读、FAQ 正文 | 向量召回 |
| 术语加长句 | 合同条款、设备手册、规范文件 | 混合检索 |
提问方式也要一起看。用户在搜索框里敲的是几个关键词、或者从下拉里选一个条件,传统检索更省,快且结果可解释;用户直接甩一整句自然语言问「这个流程卡在哪一步」,向量或混合更稳,因为它能接住同义改写和口语说法。两种需求都存在时,别硬用一个方案通吃,按入口分流更实际。
- 抽一批真实查询从日志、客服记录、搜索框历史里捞几十条,覆盖上面几种资料类型和两种提问方式,不要自己编理想问题。
- 人工标答案位置逐条标出「正确答案在哪一段」,标到段落或字段级别,这是后面判断的唯一依据。
- 两种方式各跑一遍同一批查询分别用关键词检索和向量召回跑,看目标段落有没有排进前几条。
- 按类型下结论哪类资料、哪种提问下谁更稳,就分别记下来,不要合并成一个总分。
这套小规模对照不适用两种情况:一是查询量极小、几天才来一条,标注成本不划算;二是资料本身还在频繁改结构,跑出来的结论过两周就失效。遇到这两种,先把清洗和数据结构稳住,再回头做对照。真要查各家 API 的具体能力边界和当时的数字,到站内模型资料库和价格页核对。
下一步把标注表格建起来,几十条就够,跑完再决定用哪套,比拍脑袋选靠谱得多;需要对照各家方案时可去看站内评测与工具目录。→混合检索与重排:把两套结果并成一条路
单靠一路召回,总有一头漏。关键词那路擅长精确命中:专有名词、编号、代码片段、人名地名,只要字面对上就捞得回来;向量那路擅长语义补漏:用户换个说法、用同义词、描述一个概念,它也能找到意思相近的段落。工程上的常见做法是两路并行召回再合并,让两种能力互相兜底,而不是纠结选哪一路。
| 召回路径 | 擅长 | 容易翻车的地方 |
|---|---|---|
| 关键词(倒排 / BM25 类) | 精确字面命中、术语和编号 | 换个说法就找不到,只认字面 |
| 向量(语义相似) | 同义替换、模糊描述、跨表述 | 字面命中但语义偏的项也会被拉进来 |
| 合并后统一排序 | 两路候选放同一把尺子上比 | 两路分数尺度不同,直接相加会偏袒某一路 |
| 加一层重排 | 对候选做二次精细打分 | 慢且贵,候选太少时纯属浪费 |
- 第一步:两路并行召回,各取一批候选关键词和向量各跑一次,各自保留一批候选,先别急着截断成最终条数。目的是把两边的「可能有用」都留住。
- 第二步:合并去重,统一排序同一段被两路都召回的,去重后只留一份,并给它一点加权(两路都认可通常更靠谱)。两路分数尺度不一样,别直接相加,用排名融合这类只看位次的方式更稳。
- 第三步:对合并后的候选做重排用一个更慢但更准的模型,对「问题 + 候选段落」成对打分。这一步专门压掉「字面命中但答非所问」的项——它在关键词那路分数很高,但只要读完发现没回答问题上,重排就会把它往下压。
- 第四步:控制进上下文的条数排序完之后只取靠前的几条喂给模型。先从小批量试起,看回答质量有没有掉;塞太多段落会稀释重点、推高成本。具体几条得按你的文档颗粒度(一段是一句话还是一整节)和模型上下文预算来定。
- 候选取多少:先小批量验证再加,别一上来就放大,观测指标再决定
- 重排用不用:召回候选本来就只有个位数、且人工已确认准确,再加一层重排属于白花钱
- 进上下文几条:按文档颗粒度和模型上下文预算定,可以先到 AI 模型库(/models) 核对候选模型的上下文规格
- 验证方式:固定一批问题,对比「加重排」和「不加重排」的回答质量,收益看指标说话
- 什么情况下不适用:候选量很小、延迟敏感、文档颗粒度极粗(一条就是整篇)时,重排和精细排序的收益都会明显下降
接进生产前,超时、并发、降级、日志、评测各留什么口子
把 RAG 接进生产,最怕的不是效果差一点,而是整条链路一慢全慢、一挂全挂。这一节不讲原理,只讲你在上线前应该在代码里留出的几个口子:超时、并发、降级、日志、评测。每一个口子都对应一个具体的排查动作,别等出事才补。
超时:检索和生成分开设,别用一个总超时包住
一个常见错误是给整个请求设一个总超时,结果检索卡住时你既拿不到关键词结果,也拿不到模型回答,用户就是干等。正确做法是两段各设各的超时:检索段一个上限,生成段一个上限。检索超时后先返回关键词结果或明确提示「没找到相关资料」,比整体超时体验好得多。基线延迟别凭感觉,先去 接口测速(/tools/speed-test) 把向量库和模型接口各自量一遍,再定阈值。
- 检索段超时:超时后走关键词兜底,或返回「未找到」并让模型裸答,不要静默卡住
- 生成段超时:超时后返回已检索到的片段摘要,至少让用户看到有资料
- 两段超时不要共用同一个计时器,检索超时不应该连带砍掉生成预算
- 超时阈值按 接口测速(/tools/speed-test) 的实测分位数来定,不要抄别人的数字
并发与降级:设上限、排顺序,别让链路一起挂
向量库和模型接口都要设并发上限,超了就让请求排队或做退避重试,避免把下游打爆。降级要有明确顺序,不能是「全挂」。建议的顺序是:混合检索 → 纯关键词 → 直接让模型裸答并标注「未引用资料」。每一步都要能单独触发,而不是等上一层完全失败才走下一层。
| 层级 | 触发条件 | 用户看到什么 |
|---|---|---|
| 混合检索 | 正常状态 | 带引用的答案 |
| 纯关键词 | 向量召回超时或失败 | 关键词命中的片段,可能没有重排 |
| 模型裸答 | 检索整体不可用 | 答案 + 明确标注「未引用站内资料」 |
这个表的价值在于:出问题时你能一眼看出用户现在处于哪一层,而不是只看到「接口报错」。注意,如果业务本身对引用准确性要求极高,裸答这一层可能不适用,宁可返回「暂时查不到」也别给无依据的答案。
日志:每次请求至少记这几项,不然没法复盘
很多团队上线后只记一个耗时和状态码,出了问题根本不知道是哪一段拉胯。每次请求至少记:查询原文、走了哪条召回路径、命中了几段、用的哪个索引版本、重排后的排序。有了这几项,你才能回答「是检索没召回,还是召回后排序排坏了,还是模型没用好资料」。
- 查询原文
- 用户到底问了什么,别只存 hash 或 id
- 召回路径
- 这次走的是混合、纯关键词还是兜底,决定后面怎么看
- 命中段数
- 召回了几段、进重排的有几段,判断是召回不足还是排序问题
- 索引版本
- 切块或嵌入模型换过之后,对不上版本就没法归因
- 重排后排序
- 前几段是什么,直接决定模型看到什么
评测:攒小样本,每次改动都跑一遍
只靠一两次人工体感调 RAG,非常容易改好一类问题、改坏另一类问题。做法是攒一批带标准答案的小样本,覆盖你真正关心的几类问题。之后每次改切块、换嵌入模型、调排序,都跑一遍同一批样本,对比命中率和答案正确率。
- 攒样本挑 20~50 条真实问题,人工写好标准答案和应该命中的资料片段,不用多,但要覆盖各类问法
- 定指标至少看两个:召回命中率(该出现的片段有没有出现)和答案正确率(生成有没有用对资料)
- 固定跑法每次只改一个变量,跑同一批样本,把两次结果并排看,别凭印象说「感觉变好了」
- 看回归重点盯那些上次对、这次错的样本,它们才是改坏的地方
这套口子不能保证效果一定好,它保证的是:出问题时你知道去哪看、改完之后你知道有没有改坏。如果团队只有一两个人维护,可以先从日志和一份小评测集做起,超时和降级可以后补,但别一直不补。
什么情况下你其实不需要上 RAG
判断要不要上 RAG,第一刀不是砍在模型上,而是砍在「资料有多少」。如果你手上的知识总量就是几十页说明文档、几百个字段的配置表,那它压根撑不起一套检索系统。先把这批资料整个塞进上下文,让模型直接读,比搭一套切分、向量化、召回的链路快得多,也省掉一堆调参的麻烦。
- 统计一下原始资料的总字数和字段数,别估,实际数一遍
- 拿这个总量去对比你打算用的模型上下文窗口,看是全塞得下还是塞不下
- 塞得下就先做单轮直读的版本,跑一批真实问题看答案质量
- 只有发现明显塞不下、或者每次请求都逼近上限时,才回头考虑检索
这里有个常见误区:很多人对「上下文」到底指什么、怎么算、超了会怎样,其实没算清账就开始选型。建议先去 基础概念词典:Token、上下文、温度 把上下文和 Token 的关系搞明白,能算出你这批资料大概占多少空间,再谈要不要检索。算完账往往会发现,小体量资料上 RAG 纯属多余的一层。
这几种需求,检索本身就不是正解
除了资料量小,还有一类场景是「问题形态」决定了不该用检索。如果你要的是固定格式的字段提取,或者需要精确的数值计算,结构化查询和工具调用比向量召回直接得多。检索擅长的是找相关段落,不擅长保证某个字段一定被准确取出。
| 需求类型 | 更合适的做法 | 为什么不首选检索 |
|---|---|---|
| 字段提取(如从表单抽固定几项) | 结构化解析或工具调用 | 检索只保证找到段落,不保证字段精确 |
| 数值计算(求和、比对、换算) | 代码或计算工具 | 模型做算术容易出错,检索不解决算准问题 |
| 精确 ID / 编号直达 | 数据库索引或关键词精确匹配 | 向量是近似的,精确命中反而更快更省 |
| 开放问答、资料分散 | 检索再交给模型组织语言 | 这时候检索才有存在价值 |
判断标准可以简化成一句:如果查询本身就是一串明确 ID 或编号,你要的是「精确等于」,那就别硬套向量。传统全文检索甚至数据库索引,在这种场景下响应更快、成本更低、结果更确定。硬上向量,等于给自己多加一层不确定性:明明该命中,可能因为相似度排序没排第一而漏掉。
定位不了答案在哪,先别急着选方法
- 问自己:答案大概在哪份资料里如果连大致范围都说不出,说明资料没被整理过,不是检索方法的问题
- 先把资料做基本归类按主题、部门或用途分文件夹,标注每份大概讲什么,这一步不需要任何技术
- 再判断资料量是否适合直读整理后往往总量变小,可能直读就够了,检索自然不必上
- 最后才决定要不要检索顺序反了,资料没治理好,上 RAG 还是传统检索,两种方法都救不了
先搞清楚答案在哪,再谈用什么去把它找出来;跳过第一步,后面选什么方法都是碰运气。
所以「不需要 RAG」不是偷懒,而是一种正确的取舍。资料小、需求结构化、查询精确、数据没治理,这四种情况下,先别上检索,把上下文算清、把资料理清、把工具用对,往往更省事也更准。真正该上检索的时候,是因为问题模糊、答案分散,而不是因为「大家都这么做」。
常见问题
是不是有了向量检索就可以完全不要关键词检索了?
不行。专有名词、错误码、型号、编号这类字面匹配是向量最容易翻车的地方,用户问某个具体编号时,关键词几乎是必留的一路。稳妥做法是两路都留着做混合召回,谁擅长谁上,而不是二选一。
我改了切块大小或换了嵌入模型,怎么知道是变好还是变坏?
别靠单次观感。先固定一批真实查询并标好正确答案位置,改动前后各跑一遍,比命中位置和答案正确率,同时观察延迟和成本有没有明显变差。没有这组样本,任何改动都只是赌。
检索没召回到正确段落,一般先查哪一环?
按顺序查:原始文档清洗有没有把关键内容切掉,切块有没有把一段完整意思劈开,查询有没有被改写跑偏,召回路数够不够(是否只走了单路),最后才怀疑重排排序。多数问题出在前两步。