日期:2026-08-22
我们的企业知识库问答上线后,业务方偶尔反馈"这回答偏了""说的不是我要的",但让他指具体哪错了,他又说不清,只能扔一句"你再看看"。我们这边也没法系统回应,因为从来没建过检索质量的评测,全靠上线时手感调一版,之后三个月没动过召回策略。这种状态很危险:生成层在背锅,真正可能出问题的检索层却没人盯。
RAG 这类系统,用户看到的是生成答案,但答案准不准,七成取决于前面检索召回了什么。检索差,再聪明的提示词也救不回来。我们意识到,必须把检索质量单独拎出来评,而不是混在整体效果里模糊归因。
我们建了一套离线评测闭环。先由业务和算法共建一小批评测集,每条是一个真实问法加它应该召回的标准文档片段。每周离线跑一遍,算出检索命中率、前 K 准确率这类指标,和历史基线对比,掉点就告警。
更关键的是 badcase 处理。评测跑完自动把失败的 query 聚类,按失败模式归到具体环节:是切片把一段语义切散了,是 embedding 没区分开近义问法,还是重排把对的压下去了。归因结果直接进迭代看板,算法知道这周该调切片还是调重排,而不是瞎试。改完再跑评测验证,形成闭环。
评测集代表性是个坑。我们第一版评测集是算法自己编的问法,跑出来指标漂亮,一上业务真实 query 就露馅,因为编的问法太"标准",真实用户问得乱七八糟。后来改成从线上日志抽样真实问法,人工标注标准答案,评测才贴近现实。
命中率口径也容易扯皮。前 1 命中、前 5 命中、带重排后的命中,数字能差出一倍。我们和业务的共识是看"最终进入生成上下文的片段是否包含答案源",口径统一后吵架少了。
badcase 聚类是提效核心。早期我们一条条翻失败 query,一天看十几个就头大。聚成几类后,一类问题一次改一处,效率差出一个量级。
指标看板我们做成周更闭环,不只是跑一次出个分数。每周自动对比基线和当周结果,掉点标红并附 badcase 聚类摘要,算法周一打开就能知道这周该动哪。我们也留了业务补样例的入口,业务方说偏了时先查评测集有没有同类,没有就补一条进标准答案,这样既收集了真实分布,也避免重复吐槽。这套机制跑半年,评测集从几十条约扩到上千条,覆盖度上来后,线上随机问法的答偏率明显下了一个台阶。
我们也把检索质量指标接进了发布卡点。任何涉及切片、embedding、重排的改动,必须先跑评测不降点才允许上线,从源头拦住劣化。之前有次重排参数误调,要不是卡点拦着,就带着掉点的召回上了线。把评测从事后复盘变成事前门禁,是这套机制真正发挥威力的转折点。
业务侧的教育也得做。一开始业务方把评测集当我们的内部工具,出了偏就等我们修。我们开放了补样例入口后,慢慢培养他们把真实问法喂进来,评测集才越来越像生产分布。现在业务方自己会看周报决定要不要加场景,从被动投诉变成共建。这套机制能不能活,关键在于业务愿不愿意持续投样例,我们花了不少口舌才扭转这个习惯。
效果上,我们把检索质量也接进了业务周会。不用我们汇报,业务自己看周报里的命中率和 badcase 摘要,决定下周投哪类样例。这套机制把算法和业务拉到同一张表里说话,扯皮少,迭代快,是我们觉得最值钱的协同变化。
案例片段(已脱敏): 某周评测命中率从 0.91 掉到 0.83。badcase 聚类显示 73% 的失败集中在"近义产品名"类问法,如用户问"云盘"实指"对象存储"。下钻发现 embedding 对该近义对区分度低。算法补充 40 条同义问法做对比学习微调,重排层加产品别名词典。两周后该类命中率回到 0.90,整体恢复 0.92。若无聚类,这类问题会被淹没在零散 badcase 里,至少多耗一个月。
检索命中率(进入生成上下文含答案源的比例)从建评测前的约 0.84 提升到稳定约 0.92。badcase 平均收敛周期从原来靠手感的一月以上,缩短到约两周一个迭代。准确率(答案被业务判为可接受)同步提升约 8 个点。
最实在的收益是沟通成本。现在业务方说"偏了",我们能立刻查评测集里有没有同类,有就进迭代,没有就补样例,不再是互相甩感觉。
RAG 别只盯着生成,检索差是多数答偏的根因,而检索质量必须离线评,不能靠上线手感。评测集一定要用真实日志里的问法,自己编的太标准,上线必翻车,这是我们第一版犯过的错。badcase 要聚类归因到切片、embedding 还是重排,一条条翻是在浪费命。指标口径得和业务提前对齐,否则前 K 命中差一倍,吵都没法吵。把检索评测做成周更闭环,召回策略才能持续往前走,而不是上线即终点。