🐦 第5章 基于大模型的企业知识库
📄 一句话概括

九步:建库、建工作流、开始、检索、DeepSeek、结束、试运行、发布、建智能体。 九步里真正决定成败的不是模型,是第六步之前那个阈值和一条兜底支路。 这一节把阈值调三档真跑一遍:调低答错,调高全转人工,中间没有完美值。

📝 本章和前面几章的关系

前四节讲原理和参数,这一节组装成一个东西。 这个案例在书里是「汽车行业智能客服」,做的事很朴素:用户问车的保养、保修、救援, 从八段资料里找到对应的一条,组织成一句话答。 它是第 4 章那条六节点链的直系后代——多了知识库,少了分支。 后面第 8 章的推文生成器、第 12 章的海报助手,都是同一个模子。

一、九步的路线图

步 做什么 在哪一层
1 创建知识库、传八份资料 知识库
2 新建一个工作流 工作流
3 开始节点:定义用户问什么 工作流
4 知识库检索节点:查三段 工作流
5 大模型节点:照着三段答 工作流
6 结束节点:交出答复 工作流
7 试运行,走两条分支 工作流
8 发布工作流 工作流
9 创建智能体,挂上这个工作流 智能体

九步里 1 和 9 是两次「跳出去」:一次去建库,一次去建智能体。中间七步都在同一个画布上。 很多人卡在第 9 步:工作流调通了,挂到智能体上却不生效。 原因通常是描述写得太抽象——和第 3.1 节插件那个坑一模一样。

二、前四步:建库、建流、开始、检索

建库:八份资料,一个主题(汽车售后),每份一段。 别把销售政策混进来——第 5.4 节说过,一个主题一个库。 建流:新建空白工作流,名字写清楚,比如「售后问答」。 名字是给智能体看的,它靠这个名字决定什么时候调。 开始节点:定义一个入参,就叫 question,类型 string。这一个字段会一路传到检索节点。

检索节点:查询内容填 {{开始.question}},top-k 填 3,阈值先留空。 留空的道理后面会讲——得先看看分数分布。

三、第五、六步:大模型与结束

大模型节点的提示词是这一节的核心,四段都得写。 第一段交代身份:「你是某品牌汽车的售后客服。」 第二段给资料:「以下是相关资料,用 [1][2] 标注出处:{{检索结果}}。」

第三段给规矩:「只根据资料回答。资料里没有的,回答『这个问题我需要转人工』。」 第四段给格式:「回答控制在三句话以内,不要用列表。」 第三段那句话是全篇最值钱的一句。不写它,模型会拿硬凑的那段硬答。

结束节点:把大模型的输出接到一个字段上,比如 answer。只留一个出口。

四、第七步:试运行要走两条分支

试运行至少六个问题:四个能答的、两个不能答的。 能答的看两件事:答案对不对、有没有带出处。不能答的看一件事:它有没有承认不知道。 演示里六个问题跑三档阈值,结果分三行。

召回阈值 答对 硬答答错 转人工
0.00 4 2 0
0.15 3 0 3
0.30 0 0 6

阈值 0 的时候六题全有召回,其中两题硬答——那两题库里根本没有。 阈值 0.30 的时候六题全转人工:一题都没答错,也一题都没答。 0.15 那一行是最有信息量的:三题答对、三题转人工、零题答错。 而其中一题本该答对,却因为排序错了被挤到阈值下面——下一段讲。

五、排序错了,阈值也救不回来

值得单独看一眼的题是「预约保养怎么约」。 它在阈值 0 时答对,阈值 0.15 时变成转人工。阈值调高怎么会把答对的题弄丢? 因为排在第一位的是「保养周期」那一块——「保养」两个字在问题里更显眼。 装着答案「提前一天」的「预约方式」那一块分数本来就低,一抬阈值就被筛掉了。

这类失败和阈值无关,是排序错了。 调阈值只会让它在「答错」和「不答」之间来回,治不到根。 治根的办法只有三条:换 embedding 模型、补同义词表、加关键词检索做混合召回。三条都在第 5.6 节展开。 还有一件事得同时做:提示词里写「资料里没有就转人工」。阈值管拦,提示词管劝,一个都不能少。

六、第八、九步:发布与挂智能体

发布之前再走一遍两分支。 发布后工作流有版本,改完要重新发一次——这一步经常忘,表现是「改了没生效」。 建智能体:新建一个,人设写「汽车售后客服」,然后把刚发布的工作流作为能力挂上去。 挂上去的时候要填一段说明,写「用户询问保养、保修、救援、预约相关问题时调用」。 这段说明就是模型判断调不调的依据,写得抽象等于没挂。

最后在对话框里试三句:一句能答的、一句不能答的、一句闲聊的。闲聊那句不该触发工作流。

七、这条链还能往哪长

往左长:开始节点前面加一个意图识别,把「问价格」的直接分流走。 往右长:结束节点前面加一个卡片节点,把答复和三条相关链接摆成一张卡。 往中间长:检索节点后面加一个代码节点,判断「召回分数是不是太低」,低就走转人工支路。

三条都不改骨架。第 4 章那句在这儿再用一次:骨架不变,长出来的都是枝。

八、动手 Demo:汽车客服:九步里最要紧的三步

八段汽车售后资料,六个问题(四题能答、两题不能答)。 三档阈值各跑一遍,看答对、硬答答错、转人工三种结局怎么变。

AUTO = [
    u'保养周期:首次保养 5000 公里或 6 个月,以先到为准…',
    u'保修政策:整车保修 3 年或 10 万公里,以先到为准…',
    u'易损件:轮胎、刹车片、雨刮、灯泡属于易损件…',
    # 另外五段:救援服务、首保材料、预约方式、置换补贴、充电桩
]

def run_kb(floor):
    right = wrong = hand = 0
    for q, e, answerable in CAR_Q:
        hits = Q.retrieve(q, AUTO, top=3, floor=floor)
        if not hits:
            hand += 1                    # 转人工
        elif answerable and e in u' '.join(d for _s, d in hits):
            right += 1
        else:
            wrong += 1                   # 硬答,答错
    return right, wrong, hand

实跑输出:

--------------------------------------------------------------------
汽车客服:九步里最要紧的三步
--------------------------------------------------------------------


  ① 六个问题,三个阈值
  --------------------------------------------------------------------

| 召回阈值 | 答对 | 硬答答错 | 转人工 | 答对率 |
|:---:|---:|---:|---:|---:|
| 0.00 | 4 | 2 | 0 | 67% |
| 0.15 | 3 | 0 | 3 | 50% |
| 0.30 | 0 | 0 | 6 | 0% |

  阈值 0 全都有召回,两题硬答答错——那两题库里根本没有。
  阈值 0.30 时六题里五题转人工、一题硬答答错、零题答对。
  留下的那一题是「轮胎保修吗」,它召回到的是「首保材料」,那块里提到「保修手册」,分数够线,
  但不是答案。
  阈值只按分数筛,筛不出「相关但不对」。 这是阈值这条路的天花板。
  所以两条都要:一个是阈值兜底,一个是提示词里写「资料里没有就说没有」。

  ② 阈值 0 的时候会出什么事
  --------------------------------------------------------------------
  问:竞品 C 车型便宜两万你们能便宜吗
    硬召回了 0.06 分的一块:充电桩:纯电车型随车赠送 7kW 家用充电桩,安装服务免费,超…
    模型只能拿这块硬答——答非所问,但它不会说「我不知道」。
  问:我上个月买的车能退吗
    硬召回了 0.08 分的一块:易损件:轮胎、刹车片、雨刮、灯泡属于易损件,保修 6 个月或 …
    模型只能拿这块硬答——答非所问,但它不会说「我不知道」。

  ③ 逐题看三种阈值的结局
  --------------------------------------------------------------------

| 问题 | 阈值 0 | 阈值 0.15 | 阈值 0.30 |
|---|:---:|:---:|:---:|
| 第一次保养什么时候做 | 答对 | 答对 | 转人工 |
| 整车保修多久 | 答对 | 答对 | 转人工 |
| 轮胎保修吗 | 答对 | 答对 | 转人工 |
| 预约保养怎么约 | 答对 | 转人工 | 转人工 |
| 竞品 C 车型便宜两万你 | 答错 | 转人工 | 转人工 |
| 我上个月买的车能退吗 | 答错 | 转人工 | 转人工 |

  逐行看,最值得琢磨的是「预约保养怎么约」:阈值 0 答对,阈值 0.15 反而答错。
  原因不是阈值——是排序错了。
  这句里「保养」两个字更显眼,检索把「保养周期」那块排到了前面,
  而答案「提前一天」在「预约方式」那块。这类失败调阈值治不了,只能换检索方式或补同义词。

  ④ 那条工作流的形状
  --------------------------------------------------------------------
  开始 → 知识库检索 → 选择器(有召回?)→ 大模型 → 结束
                       ↘ 没召回 → 转人工 → 结束
  九步里真正决定成败的是第三步:那个阈值,还有「没召回就转人工」这条兜底支路。
  把支路补上,最坏的结果也只是多转几个人工,不会答错。没补这条支路,最坏的结果是硬答一通。
  ! 阈值调低,答错率上升;调高,转人工率上升。
  没有两头都好的值——要按业务挑:客服场景宁可转人工,销售场景宁可多答。

①第一张表三行就是三档阈值。看第一列到第三列的变化:答对从 4 掉到 0,转人工从 0 涨到 6。 ②阈值 0 那一段最值得看:两题不能答的,都被硬召回了 0.06、0.08 分的一块。 模型拿着这块硬答,它不会说「我不知道」。 ③第三张表逐题看。注意「预约保养怎么约」这一行:阈值 0 答对,阈值 0.15 变转人工。 这不是阈值的错,是排序错了——「保养」两个字更显眼,检索把「保养周期」排到了前面。 ④0.30 那一行最干脆:六题全转人工。零答错,也零答对。客服场景这个结果不算坏,销售场景就废了。 ⑤最后那两行是那条链的形状。九步里真正决定成败的,是那个阈值和「没召回就转人工」这条支路。

30 秒实验

  1. 拿你们业务的二十个真问题跑一遍,把「该中的没中」和「不该中的中了」分开记。
  2. 在提示词里加上「资料里没有就说没有」这一句,再问一遍那两个不能答的题。
  3. 给你的工作流加一条转人工支路。加完再跑,最坏结果从「答错」变成「多转几次」。

常见误区

  1. 「阈值调高更安全。」调太高会全转人工。演示里 0.30 那档一题都没答。
  2. 「检索到了就能答对。」召回到「相关但不对」的段落,模型会照着硬答。要靠提示词兜。
  3. 「发布完就生效。」工作流改了要重新发布,不然线上跑的还是旧版本。
自测三问(点开对答案) 1. 九步里哪一步最容易被卡住? —— 最后一步,挂智能体。工作流名字和说明写得太抽象,模型判断不出什么时候该调它。 2. 提示词里最值钱的是哪一句? —— 「资料里没有的回答『需要转人工』」。不写它,模型会拿硬凑的段落硬答。 3. 为什么阈值调高反而可能答错? —— 排序错了的时候,装着答案的那段分数不高,会被筛掉;留下的是分数高但不相关的一段。

一个能跑的东西做出来了,接下来是让它靠得住。 下一节讲落地的三个坑:召回不准、更新滞后、权限穿透——第三个最要命。

相关笔记