3.1 工作流设计:问答 → 检索 → 回答 → 兜底四段闭环

知识库建好之后,我第一次在编排画布上拖节点,不到二十分钟就连出一条链:开始节点接知识库检索节点,检索结果喂给大模型节点,大模型输出直接返回用户。一测试就露馅了——问它退货政策,它把三页文档搅成一团;问它昨天的订单状态,它一本正经地编了一个不存在的时间;问得稍微模糊一点,它干脆把两个不相干的答案拼在一起。

问题不在于节点少,而在于我只想了正常流程,没想失败时怎么办。客服场景有个特点:失败路径的用户体感比成功路径更强烈。一次答得漂亮用户觉得正常,一次答错用户直接截图发群里。这一节讲我最后定稿的工作流:问答、检索、回答、兜底四段闭环,配套六个节点的画布实现和三类异常分支的处理。如果你之前没接触过编排类工具,可以先看腾讯云五件套实战里关于服务编排与链路设计的内容,底层思路是通的——把大模型、知识库、业务接口想象成三个服务,谁先谁后、挂了怎么办,和搭普通后端没有本质区别,只是这里连线的方式从写代码变成了拖节点。

先把闭环画在纸上

动手拖节点之前,我先在纸上把一次问答拆成四段,每段明确输入、输出和失败时怎么办。这一步看着多余,但后来每次改流程,我都是先改这张图而不是改画布——图上想清楚,画布上只是照着连。

环节 输入 输出 所用节点 失败处理
问答接收 用户原始消息 归一化后的意图与关键词 开始节点 输入过短或无意义时引导用户补充说明
知识检索 意图与关键词 若干条候选切片及相似度得分 知识库检索节点 检索为空则进入兜底分支
阈值过滤 候选切片与得分 过滤后的可用切片集合 条件判断节点 得分整体偏低时标注不确定
回答生成 切片加提示词 带来源要点的回答 大模型节点 生成超时则重试一次,仍失败走兜底
兜底转人工 无法自助回答的会话 工单与对话上下文摘要 转人工节点 转人工通道异常时留言存档待处理

四段里前三段是机器的活,最后一段是人的活。判断在哪一段失败、失败后流向哪里,比纠结某段内部怎么实现重要得多。多数团队第一版都栽在同一处:只设计了绿色通道,把所有红色出口留到上线后现场补。举个例子,同样是检索不到,用户问的是产品价格和问的是去年某次活动,处理方式不该一样:前者知识库没覆盖就该补知识,后者本来就超出范围,直接兜底就行。失败原因要记录下来,这就是 5.2 知识库迭代流水线的输入来源。

落到画布上的六个节点

纸上的四段闭环,落在编排画布上是六个节点。多出来的两个是条件判断节点和工具节点:前者负责分流,后者负责查订单这类外部调用(工具调用单独在 3.3 展开,这节先看链路全貌)。

节点 作用 上游传入变量 下游如何消费
开始节点 接收用户消息,做初步归一化 无 把 question 传给下游
知识库检索节点 从已建索引召回相关切片 question 输出切片列表与各切片得分
大模型节点 依据切片生成回答 question 加切片内容 输出最终回答文本
条件判断节点 判断能否自助回答 切片数量、得分阈值 命中阈值走回答,否则走兜底
工具节点 调用订单查询等外部接口 从消息提取的参数 返回业务结果给大模型节点
转人工节点 移交客服并生成工单 整段对话上下文 客服工作台接收工单

节点串联的关键是变量要对得上。开始节点输出的 question,检索节点要按同名变量引用;检索节点输出的切片列表,条件判断节点和大模型节点都要能拿到。命名一旦在画布上定下来,就别中途改——改一处,引用它的所有节点跟着报错,而且报错信息往往只说变量不存在,不告诉你哪改了。

我另外维护了一张变量清单(单独的文档,不放在画布里):变量名、来自哪个节点、被哪些节点引用、数据类型。新加节点先查这张表,确认没有重名冲突再上画布。

三种最常见的异常分支

正常链路跑通只花半小时,下面三种异常分支让我调了两天:

第一,检索为空。用户问的问题知识库里没有,比如问下一代产品什么时候出。这时候大模型拿不到任何切片,最容易开始编。正确做法是在条件判断节点里加一个空切片检查:切片数不足时,直接走兜底文案,告诉用户这个问题需要人工处理,顺手给出转人工入口。同时把这个问题记下来,周五补知识库时优先补这类空白。

第二,置信度偏低。检索有结果,但得分都在阈值边缘,回答可能对也可能不对。我的处理是允许回答,但在回答前加一句口径:以下内容依据公司文档,仅供参考,具体以人工回复为准。同时给这次会话打一个低置信度标记,纳入抽检名单,这一步配合 5.1 的效果评估一起做。阈值本身不是定死的,跑了两周数据之后我往上微调了一次,宁可多转人工也不冒险答错。

第三,整体超时。检索慢、大模型排队、网络抖动,任何一个环节卡住都会让用户干等。我在链路里加了超时控制:超过设定时长仍未返回,直接走兜底分支,并记录是哪个节点慢的,方便事后定位。超时阈值我设得比单节点耗时上限高出一截,避免误伤。上线之后的监控告警怎么做,可以看 5.3 那一节。

阈值不是一次定死的

条件判断节点里的阈值,是整条链路里我被问得最多的数字。检索得分高于多少才算可信?最初我拍了一个值,比如零点七,理由是我也不知道该填什么,先跑起来看数据。

第一周的数据就给了答案:得分在零点六五到零点七五之间的回答,抽检错误率明显高于平均水平——大量介于可答与不可答之间的问题,模型拿到还行的切片就开始下结论。我把阈值上调到零点八,抽检错误率降了,转人工率随之上升,客服组反馈能接受。又过了两周,看积累的会话分布,零点八上下其实还有一段模糊区,我把判定规则从单一阈值改成区间:高分直接答、中分带不确定口径、低分转兜底——一个阈值变成两段切分,颗粒度细一档,用户体感好一档。

这段经历我常和别人讲:阈值是运营出来的,不是设计出来的。第一版随便填都行,但必须有数据回流机制,两周看一次分布,配合 5.1 的评估指标调。没有回流的阈值就是玄学。另外提醒一句:阈值只对相似度这一维度有效,检索相关但答案过时、切片互相矛盾这些质量问题,得分上看不出来,得靠 5.2 的知识库巡检兜底,别指望一个阈值解决全部。

用户体感的三个细节

链路之外,还有三个影响体感的细节容易被忽略:

其一,回答顺序。先给结论再给依据:您的订单已发货,依据是查询结果。模型默认喜欢铺陈,把依据写完才说结论,用户读半句还不知道答案。提示词里把顺序约束写死,这一条调试效果最立竿见影,属于改一次提示词就能看到变化的典型改动。

其二,耗时体感。整个链路两三秒出结果,用户盯着空白等三秒很难受。可以在等待时先回一个正在为您查询的状态提示,等链路走完再替换。技术上是个小 trick,体感上省一半焦虑。

这条链路之外,还有一个影响体感的细节:用户体感差往往不是答错,而是等太久还不知道在等什么。所以每次返回兜底文案时,我都要求文案里说明下一步会发生什么,而不只是一句抱歉——兜底文案的完整写法见 3.4。

编排实操的三条经验

第一条,先写变量再拖节点。把整条链要用到的变量名列成清单,再上画布连。我第一次就是边拖边想变量名,结果条件判断节点里引用了一个不存在的东西,调试半小时才看出来——报错只说变量缺失,不会告诉你是笔误。

第二条,每个节点都配调试入口。编排平台一般带调试预览,能单独跑某个节点并看它的输入输出。排查问题时从最后一个看起来正常的节点开始往前推,比从头猜快得多。我习惯把每次调试用的测试问题存成固定集合,包括正常问题、模糊问题、超范围问题和接口报错场景四类。

第三条,兜底分支要能独立测。转人工节点不能等真出问题才验证能不能用。我定期造一批检索为空的样本,从入口走一遍完整兜底路径,确认工单能生成、上下文能带过去、客服工作台能收到。兜底链路三个月不测,大概率已经悄悄坏了——知识库更新、提示词改动、接口变更,任何一处都可能波及它。我习惯把测试结果截图存档,作为上一次验收的证据,下次出问题时先翻这些截图,往往能快速定位是哪次变更引入的。

另外,如果你想知道这类链路在更底层的 CVM 环境里怎么部署和排查,可以翻我另一本服务器常见问题,里面对接口超时和日志定位讲得比较细。

上线前的自检清单

链路画完不算完,上线前我把下面这几条逐条打钩,少一条都不敢发布:

  • 正常问答路径跑通:高频十条问题逐条验证回答和耗时。
  • 检索为空路径走通:造一条知识库没有的问题,确认它走兜底而不是硬编。
  • 阈值分支按预期分流:低分回答确实带上了不确定口径。
  • 超时路径生效:人为把某个节点模拟成超时,确认兜底文案弹出、且超时被记录。
  • 转人工工单能生成且带摘要:客服工作台能完整收到上下文。
  • 变量引用全部命中:画布里没有悬空的变量引用和未连接的分支出口。
  • 每个异常分支都留了日志:事后能还原用户走了哪条路。

这份清单后来几乎没改过,因为它问的不是功能存不存在,而是失败分支能不能跑。发布只是起点,上线后的持续观察见第四篇和第五篇。我们把清单打印贴在工位墙上,每次发布前两个人一起逐条念出声打钩——念出声这个动作听着傻,但确实抓住过两次差点蒙混过关的漏项。

延伸阅读