我们先看一组常见的对话。用户问「出差住宿标准是多少」,系统回答了一段关于「差旅审批流程」的内容,语气笃定、格式规范,但没有回答数字。这不是幻觉,而是检索把相邻章节取了出来——而相邻章节恰好语义相似。
在企业知识库里,这类问题占了我们处理过的失败样本的一半以上。它们的共同点是:模型没错,检索错了。
五个结构性缺陷
- 切片切断语义:按固定 500 字切分,把一张制度表格切成两半,金额和适用条件分属不同片段。
- 口径缺失:文档里写「按上年标准执行」,但「上年标准」在另一份文件里,检索层没有建立引用关系。
- 权限未继承:所有内容进同一个索引,导致低权限用户能检索到高密级片段,只能靠后置过滤补救。
- 更新滞后:制度修订后旧版本仍在库里,两个版本的条款同时被召回,模型各取一半。
- 问题类型单一:只优化了「事实型问答」,但真实提问里大量是「比较」「计算」「流程下一步」,需要不同的召回策略。
切片:按结构切,而不是按字数切
我们的默认策略是先解析文档结构(标题层级、表格、条款编号),再按语义边界切片。条款类内容以「条」为单位,表格整体保留并在片段头部附加表头;超过长度上限的片段,用摘要 + 原文双段存储,检索命中摘要时仍返回完整原文。
这一步通常能把 Top-5 召回率提升 8 到 12 个百分点,且不需要更换任何模型。
召回:向量之外必须有字面匹配
纯向量检索对型号、编号、条款号极不敏感。我们的做法是双路召回:向量检索负责语义相近,关键词检索(BM25 类)负责精确命中,再用轻量重排模型融合排序。融合权重按行业调整——制造业偏字面,客服场景偏语义。
拒答:比答对更难设计
很多团队把「拒答」当成兜底,实际上它是一个需要主动设计的策略。我们在三处设置条件:检索最高分低于阈值、召回片段之间互相矛盾、问题超出知识库覆盖范围。任意一条命中,就返回「暂未在制度中找到依据,建议咨询 XX 部门」,并附上最接近的两条参考。
在金融客户的项目里,拒答率被列为一级指标。拒答过多会降低可用性,过少则意味着错误回答被包装成了确定结论。我们通常把「应拒答样本的拒答率」目标设在 95% 以上,同时把误拒率控制在 8% 以内。
评测集怎么建
评测集不是让模型生成的,而是从真实提问里抽样。我们的流程是:从客服工单、内部群聊与搜索日志中各取 200 条真实问题,由业务专家标注「标准答案所在片段」,再补充 60 条负样本(知识库确实没有答案的问题)。
| 指标 | 含义 | 用途 |
|---|---|---|
| Recall@5 | 正确片段是否进入前五 | 判断检索层是否需要调整 |
| MRR | 正确片段的平均排名 | 观察重排模型效果 |
| 引用准确率 | 回答中的引用是否真的支持结论 | 识别「引用了但没用对」 |
| 拒答恰当率 | 该拒答时是否拒答 | 控制合规风险 |
成本与新鲜度
知识库的运维成本主要在两块:增量更新与全量重建。我们建议把文档按更新频率分层——制度类每月增量,产品手册每周增量,工单类每日增量;全量重建按季度执行,避免频繁重算向量带来的费用尖峰。同时为每个片段保留版本号与生效时间,检索时按当前时间过滤失效版本。
最后一句经验:如果评测集里超过三成的失败样本,人工看一眼原文就能回答,那问题一定在检索层,不在模型。先修数据,再谈模型选型。
本文数据来自天辰平台知识库交付项目,已做脱敏与归一化处理。