🧠 第六章 语言模型训练:高效赋能业务场景
📄 一句话概括

往 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 秒实验

  1. 把人事文档从 8 段扩到 32 段(每段复制四遍并改几个字),再看名额占用。 占用率会随无关文档比例线性涨。
  2. 把 k 从 3 改成 1,看混库后 Top-1 命中会不会掉。 名额越少,被占一次就掉一条命中——k 小的时候混库更危险。
  3. 把「加班与调休」里那句「调休需在三个月内使用」删掉,重跑。 那一句是主要污染源,删了之后名额占用会明显下降。

常见误区

  1. 「所有文档放一个库里省事」。小库看不出问题,库一大,名额被占就变成检索全废。
  2. 「混库会立刻让准确率下降」。Demo 里 Top-3 没掉——问题在于名额被悄悄吃掉,不是立刻出错。
  3. 「让模型自己决定查哪个库」。它会挑看起来最像的,包括那些不该给这个用户看的库。按入口路由。
自测三问(点开对答案) 1. 建库时要定哪三件事? —— 嵌入模型、切块参数、文档边界(哪些文档放进这个库)。第三条工具不提醒,但影响最大。 2. 混库为什么在小库上看不出问题? —— 因为无关文档占比还不高,名额偶尔被占一位,Top-3 仍有位置。 库一大,名额被占的概率按超几何分布快速上升。 3. 按什么维度分库最实用? —— 按主题(词面重叠少)、按更新频率(重建粒度不同)、按权限(天然形成访问边界)。

库建好了,接下来把文件真的传进去。这一节讲两件事:向量化的吞吐,以及向量到底占多少地方。

相关笔记