# 知识库里明明写了，AI 为什么答不上来：一次检索复现定方向

> 八成以上「知识库没生效」的排查结果，其实是内容没被检索到，而不是内容缺失。本文拆解混合召回的五环链路（范围、召回、扩展、重排、拼装），给出内容问题与排名问题的判定表，以及在动手补内容之前该做的免费复现。

## 一个高频困惑：「知识库里明明写了，AI 就是答不上来」

这是接入知识库之后第一个月最容易遇到的问题。团队把产品手册、FAQ、案例、资质证书全部导进去，问一句「你们支持哪些部署方式」，AI 答得含含糊糊，或者干脆说“资料中没有相关信息”。

多数人的第一反应是「知识库没生效」或「模型太笨」。但实际排查下来，**八成以上的情况不是内容缺失，而是内容没被检索到**。

这两件事的处置完全不同：

- 内容缺失 → 去补内容；
- 检索没召回 → 内容写着也没用，改的是检索条件或内容组织方式。

如果用错方向，就会陷入「按 AI 提示的缺口反复补文章，补了还是答不上来」的死循环。这篇文章就是讲怎么在动手补内容之前，先用一次检索复现把方向定准。

## 一、先理解检索是怎么走的

我们的知识库检索不是单纯的向量相似度。完整链路大致是这样：

**① 范围确定。** 先确定这次检索在哪些知识库里找。这一步会按当前租户、项目、可见性做筛选——如果某些知识库被排除在范围外，再怎么调召回也找不到它。

**② 混合召回。** 三条通道并行：

| 通道 | 权重 | 说明 |
|---|---|---|
| 向量召回 | 0.7 | 语义相似，能匹配“意思相近但用词不同”的问法 |
| 关键词召回 | 0.3 | 字面命中，对产品型号、专有名词、编号类查询很重要 |
| 标题/章节召回 | 0.15 | 命中标题或章节名，对结构化资料的定位很有效 |

三条通道的结果用 **RRF（Reciprocal Rank Fusion，k=60）** 融合。RRF 的意义在于：它不看原始分数的绝对值，只看排名——这样不同通道的分数尺度不一致也不会互相干扰。

另外有一个**元数据加成（0.08）**，按知识库身份给一个静态质量分。它不参与 RRF，因为它对同一个库的每个切片都相同，排序它等于按库名排序，没有信息量。

**③ 查询扩展（按需触发）。** 如果基础召回的数量低于门槛（默认是候选限额的 50%），系统会自动生成查询变体再捞一轮，去重后合并。这是“召回不足时的补救”，不是每轮都跑。

**④ 重排。** 召回结果会经过重排（rerank）再取前若干条拼成证据上下文。

**⑤ 拼装上下文。** 最终返回的是证据条目（默认取前 5 条，上下文长度上限 3200 字符），连同来源信息一起交给生成模型。

看明白这条链路，排查就有顺序了：范围 → 召回 → 扩展 → 重排 → 拼装，每一环都可能把内容滤掉。

## 二、诊断对象：为什么“没检索到”是可观测的

平台为每次检索记录了一份诊断信息，字段刻意保持少量但语义明确：

- **范围规模**：请求了几个库、实际参与检索几个、有没有被安全阀截断；
- **候选规模**：向量通道和关键词通道各召回了多少切片；
- **各阶段滤除计数**：被时效性排除多少、被范围筛选排除多少；
- **查询扩展**：是否触发、触发门槛是多少、用了哪些变体、额外捞到多少；
- **最终返回**：几条、耗时多少、有没有告警。

这份诊断的价值在于：它能回答「到底哪一步把内容滤掉了」。

举一个实际会遇到的形态：诊断显示向量候选 40 条、关键词候选 12 条，最终返回 5 条，但答案里没有你需要的那条。这说明**召回没问题，问题在排名**——你的目标切片进了候选池，只是没排进前 5。这种情况下补内容没用，要改的是这条内容的表述方式或检索参数。

反过来，如果诊断显示范围里只剩 1 个库、向量候选只有 3 条，那问题在**范围或召回**——要么库被过滤了，要么切片太少。

## 三、两类失败：内容问题与排名问题

这是本文最重要的一张对照表：

| 现象 | 判据 | 正解 |
|---|---|---|
| 内容确实没有 | 检索复现时**完全没进候选**，且按关键词搜库内也搜不到 | 补内容 |
| 内容有但排不进前 K | 进了候选池，但最终取证时被挤掉 | 改表述/补专题篇章，不是补一行 |
| 内容有但召回不到 | 按字面关键词能在库里搜到，语义检索却召不回 | 内容与问法词汇差异过大，补同义表述 |
| 召回正常但答不好 | 证据进了上下文，模型仍答错 | 检索/生成口径问题，不是知识库问题 |

**特别提醒第三行和第四行**：按“缺口”补一行表项，往往召回还是打不过——因为一行表项的语义信号太弱。要补就补**专题篇章**：标题和段落里覆盖用户真实会用的问法词，这样语义召回的信号才够强。

## 四、一次免费的最小复现

排查不该拿付费对话来试。检索复现本身只花 embedding 的钱，成本极低。

具体做法：

1. 把用户那个问句原样（不要改写）拿去跑一次 `retrieveEvidence` —— 取前 8 条证据；
2. 逐条看你期望的那段内容在不在里面；
3. 按上面表格定位是哪一类失败；
4. 再决定是补内容、改表述、还是调参数。

关键纪律：**用用户的原话，不要用你整理过的规范问法**。用规范问法测出来能召回到，不代表用户那样问也能召回到——而线上是用户在问。

## 五、几个容易被忽略的坑

**「改了默认值」不等于「生效了」。** 检索和生成的相关配置有优先级：**数据库里保存的值优先于代码里的默认值**。只改代码默认值，线上跑的还是 DB 里那份旧配置。这类“看起来改了但没变”的问题，靠界面是看不出来的，必须核对实际生效值。

**分块没同步完，检索结果就是 0。** 内容入库是异步链：准备 → 向量化 → 完成。这期间状态是 pending 或 processing，切片数是 0。这时候去检索当然什么都没有——**看起来完全像“入库失败”**，实际是在正常处理中。判据是看切片同步状态是否 ready，不是看入库脚本有没有返回成功。

**时效性会静默排除内容。** 过了时效的资料会被排除在检索范围外。如果某个库里的内容都属于“会过期的事实”，而你没维护复审时间，它可能整体失效而不报错。

**“检索到”和“答对了”是两件事。** 证据进了上下文，模型仍可能组织错。这时候要看生成侧的提示词与上下文拼装，不要在知识库上继续加内容。

## 六、这套做法的适用边界

需要说清楚的几点：

- 检索诊断是**排查工具，不是评分**。它不给你“知识库质量 78 分”这种数字，因为质量问题不是单一维度能概括的。
- 混合召回的权重与 RRF 参数是**默认值，可调**。不同知识库类型（结构化手册 vs 叙事型案例）最优参数不一样，调参前先做上面那套复现，否则是盲调。
- 补内容永远是最后一招。先确认前面五环都没问题，再动内容——否则你会补一堆用不上的东西，还破坏了原本的组织结构。

