🧠 第三章 数据获取:构建AI模型的知识库
📄 一句话概括

979 字的制度文档切成 10 块,不重叠时 10 条关键句有 1 条被切断; 重叠调到 10% 就全部补齐,代价是总字符从 1.00 倍涨到 1.10 倍。 块 800 时检索 3 条要吃 2400 字——占掉 8192 上下文的近三成。

📝 本节和前面几节的关系

上一节装好了,这一节配。配置项看着多,其实只有四项,其中三项填上就完事,只有切块需要试。

一、配置只有四件事

接哪个模型:Ollama 地址加模型名。填错就是「界面能开、答不出来」。 文档怎么切:块大小(chunk size)和重叠(overlap)。只有这一项需要试。 检索几条:top-k 和相似度阈值。 提示词模板:把检索到的几段拼成什么样的话交给模型。 前三项直接影响召不召得回、召回来的是不是对的;第四项影响模型用不用这些材料。 顺序上先配前三项,最后再调提示词。 还有一个藏在高级设置里的:嵌入模型选哪个。换嵌入模型等于换一把尺子, 之前存的向量全部作废——这个参数一旦定了,就尽量别改。

二、切块是唯一需要试的参数

块大小的目标很朴素:一个块刚好装下一条完整的规定。 太小,一句话被切成两半。 「国际差旅须提前十个工作日」和「提交申请」分在两个块里,检索到前半截,答案就不完整。 太大,一块里混着交通费、住宿费、餐补三条不同的规定。 检索时字面重合度被稀释,捞回来的可能是相邻的那条,还白占上下文预算。 所以这个数不是查出来的,是拿你自己的文档试出来的。 Demo 那份制度文档,每章大约 100 字,所以 200 到 400 是甜区。换一份文档,甜区就换一个位置。

三、成本与效果的交换

Demo 扫了四档大小乘三档重叠,一共十二组,三组数分别看成本、效果、代价。 成本:重叠从 0 调到 25%,总字符从 1.00 倍涨到 1.31 倍。 多出来的三成不是浪费,是为了「不让一句话被切成两半」付的保险费。 效果:块 100、不重叠时,10 条关键句有 1 条被切断。 把重叠调到 10%,这 1 条就补齐了——这就是那笔保险费买到的东西。 代价:最后一列是「检索 3 条要占多少字」。 块 800 时 3 条就是 2400 字,占掉 8192 上下文的近三成。还没算对话历史和系统提示词。 三组数放一起,就得到一句可操作的结论: 先按「一条完整规定」定块大小,再按「关键句有没有被切断」加一点重叠。 不要一上来就把重叠设成 50%。

四、top-k 和阈值:宁可不答,别乱答

top-k 是「每次给模型几条」。默认常见是 3,5 更稳,10 开始引入噪声。 阈值是「相似度低于多少就不要」。 这一项比 top-k 更重要,因为它决定了「什么都不像的时候,是硬答还是说不知道」。 知识库类应用最怕的不是答不出,是答错还很有底气。阈值调高一点,宁可回「资料里没写」, 也不要拿一段不相关的内容硬凑。 还有一条预算账要算:k 乘块大小不能超过模型的上下文窗口。k=5、块 800,就是 4000 字, 加上对话历史很容易顶到 8192。 调参顺序建议: 先定块大小(按文档结构)→ 再定 k(按上下文预算)→ 最后调阈值(按误答率)。 反过来调, 每改一项都要重试前面两项。

五、动手 Demo:重叠 0 时切断 1 条关键句,加到 10% 补齐,代价是 1.10 倍冗余

拿一份 979 字的差旅报销制度(10 章),按四档块大小乘三档重叠切块,对每组统计三件事:块数、 总字符与原文的比值(冗余倍数)、10 条关键规定里有几条完整落在同一个块里。

def cut(size, ov):
    step = max(1, size - ov)
    out, i = [], 0
    while i < len(DOC):
        out.append(DOC[i:i + size])
        if i + size >= len(DOC):
            break
        i += step
    return out

cs = cut(size, ov)
cover = sum(len(c) for c in cs) / float(len(DOC))
kept = sum(1 for k in KEY_SENTS if any(k in c for c in cs))

实跑输出:

--------------------------------------------------------------------
配置 AnythingLLM:切块参数是成本与效果的交换
--------------------------------------------------------------------
  文档长度                 979 字,10 章
  关键句                   10 条(每条规定的完整表述)

  两个参数:块大小(chunk size)和重叠(overlap)。扫四档大小、三档重叠:
  块大小   重叠   块数   总字符/原文   关键句完整   k=3 预算
  ----------------------------------------------------------
     100      0     10        1.00 倍        9/10     300 字
     100     10     11        1.10 倍       10/10     300 字
     100     25     13        1.31 倍       10/10     300 字
     200      0      5        1.00 倍       10/10     600 字
     200     20      6        1.10 倍       10/10     600 字
     200     50      7        1.31 倍       10/10     600 字
     400      0      3        1.00 倍       10/10    1200 字
     400     40      3        1.08 倍       10/10    1200 字
     400    100      3        1.20 倍       10/10    1200 字
     800      0      2        1.00 倍       10/10    2400 字
     800     80      2        1.08 倍       10/10    2400 字
     800    200      2        1.20 倍       10/10    2400 字

  第一组数看成本:**重叠从 0 调到 25%,总字符从 1.00 倍涨到 1.31 倍。**
  这多出来的几成不是浪费——它是为了「不让一句话被切成两半」付的保险费。

  第二组数看效果:**块大小 100、不重叠时,10 条关键句里有 1 条被切断。**
  切开之后,那一句的后半截在另一个块里,检索到的块里没有完整答案。
  把重叠调到 10%,这 1 条就全部补齐了——**这就是那笔保险费买到的东西。**

  第三组数看代价:最后一列是「检索 3 条要占多少字」。
  **块 800 的时候,3 条就是 2400 字,占掉 8192 上下文的近三成。**
  这还没算对话历史和系统提示词——预算是一起用的。

  所以两个方向的错法完全对称:
  块太小                   句子被切断,答案跑到隔壁块里去了
  块太大                   一条块里混着几条不同的规定,还吃掉上下文预算
  **调这个参数的唯一依据是「一个块能不能装下一条完整规定」,不是「别人推荐多少」。**

表的十二行里,第一组(块 100、重叠 0)最关键:10 条关键句只有 9 条完整,1 条被切断了。 切断的那条是「飞机经济舱仅限总监及以上人员乘坐,且需提前三天报行政部备案」——它比 100 字长, 跨了块边界。前半截和后半截分居两个块。 把重叠加到 10%(也就是 10 字),这 1 条就补齐了,总字符从 1.00 倍涨到 1.10 倍。 多付一成冗余,换回一句完整的话。 再看最后一列:块 100 时 k=3 只要 300 字,块 800 时要 2400 字。 八倍的预算差,全在块大小这一个参数上。 要说明一点:这份文档每章约 100 字,所以 200 到 400 的块刚好一章一块,关键句自然全在里面。 换成合同、代码库、法律条文,这个甜区会完全不同。参数要按你的文档结构定,不能照抄。

30 秒实验

  1. 把块大小加到 2000(比整份文档还大),看是不是只剩 1 块、关键句全在。 再算 k=3 要多少字——这就解释了为什么「块调大」不能无限用。
  2. 把重叠从 10% 加到 50%,看关键句完整率还涨不涨。 多半不涨,但总字符会涨到 1.5 倍以上。 这就是边际收益递减。
  3. 换一份更长的文档(比如把这份制度复制三遍拼起来),再跑一遍,看甜区会不会位移。 同一套参数在不同文档上的表现不一样,这就是要试的原因。

常见误区

  1. 「块越大越不容易漏」。块大了确实不缺句子,但会引入无关内容、稀释相似度、吃掉上下文预算。 它不是免费的。
  2. 「重叠设 50% 最保险」。冗余涨到 1.5 倍以上,检索还会重复命中,塞进提示词里同一段出现三遍。 10% 到 20% 通常就够。
  3. 「top-k 越大召回越好」。k 大了塞进大量不相关段落,模型要在噪声里挑,效果反而下降。 k 受上下文预算约束。
自测三问(点开对答案) 1. 块切得太小会出什么问题? —— 一条完整的规定被切成两半,前半截和后半截分居两个块。 检索到其中一块时,答案不完整或者干脆不在里面。 2. 重叠的作用是什么?代价是什么? —— 作用是「不让一句话被切成两半」; 代价是冗余——总字符从 1.00 倍涨到 1.3 倍左右,存储和检索都要多付。 3. 为什么 k 不能随便调大? —— 因为 k 乘块大小直接吃上下文预算。 k=5、块 800 就是 4000 字,加上对话历史很容易顶满窗口,反而让模型看不清重点。

参数配好了,最后一步是上传资料并验收。下一节看同一份文档在不同切块下,答对率能差多少。

相关笔记