☰
历史工单接入RAG:智能客服Agent知识库构建与检索实战
2026/10/2 4:05:19 网站建设 项目流程

1. 接历史工单这件事,本质是给 Agent 补“工作经验”

上个月我们团队接了一个智能客服类的 Agent 项目,刚开始跑出来的效果让人很尴尬——用户问“我的发票开错了怎么办”,Agent 能给你回一段大而全的官方流程说明,但完全没提到我们业务里真正会遇到的“发票抬头变更后系统自动同步要两个工作日”这种实战细节。说白了,它没有见过公司内部这些真实发生过的故障和处理方式。

后来我把目光转向了工单系统里堆着的那几万条历史工单,思路一下子打开了。这些工单是过去几年一线客服、技术支持、研发处置真实问题的完整记录,每一单都包含用户原始描述和处理人员的最终结论。把历史工单和知识库接起来,让 Agent 先去检索相似问题,再基于检索到的历史工单给出有依据的回答,这个方向才是对的。

这篇博文就把我们这次改造的完整过程拆开讲:为什么历史工单比产品文档更适合先入库、工单数据怎么清洗和向量化、相似问题检索怎么做召回重排和阈值控制、Agent 拿到检索结果后怎么编排应答,以及我们踩过的那些坑。适合正在做智能客服 Agent、想通过 RAG 给大模型接企业私有数据的开发者参考,也可以当作一个“历史工单 + Agent + 知识库”三件套的落地案例来看。

1.1 为什么历史工单比产品文档更值得先入库

很多人做企业知识库,第一反应是把产品手册、内部 Wiki、FAQ 往向量数据库里塞。这个思路没错,但对一个已经运行了好几年的业务系统来说,历史工单的数据价值往往被严重低估。

工单本质上是一批经过人工验证的“问题-答案”对。用户在工单里用大白话描述自己遇到的问题,客服或研发人员经过排查给出了解决方案,并且在结单时基本都会写清楚最终是怎么处理的。这些内容不是从文档里抄出来的理论,而是真实场景下验证过的操作路径。比如“客户反馈登录后提示账号异常,实际原因是密码连续错误触发风控锁定,处理方式是通过管理后台解除锁定”,这种一对一的因果信息,在标准文档里通常找不到。

另外从冷启动的角度看,新上线的 Agent 最缺的就是领域语料。通用大模型对行业内部的专有名词、系统名称、特殊流程基本是一无所知,而历史工单里恰好包含了这些语言习惯和业务背景。把这些数据接进来,Agent 相当于直接获得了这家公司多年的“工作经验”,而不是靠 prompt 里写几条规则来假装懂业务。

1.2 整个方案的链路设计

这次改造的整体链路可以用一句话概括:把历史工单清洗成可用语料,向量化后放进知识库,接到 Agent 的相似问题检索流程里,让大模型基于检索结果作答。

具体拆开是四个环节。第一,工单清洗,把脏乱差的历史记录变成规范的问题和答案对。第二,知识库构建,设计切片策略、选择合适的 Embedding 模型、确定向量库的元数据字段。第三,相似问题检索,混合召回加精排,算相似度分数并设置阈值。第四,Agent 编排,把检索结果拼进 prompt,约束大模型的回答逻辑和引用格式。

这里有一点我想强调:很多团队做这类项目,一开始就把精力花在调 prompt、试各种 Agent 框架上,但效果始终上不去。问题的根源往往在数据链路——知识库里装的是垃圾,检索再准也是垃圾。所以我们这次把 70% 的时间花在了工单清洗和知识库构建上,后面所有环节都顺了。

提示:别一上来就选 Agent 框架、配对话流程,先把数据源的质量和检索链路跑通。数据不行,框架再花哨都是白搭。

2. 工单知识库构建:从脏数据到可用语料

历史工单数据直接搬进知识库是行不通的。我见过有人把原始工单原封不动向量化入库,结果用户问“账户被锁定了怎么办”,检索出来的“相似工单”里掺杂着大量客服之间的内部沟通记录、无意义的系统报错日志,甚至还有“该问题请联系某某处理”这种半截话。

2.1 工单清洗的五个关键动作

老工单系统的数据质量参差不齐,我们的清洗流程主要做了五件事。

第一是剥离个人信息。工单里通常包含用户手机号、邮箱、公司名称等敏感信息,入库前必须脱敏。简单做法是用正则匹配替换成占位符,复杂一点的可以用命名实体识别来扫。这块不做好,知识库本身就成了隐私泄露风险点。

第二是过滤无效内容。工单系统里大量存在“测试单”“重复单”“误提单”,特征是标题里带“测试”、描述极短、或者处理结果为空。这些直接过滤掉,可以用结单状态加描述长度做组合规则。

第三是合并同义表达。用户的口语五花八门,“登不上”和“无法登录”、“卡死了”和“没反应”本质是同一类问题。清洗时可以建立同义词表,在入库前做一轮归一化替换,这样后面检索时的命中率会高很多。这个环节看起来土,但收益非常直接。

第四是拆分一单多问。有些工单里用户一口气问了三个问题,处理人员的答复也是混在一起的。这种如果不拆,后面的检索和回答都会很糊。我的做法是按问题语义切分,把“问题描述+对应处理结论”绑定成一个条目,宁可拆碎一点也不要一整块扔进去。

第五是标准化方案描述。处理人结单时写的字往往很随意,比如“重置一下就好了”。这类描述作为检索结果给大模型用很容易产生歧义。清洗时要补全操作对象和操作路径,比如“通过后台用户管理页面重置该用户的登录密码后恢复”。这一步可以靠规则模板加人工抽查来做。

2.2 切片策略与向量化入库

清洗完的数据,下一步就是切片和向量化。这块我们踩过不少坑,这里直接给结论。

切片策略上,工单不同于长文档,核心信息密度高,不适合按固定字数机械切。我们采用的是“语义单元”方式:一条工单处理完后,把“问题描述”“处理过程”“最终结论”打包成一个条目,整体作为向量化单元。因为工单短的平均也就一两百字,直接整条向量化,语义完整性最好。对于极少数超长工单,比如研发排查了三天、贴了几十屏日志的,先按“排查日志”和“结论摘要”分开,只把结论摘要部分入库。

向量化模型的选择上,中文场景推荐 BGE-M3 或者 bge-large-zh,前者多语言能力强,后者中文精度高,实测下来都比 OpenAI 的 text-embedding-ada-002 更懂工单里的中文口语。如果机器资源有限,bge-small-zh 也可以作为起步方案,后面再换大的。这里要注意一个问题:如果你要混合检索,向量模型必须固定下来,中间不要随意换,否则历史向量和新向量的语义空间不一致,检索分数就失真了。

向量数据库方面,工单量在几十万条以内的,Chroma、Qdrant 都够用;数据量更大或者要跟已有 ES 体系融合的,直接用 Elasticsearch 的向量检索插件更省事,可以一套系统同时搞定关键词检索和向量检索,混合召回的工程成本最低。

2.3 时效性处理:老工单的生死线

历史工单最大的隐患是过时。两年前处理“发票打印格式异常”的方案,可能因为系统升级已经完全失效了;三年前的某个 bug 工单,对应的功能模块可能已经下线。如果不做时效性处理,Agent 很可能拿旧方案回答新问题,而且答得振振有词。

我们采用了分层策略:工单入库时根据创建时间分成“活跃区”和“存档区”,默认只检索活跃区,时间窗口设为最近 18 个月。同时给工单打上业务线和版本标签,检索时优先匹配当前系统版本相关的内容。这个设计还有个额外好处,向量库规模被控制住了,检索延迟会明显下降。

注意:历史工单的时效性不能只看时间,还要看业务变化。如果产品在某个时间点做过大版本升级,应该按版本节点重新划定活跃窗口,而不是一刀切按月数算。

3. 相似问题检索:召回、重排、阈值三步定生死

知识库建好之后,核心问题就变成了:用户问一个问题,系统能不能在几万条工单里把真正相似的历史工单捞出来。这一步的效果直接影响 Agent 的回答质量。

3.1 召回层:向量检索与关键词检索的混合策略

只靠向量检索在工单场景下是不够的。原因在于,用户的实时提问通常非常口语化,而工单里客服记录的描述往往半书面化。“我账号怎么登不上了”和工单里写的“用户反馈登录失败”,向量距离未必近。另外,工单里往往包含大量专有名词,比如具体功能模块名、错误码、系统名称,这些关键词的精确匹配,向量模型反而不如传统检索做得好。

我们的方案是关键词检索和向量检索双通道并行。关键词用 BM25,跑在 Elasticsearch 上,针对问题描述字段做全文索引;向量通道负责语义召回,用 query 的 embedding 去向量库做 KNN 搜索。两条通道各取 Top 50,然后合并去重,作为粗排结果送给重排层。

两条通道的权重不需要一开始就定死,先各取 50 条,让重排层自己去学,效果会更稳。等到线上跑了小一个月积累了反馈数据,再根据实际命中来源调整比例,比如发现 70% 的最终命中来自向量通道,就可以把向量 TopK 加大到 80。

3.2 重排层:精排模型怎么选怎么用

召回层是广撒网,重排层是精准定位。粗排出来的候选集往往有大量“看着像但实际不相干”的工单,比如用户问发票问题,召回里混着用户问“报销单怎么打”的工单——都跟财务相关,但根本不是同一件事。

重排我们用 bge-reranker-base,交叉编码器直接计算 query 和每个候选工单的相关性分数,效果比向量距离靠谱得多。逻辑很简单:把候选工单按相关度从高到低排序,截取 Top 3 到 Top 5 送给大模型作为参考材料。这里有个工程细节,交叉编码器速度慢,所以只对粗排的 100 条候选做重排,权衡下来单次查询延迟能控制在 300 毫秒以内。

重排分数的使用有一点容易忽略:不同 query 的重排分数分布差异很大,同一个 0.6 分在有些问题下已经算高相关,在另一些问题下可能就是不相关。所以我不建议用一个绝对分数做硬性过滤,而是先排序,再看 Top 结果的分差,如果第一名和第二名分差特别大,基本可以确认命中;如果前三名分数都差不多,说明候选集本身区分度不高,这时候要降置信度。

3.3 阈值控制:什么时候宁可说“不知道”

这一节是整个检索链路里我特别想强调的部分。很多 Agent 翻车不是因为没有检索到内容,而是检索到的内容明明不相关,Agent 依然硬着头皮回答。解决这个问题必须设阈值,而且这个阈值不是一个拍脑袋的数字。

我们的做法是拿历史真实用户问题做了一次回放:把过去三个月的用户咨询全部跑一遍检索流程,人工标注每一条检索结果是否真的相关,然后画出相关分数分布图。最终把“不回答”的阈值定在重排分数的 0.45 分——低于这个分数,Agent 直接说“该问题需要进一步确认,已为您转接人工”。这个策略牺牲了一点自助解决率,但把胡说八道的比例压到了极低。

阈值设完不是一劳永逸的。随着知识库持续新增工单、检索策略调整,分数分布会漂移,建议每季度重新做一次回放校准。

4. Agent 应答编排:让检索结果变成有依据的回答

检索做得好,只代表拿到了好的材料。Agent 能不能把材料变成用户听得懂、且准确可靠的回答,取决于应答编排这一步。这里说的编排不是 LangChain 里写几个链那种“编排”,而是 prompt 约束、引用逻辑和对话状态管理。

4.1 Prompt 设计:把工单变成依据而不是噪音

大模型拿到检索到的工单原文,最容易犯的毛病是照抄。工单里有大量内部沟通痕迹,比如“让张三帮忙看下”“用户说已经按操作试过了”,直接复述给用户非常奇怪。所以 prompt 里要明确几条规则。

第一,检索结果只是参考依据,回答必须提炼成给终端用户的直接表述。第二,如果检索结果之间有冲突,以工单时间更新的为准。第三,所有结论必须对应引用工单编号,格式类似“参考工单 #20240315-0089”。第四,禁止在回答中出现工单里的系统内部术语、处理人姓名、内部路径。这几条约束写进系统提示词,实测能把回答的专业度拉高一大截。

Prompt 的结构我们也做了规范化处理:系统提示词固定不变,中间插入检索结果区,最后是用户问题。检索结果区用序号列出每条工单的“问题-结论摘要”,并附上相关度和工单号。这个结构的稳定性很重要,不要频繁调整措辞,否则后面做效果评估时很难分清是检索问题还是 prompt 问题。

4.2 引用溯源与置信度展示

引用溯源不只是一个好看的格式,它是整个系统的信任基石。用户在收到答案时能看到“参考历史工单 #xxx”,既方便追问,也给客服质检留了后门。我们内部还做了一个小工具,Agent 每次回答都会在后台记录命中了哪些工单、重排分数多少,质检团队可以快速定位错误答案的根源。

置信度展示其实是阈值控制的延伸。我们分了三档:重排分数高于 0.75,Agent 直接给出确定性回答;分数在 0.45 到 0.75 之间,回答里加上“根据历史处理经验,您可以先尝试……,若无法解决请补充信息”;低于 0.45,直接转人工。这三档提示在 prompt 里也是写死的,避免大模型自己发挥。

这里有个细节,工单检索和知识库问答在置信度上的体验期望是不同的。用户问“这个功能怎么用”,Agent 哪怕答得一般也能接受;但用户问“我这个问题能不能解决”,答错一步就可能让用户白跑一趟。所以对于带明确操作指令的问题,我们把置信度要求整体提高了一档。

4.3 多轮对话场景下的检索策略

多轮对话是 Agent 场景绕不开的,也是检索最容易翻车的地方。用户第一轮说“我的发票开错了”,第二轮说“那我已经提交了红冲申请怎么办”,如果每轮都独立去检索,第二轮很容易把“发票开错”的工单再次召回,忽略“红冲”这个关键新信息。

我们的处理策略是:第一轮正常检索并保留命中的候选集;第二轮先判断用户的消息是否引入了新的业务关键词,如果引入了,就在原有候选集上做一次重排过滤,并补充检索新关键词;如果没有引入新关键词,直接沿用第一轮的检索结果,不做重复检索。这样既保证了上下文连贯,又避免不必要的检索延迟。

这种做法简单有效,但注意不要做得太复杂。我也试过把整轮对话拼接起来做查询改写,效果反而不稳定,因为工单场景的 query 本身就短,改写之后的语义容易跑偏。

5. 常见问题与排查技巧实录

这部分是我们线上踩坑两个月的浓缩。每个问题都对应一次真实的故障排查经历,大家可以直接拿这份清单做排查手册。

5.1 召回率低、命中不准怎么定位

症状是用户问题检索出来的工单明显不对。排查顺序我建议从下往上走:先看召回层的日志,确认向量检索和关键词检索分别返回了什么;再看重排层的分数分布,判断是粗排漏了还是精排排错了;最后看知识库数据,确认对应问题的语料是否真的存在。

我们遇到最多的情况是语料确实存在,但被切片策略切坏了。比如一条工单同时包含登录问题和支付问题,清洗时没拆开,整条入库后,检索“支付失败”时把“登录问题+支付问题”的混合体召回了,重排分数还不低,因为里面确实提到了支付。这种问题纯调检索参数解决不了,必须回到清洗环节,把切片粒度改细。

另一个高发原因是 Embedding 模型的领域适配性差。工单里的说法高度行业化,通用模型容易把它们映射到语义相近但完全无关的区域。排查方法是抽取 100 条典型问题,人工标注期望命中的工单,跑一遍检索看 Top 10 的命中率,如果明显偏低,建议换更专业的模型做向量化。

5.2 工单数据过时导致答错怎么办

这是时效性问题的典型表现:用户问“发票税率是多少”,Agent 基于一条两年前的工单回答了旧税率,哪怕检索和生成都是完全正确的流程,答案也是错的。

这个问题的根源在数据层,光靠 prompt 约束“注意时效性”基本没用,大模型很难自己判断哪条历史知识已经失效。我们的方案是双管齐下:一方面在知识库里给工单加“有效状态”字段,业务方定期批量审核标记;另一方面在检索结果展示时把工单时间透传给大模型,prompt 里明确要求“优先参考最近 6 个月内的工单,如果只有超过一年的工单,必须进行提示”。

还有一种更隐蔽的情况:旧工单本身方案没错,但操作路径变了。比如原来解决问题是找管理员重置密码,现在系统支持用户自助找回。这种场景需要知识库维护人员定期合并同类工单,更新方案描述,不能只靠自动流程。

5.3 线上效果评估与持续迭代

知识库和检索系统上线后,评估是最容易被忽视的环节。我见过不少项目上线后就只看一个“回答率”指标,回答率高就觉得成功了,但回答里隐藏的错误没人看。

我们的评估方式是每周抽检加月度复盘。每周随机抽 100 条 Agent 的真实回答,按“正确”“部分正确”“错误”“无法判断”四档人工标注;月度复盘把本周抽检结果按业务线和问题类型分类,找出错误集中的模块,优先优化。这个机制跑下来,我们第二个月的自助解决率提升了近 10 个百分点,核心就是靠抽检发现了“发票类问题检索总是召回到报销类工单”这个系统性问题。

这里分享一个排查技巧:所有 Agent 回答都在后台记录命中的工单 ID 和重排分数。抽检时如果发现某条回答错了,第一步不是看大模型生成逻辑,而是先看命中的工单本身是不是相关。如果工单相关但答错了,那是生成问题;如果工单不相关,那是检索问题,处理路径完全不一样。用这个二分法排查,定位问题的速度会快很多。

6. 从“查相似问题”到“工单知识闭环”

历史工单接进知识库做成相似问题检索,只是第一步。项目跑起来后我们发现,如果知识库不能持续更新,当初的几万条工单会被新的业务问题逐渐稀释,准确率会缓慢下降。

6.1 自动沉淀新工单的方法

堵住知识库老化漏洞的办法,是把新工单也自动接进知识库。我们的流程是:工单结单后,如果处理结果标记为“已解决”,就进入待入库队列;系统按清洗规则自动过滤无效内容,把工单拆成“问题-结论”条目;经过一道人工抽检后写入知识库。整个流程用定时任务驱动,一天一跑,知识库的增长完全跟着真实业务走。

自动沉淀里有一个细节:不是所有结单工单都值得入库。如果处理人的结论是“无法复现”或者“属于用户操作问题,引导即可”,这类工单的价值很低,入库反而会污染知识库。建议在入库规则里加一层标签过滤,把工单分类字段作为一个准入条件。

6.2 这个思路还能辐射到哪些场景

历史工单接知识库这套打法,本质上是把“历史处置经验”变成“可检索的依据”,思路完全可以复制到其他场景。做运维排障的团队,可以把历史故障工单接进监控告警 Agent,告警时直接给出历史上类似的处置方案;做销售支持的,可以把历史客户询单和报价方案做成知识库,让 Agent 在接到客户问题时先查历史记录;工业企业可以把设备维修工单接进检修 Agent,报修时自动推荐同型号设备的常见故障处理方式。

我在实际操盘这个项目时最深的体会是:大模型的能力大家都差不多,真正拉开差距的是你喂给它的数据质量。历史工单是企业里现成的、最贴近真实业务的知识资产,接好这一层数据,Agent 的实用性会立刻上一个台阶。先别急着做复杂的功能编排,认认真真把历史工单清洗干净、检索链路调准,这个“笨功夫”会是你整个项目里性价比最高的一笔投入。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询