往 12 段客服库里混进 8 段人事文档:Top-1 和 Top-3 都没掉(9/10、10/10), 但 Top-3 的 30 个名额被无关文档占走 2 位。 按无关占比外推: 1000 段里 96% 是无关文档时,k=3 全部落空概率 88.5%。 这才是分库的理由。
上一节把嵌入模型配好了。这一节建库,而建库最要紧的决定不是参数,是「哪些文档该放在一起」。
一、建库时定下的三件事
① 嵌入模型。 定下来之后,换就要重建,所以第一步想清楚。
② 切块参数。 段多大、重叠多少。中文建议 300 到 500 字、重叠 10% 到 15%。
③ 文档边界。 哪些文档进这个库——这一条最少被认真对待,但它对效果的影响最大。
前两条在界面上是输入框,第三条在界面上根本不存在——它是你自己要拿主意的事,工具不会提醒你。
📋 实操清单:
Cherry Studio 里新建知识库 → 填名字 → 选嵌入模型 → 设定分块大小 → 保存 → 上传文件。
名字里带上主题和版本(客服FAQ-v3),半年后你会感谢自己。
二、混库的真实代价
直觉是「混库会检索混乱」,Demo 的结果比直觉微妙:
| 配置 | 库大小 | Top-1 | Top-3 | Top-3 被占 |
|---|---|---|---|---|
| 只查客服库 | 12 段 | 9/10 | 10/10 | 0 位 |
| 混进 8 段人事 | 20 段 | 9/10 | 10/10 | 2 位 |
| 召回居然没掉。 因为两个主题的词几乎没有交集, | ||||
| 客服问题不会因为多了人事文档就找错——这是实话,不能夸大。 | ||||
| 但名额被吃了:10 个问题 × 3 个名额 = 30 位,被占掉 2 位。占位的是「加班与调休」那一段, | ||||
| 原因很土——「东西用过了还能退吗」里的「使用」、「卡里的分多久清零」里的「过期」, | ||||
| 它里面都有。 | ||||
| 两个字就够把一段无关文档拉进前三。字面向量就是这么蠢,而真正的嵌入模型也只是「稍微聪明一点」, | ||||
| 不是不会犯这种错。 |
三、库大了以后会发生什么
现在没事,不代表以后没事。按「无关文档占比」算,k=3 全部落在无关文档上的概率是这样的:
| 无关占比 | 1000 段里无关 | k=3 全是废的概率 |
|---|---|---|
| 10% | 100 段 | 0.1% |
| 40% | 400 段 | 6.4% |
| 80% | 800 段 | 51.2% |
| 96% | 960 段 | 88.5% |
| 这个算法是超几何分布:三个名额全从无关堆里抽的概率。无关文档占八成时,一半的检索全是废的。 | ||
| 而且这还是「随机抽」的下限——真实检索有字面偏好,实际被污染的比例只会更高。 | ||
| 所以分库不是洁癖,是工程: | ||
| 一个库一个主题,调用时按入口路由(客服页面挂客服库),不让模型自己猜。 |
四、怎么分:三个维度
按主题分是最常见的:产品、售后、人事、财务,各一个库。主题之间词面重叠越少,分开的收益越大。 按更新频率分更实用:制度一年改一次,产品手册一周改一次。混在一起,每次改手册都要整体重建索引; 分开就只重建那一个。 按权限分是最容易被忽略的:人事制度不该被客服机器人的用户检索到。 分库天然就是一道权限边界,混库就得靠额外的过滤条件,很容易漏。 代价是「要先判断这个问题查哪个库」。简单做法是按入口分;复杂一点可以让一个分类器先判意图。 但别指望「让模型自己决定查哪个库」——它会挑一个看起来最像的,包括那个不该给用户看的。
五、动手 Demo:混库的真实代价:名额被占,库一大就全是废的
12 段客服文档做底库,另造 8 段人事文档。 分别算「只查客服库」和「混进人事库」两种情况下的 Top-1/Top-3 命中、Top-3 里被无关文档占走的名额, 以及平均最高相似度。再按无关文档占比外推「k=3 全部落空」的概率。
for tag, extra in ((u'只查客服库', []), (u'混进 8 段人事', hr)):
bank = kb + extra
for q, gi, _ in P.QS:
M, v = P.encode(bank + [P.norm(q)])
sim = P.cos(M[:len(bank)], M[len(bank)])
top3 = list(np.argsort(-sim)[:3])
poll += sum(1 for i in top3 if i >= len(kb))
# 外推:k=3 全落在无关文档里的概率(超几何)
p = 1.0
for i in range(3):
p *= (n_tot - n_rel - i) / float(n_tot - i)
实跑输出:
--------------------------------------------------------------------
混库的真实代价:名额被占,库一大就全是废的
--------------------------------------------------------------------
配置 库大小 Top-1 Top-3 Top-3 被占 最高相似度
----------------------------------------------------------------
只查客服库 12 段 9/10 10/10 0 位 0.206
混进 8 段人事 20 段 9/10 10/10 2 位 0.207
看前两列:**混进 8 段人事之后,Top-1 和 Top-3 都没掉。**
这是实话——两个主题的词几乎没有交集,检索没那么容易乱。
但后两列有问题了:**Top-3 的 30 个名额里被占掉 2 位**。
平均最高相似度几乎没动(0.206 对 0.207)。
这说明混库**不会稀释分数,它只是占名额**——分数该多高还是多高,只是名额少了两个。
占走名额的都是**「加班与调休」**这一段。原因很土:
「东西用过了还能退吗」里的**「使用」**、「卡里的分多久清零」里的**「过期」**,它里面都有。
**两个词就够把一段无关文档拉进前三。**字面向量就是这么蠢。
真正要担心的是库变大以后。按无关文档占比算,**k=3 全部落在无关文档上的概率**是这样的:
无关文档占比 1000 段里有多少无关 k=3 全是废的概率
----------------------------------------------------
10% 100 段 0.1%
40% 400 段 6.4%
80% 800 段 51.2%
96% 960 段 88.5%
库一大,**「名额被占」就变成「检索全废」**。这才是分库的真正理由——不是现在会错,是以后必错。
所以分库不是洁癖,是工程:**一个库一个主题,调用时按入口路由。**
第一张表最反直觉的是前两列:混进 8 段人事,Top-1 和 Top-3 都没掉。我原本以为会掉, 实测没掉——那就照实写。 掉的是名额:30 个名额里被占 2 位。而且最高相似度几乎没变(0.206 对 0.207), 说明混库不稀释分数,只占名额。 占位的都是「加班与调休」。它凭的是「使用」和「过期」这两个词——跟客服问题撞上了。 两个字就够。 第二张表是外推:无关文档占 80% 时,k=3 全废的概率 51.2%;占 96% 时 88.5%。这就是「现在没事、 以后必错」的定量版本。 要说明:这 8 段人事文档是我写的,相似度也是字二元组算的。 真嵌入模型下这些数字会变,但「名额固定、无关文档占位」这个结构不变。看的是结构,不是这几个数。
30 秒实验
- 把人事文档从 8 段扩到 32 段(每段复制四遍并改几个字),再看名额占用。 占用率会随无关文档比例线性涨。
- 把 k 从 3 改成 1,看混库后 Top-1 命中会不会掉。 名额越少,被占一次就掉一条命中——k 小的时候混库更危险。
- 把「加班与调休」里那句「调休需在三个月内使用」删掉,重跑。 那一句是主要污染源,删了之后名额占用会明显下降。
常见误区
- 「所有文档放一个库里省事」。小库看不出问题,库一大,名额被占就变成检索全废。
- 「混库会立刻让准确率下降」。Demo 里 Top-3 没掉——问题在于名额被悄悄吃掉,不是立刻出错。
- 「让模型自己决定查哪个库」。它会挑看起来最像的,包括那些不该给这个用户看的库。按入口路由。
自测三问(点开对答案)
1. 建库时要定哪三件事? —— 嵌入模型、切块参数、文档边界(哪些文档放进这个库)。第三条工具不提醒,但影响最大。 2. 混库为什么在小库上看不出问题? —— 因为无关文档占比还不高,名额偶尔被占一位,Top-3 仍有位置。 库一大,名额被占的概率按超几何分布快速上升。 3. 按什么维度分库最实用? —— 按主题(词面重叠少)、按更新频率(重建粒度不同)、按权限(天然形成访问边界)。库建好了,接下来把文件真的传进去。这一节讲两件事:向量化的吞吐,以及向量到底占多少地方。
- ☐ (本节完,下一节 6.2.3 添加文件并进行向量化处理)