news 2026/10/2 22:19:13

让Agent会“翻旧账”:历史工单知识库接入与RAG检索落地实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
让Agent会“翻旧账”:历史工单知识库接入与RAG检索落地实践

年初接了一个企业内部技术支持场景的 Agent 项目,聊需求的时候业务方提了一句话让我印象很深:“我们不指望这个 Agent 什么都会,它只要会翻旧账就行。”当时我还没太在意,直到上线前测试才发现,大模型在没有历史工单做支撑的情况下,给出的方案全是泛泛的“重启一下试试”,业务方当场脸色就很难看。

这个问题的本质,就是孤零零的 Agent 没有“依据”。要让它像老员工一样开口就能说出“这个问题三号工单处理过,当时是换了电源模块”,就必须把历史工单和知识库接进来,让 Agent 有依据地查相似问题。这篇我把整个落地过程写全,包括技术选型怎么定、工单数据怎么洗、检索链路怎么搭,以及上线后几个翻车场景的排查记录。打算在客服、运维、售后这类场景里给 Agent 接知识库的同学,可以直接当案例参考。

1. 为什么我要给 Agent 接上“历史工单”这根拐杖

1.1 大模型会一本正经地编案例

先说一个真实测试现场。我把一个支持 Wi-Fi 类问题的工单丢给 Agent 试答,用户的问题是“办公室的打印机一直提示错误码 E02”,Agent 的回答是:检查纸张是否卡住、检查打印头、联系供应商更换耗材。逻辑没错,但没有任何一条和真实情况吻合。

因为企业历史工单里躺着五条同类问题,处理结论高度一致:这批打印机固件有缺陷,升级到 2.3.1 版本后 E02 才会消失。这个信息藏在工单系统里,而大模型的训练语料里不可能有你们公司的工单数据。于是模型只能用通用常识编一个“看起来合理”的答案。

这就是我做这个项目的起点:不是模型不够聪明,是它缺一个能查的“企业记忆”。所谓给 Agent 接上历史工单和知识库,本质上就是把这个记忆体补上,让它回答之前先去翻一翻旧档案。

1.2 知识库在这里扮演的角色

这里说的知识库不是企业 wiki 那种文档堆,而是经过清洗、分块、向量化之后,可以被语义检索命中的数据集合。历史工单就是最重要的知识来源,因为它们记录了真实发生过的故障、排查过程、最终解决方案。

整个链路可以这样理解:老员工带新人的时候,新人口头禅是“我以前又没处理过,我怎么知道”。你去帮他接知识库,就好比给新人的工位边上放了一个柜子,里面整整齐齐码着历年的工作笔记,柜子上贴了标签。新人接到问题先去翻笔记,找到相似记录再组织语言回答。RAG 在这里起到的就是这个“翻笔记”的检索作用。

这个类比里还有个容易被忽略的点:光有柜子不行,笔记得整理过。如果历史工单本身是一堆“问题描述有、处理结果空”的废单,Agent 翻出来的也只能是废话,下面第 3 部分会专门讲这个。

1.3 三种常见接法的对比与选择

有人问,接历史工单一定要搞 RAG 吗?我在做选型的时候把市面上的路子捋了一遍,主要有三种。

第一种是纯上下文硬塞。把最近若干条工单当成对话背景直接拼进 prompt。问题显而易见:工单稍微多点就超 token 上限,而且大模型真的不会在一百条工单里精确找到那条最相关的。我测试过把二十条工单塞进去,回答效果远不如检索后只塞五条。

第二种是关键词模糊匹配。用 SQL 的 LIKE 或者 Elasticsearch 的全文检索把工单捞出来,再拼给模型。对报错码、设备型号这类标准化字段好用,但对“网老是掉线”“WiFi 不稳定”这种口语化描述就抓瞎。

第三种是 RAG,即语义向量检索配合关键词检索的混合方案。既能靠向量模型理解“开不了机”和“无法启动”是同一个意思,又能靠关键词精确锁定“E02”这种编号。这也是我最终采用的方式,下面的内容基本都是围绕这条路展开。

接法语义理解硬编码字段命中成本适用阶段
上下文硬塞靠大模型自身差高几十条工单的临时演示
关键词匹配无好低字段规范、描述模板化
RAG 混合检索好好中历史工单多、描述口语化

2. 技术选型:Dify 还是自建 RAG,我最后怎么定下来的

2.1 Dify 流水线很省事,但卡在内网部署和自定义检索

项目最开始,我确实试过 Dify 的知识库流水线。Dify 把文档上传、分段、向量化、检索、Agent 编排这一整套流程都做了可视化,不用写多少代码就能跑通一个带知识库的问答 Agent。如果是内部小范围验证,我推荐你也先从这个开始,能省掉很多重复造轮子的时间。

但我在这个项目里被两个问题卡住了。第一,工单系统在内网,Dify 的部署位置和数据同步要反复权衡,我不想把工单数据倒腾到外部环境。第二,也是最关键的,业务上要求检索的时候必须带过滤条件——比如用户报的是财务部那台打印机,就只应该查“财务打印机”组的工单;用户提到了错误码 E02,就应该优先把带 E02 的工单捞出来。Dify 默认知识库检索对这种细粒度控制不顺滑,后面我还是拉了一条自建 RAG 的服务。

这不是说 Dify 不好,而是选型要看场景。如果你的需求是“有一批文档,让 Agent 对着文档回答”,Dify 完全够用;如果你的需求是“现有工单系统,每天增量同步,检索需要多层过滤”,自建更可控。

2.2 中文工单场景下的 Embedding 模型选择

选定自建之后,第一个要定的就是 Embedding 模型。我看不少项目上来就习惯性用海外通用 Embedding 接口,但在中文工单这个场景里,效果真的不一定好。工单文本是口语化、错别字多、术语混杂的,比如“wifi 老段”“无法链接”“不上网”这种写法,通用模型处理起来容易漂。

我最后选了 bge-m3 这一档的中文模型,本地部署。用一千条标注过的工单做过对比,同一个 query 在 TOP5 命中率上,中文专用模型比通用英文模型高出差不多十几个百分点。如果你的工单里有大量设备型号和报错码,建议优先选对中文支持好的模型。

提示:Embedding 模型不要选当前最大的,而是要选和你领域文本最贴近的。先拿出工单里两百条真实数据做一次批量向量化,再用测试 query 手工查 TOP10 效果,这一步值得花一个下午做,后面省很多事。

2.3 向量库选型:从 pgvector 到 Qdrant

向量库存哪,我一开始在 pgvector、Chroma、Qdrant、Milvus 之间犹豫。最后选型逻辑很简单:数据量级决定复杂度。历史工单清洗后大概三万条,每条拆成一到三个分块,也就是五万左右的向量规模。这个量级 pgvector 完全扛得住,而且可以直接用团队已有的 PostgreSQL,少维护一套中间件。

三万条以下,我建议 pgvector;十万条以上,再考虑 Qdrant 或 Milvus 这类专用向量库。Chroma 在快速原型验证阶段挺好用,但做持续增量更新和权限过滤时,关系型数据库的天然优势就出来了——你能按部门、设备型号、工单状态直接写过滤 SQL。

2.4 混合检索:不能把宝全押在语义向量上

选完 Embedding 和向量库之后,最容易犯的错误是只用向量检索。我第一版也这样,后来发现工单场景有个特殊性:大量工单里包含“E02”“P1108”“固件 2.3.1”这种硬编码信息。这些字符串在语义向量空间里经常被忽略——模型可能觉得“固件”和“系统”意思相近,就把没有 E02 的工单也召回排在前面,导致准确率一塌糊涂。

解决方式是混合检索:向量召回一条线,BM25 关键词召回一条线,两条线取并集再合并打分。这样对“打印机报 E02”这个 query,vector 负责理解“打印机、报错”,BM25 负责精确锁定“E02”,两边互补。

在实际实现里,我用了一个很朴素的服务,同时挂了两个检索接口,查询时并行调用再融合。代码不长,但效果立竿见影,下一节直接讲这套链路怎么落地。

3. 工单数据清洗:脏数据入库之前要过的三道关

3.1 常见脏数据:重复工单、空字段、敏感信息

接历史工单这件事,百分之八十的工作量其实不在检索算法上,而在数据清洗上。我拿到工单导出文件的第一反应是:这和宣传里的“干净知识库”差距太远了。常见问题差不多四类:同一条故障被反复提单导致重复;工单只有“问题描述”没有“解决方案”,处理结果字段是空的;用户电话、身份证号、工号散落在描述文本里;还有大量“已取消”“已关闭无处理”状态的无效单。

处理逻辑是这样的。先按工单去重,同一客户同一设备同一问题描述只保留最后完结的一条;再把空解决方案的工单剔除出 RAG 索引,当然可以在前端保留,只是不让 Agent 引用;然后做脱敏,把手机号用正则替换成占位符;最后按状态过滤,只保留“已解决”和“已关闭”两类。

注意:脱敏这一步一定不能省。RAG 检索出来的工单会作为引用内容拼进 prompt 再给大模型生成回答,如果你没脱敏,Agent 会把客户的手机号原封不动输出出来,这就是事故。

3.2 分块策略:按“工单事件”切而不是按固定字数切

知识库分块这事,做文档问答的人习惯按固定字符数切,512 字或 1024 字。但工单数据不能这么干。一条工单本身就是一个完整事件,有现象、排查、结论三段逻辑,拆散了再去 embedding,语义完整性就被破坏了。

我的做法是:默认一条工单就是一个分块,标题加描述加解决方案拼起来作为索引内容。对特别长的工单,按“现象描述”和“解决过程”两段切,而不是硬切成均匀小块。这样检索的时候,如果命中“现象”部分,Agent 能看出用户遇到什么问题;如果命中“解决过程”,Agent 能直接抄答案。

这里有个实操细节:拼索引内容的时候,把工单号和业务线这些元数据也拼进去。比如“【工单】#10234【业务线】办公网络【设备】TP-LINK 路由器”,这个大前缀能让向量检索更精准。原理很简单,模型对“这个文本属于网络故障案例”的语义判断有几个强锚点。

3.3 元数据设计:保留设备型号、报错码和工单号

工单入库时我建了一个核心字段表,这决定了检索阶段能不能做精确过滤,也决定了 Agent 生成的回答有没有“出处”。我的表结构大致是这样:

字段示例作用
ticket_id#10234回答里带上工单号,业务方可追溯
title财务部网络频繁断线标题本身是高价值检索内容
description用户反馈每日下午断网多次现象描述,用于语义检索
solution更换交换机端口后恢复最终方案,回答的核心素材
device_modelTL-WAR1200L设备维度的精确过滤
error_codeE02报错码维度的精确过滤
department财务部部门维度的过滤
status已解决只用已解决工单参与回答

有了这些字段,retrieval 阶段就可以做组合过滤。用户说“财务打印机报 E02”,query 解析出两个条件,一个是 Wasser 部门的财务室,一个是错误码 E02,检索时直接拼上过滤条件。这一套下来,召回精度完全不一样。

4. 检索链路实现:让 Agent 真正“有依据”

4.1 入库与索引流程的完整步骤

数据清洗完,入库流程我大概分七步走:读工单表、清洗字段、切分内容、生成 embedding、写向量库、写元数据表、同步搜索索引。其中生成 embedding 是最耗时的,三万条工单跑 bge-m3 大概要小半天,建议用带 GPU 的机器或跑批任务,不要占着开发机跑。

核心代码不复杂,pgvector 的插入就是一个带 embedding 字段的 insert。真正值得写清楚的是增量同步逻辑。工单系统每天都会进新单,我写了一个每十五分钟跑一次的定时任务,只拉“状态从处理中变为已解决”的单子增量入库。不然新问题反复出现,Agent 却查不到最新处理方案,知识库就成了过期知识库。

4.2 检索时的 query 解析、召回、重排序

检索链路我拆成了三步,每一步都值得单独设计。

第一步是 query 解析。用户不会老老实实把“设备型号、报错码、部门”三个参数说全,往往只说一句“我这边电脑开不了机”。我用大模型做了一次轻量信息抽取,让 LLM 从原话里抽出三个关键项:设备型号、报错码、业务线。抽不出来的就留空,留给后续混合检索去兜底。

第二步是召回。向量检索和 BM25 并行跑,各取二十条,合并去重。这里我用了一个简单的调和加权打分:score = 0.6 * vector_score + 0.4 * keyword_score,再做一次字段命中加分——命中 error_code 的加 0.5,命中 device_model 的加 0.3。

第三步是重排序。二三十条候选直接塞给 LLM 生成答案,token 太浪费且噪声大。我先用一个轻量 Rerank 模型把候选压到五条,再进 LLM。没有 Rerank 模型的话,用上面的加权打分公式也够用,只是精度差一些。

4.3 定义检索工具,约束 LLM 的引用格式

Agent 不直接读向量库,而是通过一个检索工具去查。我给 Agent 定义了一个函数,叫 search_similar_ticket,参数包括 query、device_model、error_code、department、limit。Agent 判断用户的问题需要查历史工单时,会自己决定填哪些参数。

这一步很有讲究。把检索封装成工具而不是把全部工单塞进上下文,既控制了 token 成本,也让 Agent 的行为可解释——它“决定查一下历史”,再“基于查到的工单来回答”,这中间有清晰的推理过程。

回答的引用格式也必须约束。我在 system prompt 里明确要求:如果参考了历史工单,必须引用工单号和结论,格式是“根据历史工单 #10234,该问题此前定位到……”。不让 Agent 凭空说“根据历史经验”,因为“历史经验”四个字查无实据,工单号才是有依据的证明。

5. 实测效果与翻车调试记录

5.1 语义检索失效的典型场景

上线第一周就翻了一次车。用户报“电脑一直转圈打不开网页”,Agent 召回的第三条工单才勉强相关,回答参考了一条“网页加载慢,更换 DNS”的工单,看完只能说方向接近,不是最佳答案。

事后排查发现两个原因。一是这句话里“打不开网页”这种口语,embedding 后的向量离工单里的标准技术描述“无法访问外网”距离并不近。二是用户没有提供任何设备型号,query 解析阶段三个字段全空,向量检索只能靠整句语义去大海捞针。

对策是给关键词检索加重权重,同时维护了一张同义词表,把口语说法映射到工单里的标准说法。“打不开网页”映射“无法访问外网”,“开不了机”映射“无法开机”,“网老断”映射“网络不稳定”。这套规则不复杂,但把 TOP5 命中率拉高了一截。纯语义向量不是万能的,工单场景里口语到技术术语的这一跳,需要规则去补。

5.2 多轮对话上下文丢失问题

另一个高频事故出现在多轮对话。用户先问“我的网断了”,Agent 查了网络故障工单;用户接着问“用网线也不行,多久能来修”,Agent 把这句单独解析,查出来的全是“维修时效”相关的通用工单,回答直接偏了。

问题出在我只对当前这轮 query 做了解析和检索,没有利用前文。修正方案是引入 query 重写:在检索之前,先把多轮对话的历史压缩成一句话。用户第二句“用网线也不行”,重写后变成“设备使用网线也无法联网,用户请求上门维修时效”。这里面“设备、网络故障、维修时效”三个检索锚点都清晰了,再去做 query 解析和混合检索,效果就对了。

这个坑很值得提,因为 Agent 项目做到后面,多轮对话几乎都会遇到。记住一条原则:检索的输入不应该是用户最后一句话,而应该是“带着上下文重写后的完整意图”。

5.3 并发与延迟:Agent 扛并发的实测数字

最后讲讲大家关心的性能。上线前压测,纯 LLM 回答大概 1.2 秒;接上 RAG 检索链路后,整体延迟到了 1.6 到 2 秒,其中检索和重排序占了 300 到 500 毫秒,在客服场景完全可以接受。

并发方面,最开始直连云端 Embedding 接口,十几个并发请求就把延迟拖得很高。后来把 Embedding 和 Rerank 两个模型都做到本地推理,又加了一级缓存——同一问法在十分钟内直接命中缓存,不再重复检索。压测从 10 并发提到了 50 并发,业务峰值足够用了。如果你想扛更高,往向量库前面加一层 Redis 缓存,或者把重排序模型拆到单独服务,都能再顶一段。

注意:延迟不是越短越好,准确性才是。我调试中把检索 TOP10 限制改成 TOP3,延迟确实低了不少,但遇到表述模糊的工单,答案质量掉得厉害。平衡点要靠业务口径来定,别一味追低延迟。

最后说个感受。整条链路跑通之后,我非常确定一件事:Agent 的能力上限,其实是由数据质量和检索精度决定的,而不是模型本身。如果你的历史工单里已经沉淀了靠谱的解决方案,接上 RAG 之后,Agent 回答得确实像个干了三年的老员工;反过来,工单库一塌糊涂的话,用再大的模型也只是把噪声包装成自信。所以第一件事永远是拉着业务方把历史工单里真正能用的那部分整理清楚,这一步的优先级,排在所有选型之前。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 22:17:08

端侧模型才是未来?设备即环境下的AI落地与工程挑战

「设备即环境」这个说法,最近又被推到了风口上。一家北大系公司在各种场合反复强调这句话,核心意思其实很朴素:AI的下一轮竞争,不会只在云端的数据中心里决定,而是会发生在每个人的手机、电脑、汽车和智能家居里。用他…

作者头像 李华
网站建设 2026/10/2 22:17:03

Hindsight:用后验监督突破深层网络训练瓶颈,让每一层都有方向感

第一次看到“Hindsight”这个名字,我以为是哪个日志分析工具或者复盘软件。翻到论文首页才发现,这是 CVPR 2023 上关于深度网络训练方法的一项工作。名字起得很妙:hindsight 是“后见之明”,而它想解决的核心问题恰恰是——为什么…

作者头像 李华
网站建设 2026/10/2 22:16:44

高空抛物检测数据集实战:VOC+YOLO双格式与YOLOv8训练全流程

简介:这份资源面向计算机视觉初学者与安防场景研究者,提供高空抛物检测的完整数据集与配套训练成果,解决从零采集、标注到模型落地周期长的问题。包内共807个文件,约377.94MB,包含259张jpg图像、259个xml标注与261个tx…

作者头像 李华
网站建设 2026/10/2 22:16:43

高空抛物数据集VOC+YOLO格式259张:yolov8训练与视频抽帧实战

简介:本资源面向计算机视觉入门与安防场景研究者,提供高空抛物检测的完整数据集与配套训练成果。数据来源于6段简短抛物视频,逐帧截取259张图像并用labelImg完成标注,同时给出VOC与YOLO两种格式,方便直接接入不同检测框…

作者头像 李华
网站建设 2026/10/2 22:16:40

ReentrantLock实战指南:从可重入原理到AQS,彻底搞懂并发锁

身边总有人问我,Java并发编程那么多锁,synchronized用了十几年,为什么还要搞出一个ReentrantLock重入锁?这俩到底啥区别?还有更扎心的问题——明明我用了ReentrantLock,线上还是出现了偶发的状态错乱&#…

作者头像 李华
网站建设 2026/10/2 22:11:54

Strix Halo迷你主机部署halogen-flash-server:实测性能与调优指南

先交代一下背景:我盯手头这台 Beelink Strix Halo 迷你主机盯了挺久,它用的是 AMD 新一代的 Strix Halo 平台处理器,这类小主机最大的卖点就是把大容量统一内存和高带宽打包塞进一个方盒子。之前我一直拿它跑 Stable Diffusion 和本地 RAG&am…

作者头像 李华