news 2026/9/29 4:05:50

让Agent有依据地查相似问题:历史工单与知识库接入RAG实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
让Agent有依据地查相似问题:历史工单与知识库接入RAG实战

1. 为什么单靠大模型回答不了工单问题

做过客服系统或者内部工单系统的人都有一个共同体会:用户提的问题,十有八九不是全新的。同一个报错、同一个操作疑问,可能上个月已经有人问过,上季度已经有人解决过,甚至解决方案就躺在某个同事的历史回复里。但如果没有一套机制把这些历史沉淀接进 Agent,它就只能靠大模型的“通用常识”硬答,结果要么答得笼统,要么直接编。

我最早做 Agent 问答的时候踩过这个坑。当时接了一个内部技术支持场景,用户问“批量导入失败提示编码错误怎么办”,Agent 张口就是“请检查文件编码格式,建议使用 UTF-8”。听起来没毛病,但实际业务里这个报错是因为导入模板的某一列被 Excel 自动转成了日期格式,跟编码根本没关系。历史工单里明明有三条一模一样的案例,解决方案写得很清楚,但 Agent 看不到,只能瞎猜。

这就是标题里说的核心问题:让 Agent 有依据地查相似问题。拆开看是两件事,一是“有依据”,二是“查相似”。有依据意味着回答不能凭空生成,必须挂靠在可信的知识源上;查相似意味着要能从历史工单和知识库里捞出真正相关的内容,而不是关键词碰巧对上的噪音。

这一篇我就围绕这个场景,把历史工单接入、知识库构建、检索链路设计、相似度判断、结果融合这几块拆开讲。涉及的工具选型我会说明为什么这么选,涉及参数的地方我会给出计算或者经验值,涉及代码的地方给可直接跑的片段。适合正在做 Agent 落地、RAG 知识库、客服工单系统的朋友参考,新手也能跟着捋清楚整条链路。

2. 整体方案设计与核心思路拆解

2.1 两类数据源的本质差异

历史工单和知识库虽然都叫“知识”,但它们的结构、质量、更新频率完全不同,不能混在一起处理。

历史工单是过程性数据。一条工单包含用户原始描述、客服的追问、最终的解决方案、可能还有附件和截图。它的价值在于真实、具体、带上下文,缺点是口语化严重、格式混乱、有大量无关对话。比如用户可能写“那个东西又不行了”,你得结合前面的对话才知道“那个东西”指的是什么。

知识库是结论性数据。它通常是人工整理过的 FAQ、操作手册、故障排查指南,结构清晰、表述规范,缺点是覆盖面有限,更新滞后,很多长尾问题没有收录。

我的做法是两条链路分开处理,检索时再融合。原因很简单:如果强行把工单塞进知识库的格式里,要么丢失上下文,要么清洗成本高到无法维护。分开处理后,工单走“相似问题匹配”,知识库走“语义检索”,最后按相关度加权合并。

2.2 为什么选 RAG 而不是微调

有人会问,既然有历史工单,为什么不直接拿这些数据微调一个模型?我的经验是,工单场景不适合微调,原因有三个。

第一,工单是持续增量的。今天解决的问题,明天可能又出现,微调一次成本高、周期长,跟不上业务变化。RAG 只需要把新工单写进向量库,立即可检索。

第二,微调会让模型“记住”具体答案,但工单里的解决方案往往带有时效性和条件性。比如“重启服务即可”这个方案,可能只适用于某个版本,微调后模型会对所有版本都这么答,反而危险。RAG 可以把工单的元信息(时间、版本、产品线)一起带出来,让 Agent 判断适用性。

第三,可解释性。RAG 检索出来的内容可以展示给用户看,“根据工单 #12345 的解决方案”,用户能追溯来源。微调模型的回答是黑盒,出了问题没法排查。

所以这个场景我坚定走 RAG 路线,而且是Agentic RAG——不是一次检索就完事,而是让 Agent 自己决定要不要查、查哪个源、查几次、结果够不够。

2.3 检索链路的分层设计

整个链路我分成四层,从粗到细逐层过滤。

第一层是召回层,用向量检索从工单库和知识库里各捞一批候选,数量可以放宽,比如各取 top 20。这一层追求高召回,不怕多,怕漏。

第二层是粗排层,用轻量模型或者规则做初步筛选,去掉明显不相关的。比如用户问的是“登录问题”,召回里混进了“支付问题”的工单,靠关键词和类目就能过滤掉。

第三层是精排层,用交叉编码器或者更强的相似度模型对候选做精细打分,取 top 5 左右。这一层追求准确率,宁缺毋滥。

第四层是融合层,把工单结果和知识库结果按权重合并,同时考虑时效性、解决率、来源可信度等因素,最终给 Agent 一个排序后的依据列表。

这个分层的好处是每一层职责单一,出了问题容易定位。我见过一些方案把召回和排序揉在一起,结果召回率上去了准确率崩了,或者反过来,调参的时候完全不知道动哪里。

2.4 相似问题的判断标准

“相似”这个词很模糊,得定义清楚。我的标准是三个维度同时满足才算相似。

语义相似:用户描述的问题本质是同一个。比如“导入报错”和“上传失败”可能语义相近,但如果一个是编码问题一个是权限问题,就不算相似。

场景相似:产品线、版本、操作路径要匹配。同一个报错在不同产品线下的原因可能完全不同,不能直接套用。

解决方案可迁移:历史工单的解决方案在当前场景下仍然有效。有些方案依赖已经下线的功能,或者已经被新版本修复,这种即使语义相似也不能用。

实际操作中,我会给每个维度一个权重,综合打分超过阈值才认为相似。阈值不是拍脑袋定的,而是拿一批标注数据跑出来的。我一般会标 200 到 500 条样本,看准确率和召回率的平衡点在哪里。

3. 历史工单接入的实操细节

3.1 工单数据的清洗与结构化

工单原始数据直接拿来用效果很差,必须先清洗。我通常按这几个步骤走。

第一步是去噪。工单里大量内容是“你好”“在吗”“谢谢”这类寒暄,还有客服的模板回复。这些对检索没有价值,反而会稀释语义。我的做法是用规则加小模型结合的方式过滤,规则处理明显的模板句,小模型判断剩余内容的信息密度。

第二步是抽取核心字段。一条工单我至少抽这几个字段:问题描述、问题分类、产品线、版本号、解决方案、解决状态、创建时间、解决时长。问题描述取用户的首条有效描述加上客服确认后的复述,解决方案取最终标记为“已解决”的那条回复。

第三步是拼接检索文本。不是把所有字段拼在一起,而是有策略地组合。我的做法是:问题描述 + 问题分类 + 产品线 + 解决方案摘要。解决方案摘要单独用一个小模型生成,控制在 100 字以内,保留关键操作步骤。

这里有个细节,解决方案不要全文拼进去。因为解决方案往往很长,拼进去会稀释问题描述的语义权重,导致检索时匹配到的是解决方案里的词而不是问题本身的词。我试过全文拼接,召回的相关性明显下降,改成摘要后好很多。

3.2 向量化模型的选择与考量

向量化模型直接决定召回质量,选型不能随便。我评估过几类方案。

通用 embedding 模型,比如常见的开源中文 embedding,优点是开箱即用,缺点是对方言、行业术语、口语化表达的处理不够好。工单里用户经常用内部黑话,通用模型可能理解不了。

领域微调的 embedding 模型,拿工单数据继续训练,效果明显提升,但需要标注数据,成本高。如果工单量足够大(比如十万条以上),值得投入。

混合方案,用通用模型打底,对识别出的行业术语做同义词扩展后再向量化。这个方案成本低,效果也不错,我目前用得比较多。

具体选型时我会看几个指标:在自建测试集上的 recall@20、推理速度、向量维度。维度不是越高越好,768 维和 1024 维在实际场景里差距不大,但存储和检索成本差不少。我一般选 768 维,平衡效果和成本。

注意:embedding 模型一旦选定,后续所有数据都要用同一个模型向量化。中途换模型会导致新旧向量不在同一空间,检索直接失效。如果必须换,要全量重新向量化。

3.3 工单库的增量更新机制

工单是每天新增的,向量库必须支持增量写入。我的做法是维护一个同步任务,每小时跑一次,拉取新增和更新的工单,清洗后向量化写入。

这里有个坑,工单会被修改。比如客服一开始填的解决方案后来被修正了,或者工单被重新分类了。如果只做增量插入,旧向量还在库里,检索时会召回过期内容。所以同步任务要支持 upsert,用工单 ID 作为主键,存在就更新,不存在就插入。

另外,已关闭且超过一定时间的工单可以考虑归档。不是删除,而是标记为低优先级,检索时降权。因为太老的工单解决方案可能已经过时。我一般把一年以上的工单权重降到 0.5,两年以上的降到 0.3。

3.4 工单检索的字段权重设计

检索时不同字段的权重不一样。问题描述的权重最高,因为用户问的是问题;解决方案的权重次之,因为要匹配可用的答案;分类和产品线作为过滤条件,不直接参与语义打分。

我的权重配置大概是:问题描述 0.6,解决方案摘要 0.3,分类 0.1。这个比例是调出来的,不同业务可能不一样。调整方法是拿一批测试 query,看不同权重下的 recall 和 precision,找平衡点。

如果工单量很大,还可以加一层类目预过滤。用户提问时先判断属于哪个类目,只在该类目下检索,能大幅提升速度和准确率。类目判断可以用小模型或者关键词规则,成本很低。

4. 知识库的构建与检索优化

4.1 知识库内容的组织方式

知识库和工单不一样,它是人工整理的,所以组织方式直接影响检索效果。我见过很多知识库就是把文档一股脑切块扔进向量库,效果很差。问题出在切块方式上。

我的做法是按语义单元切块,不是按固定字数切。一个语义单元可以是一个完整的问答对、一个操作步骤、一个故障现象加解决方案。切块时保留标题和层级信息,因为标题往往包含关键语义。

比如一个故障排查文档,结构是“问题现象 - 可能原因 - 排查步骤 - 解决方案”,我就切成四块,每块带上父级标题。这样检索时“排查步骤”这块能独立被召回,不会和“解决方案”混在一起。

切块大小我一般控制在 200 到 500 字。太短语义不完整,太长噪音多。超过 500 字的段落再按句子边界切分,保证每块至少包含一个完整意思。

4.2 知识库的元数据设计

元数据是知识库检索的隐藏武器。很多人只存文本和向量,检索时只能靠语义,效果有限。加上元数据后,可以做过滤、加权、排序,效果提升明显。

我通常给每个知识块打这些元数据:所属文档、文档类型、适用产品线、适用版本、更新时间、可信度等级、被引用次数。

可信度等级是人工标的,比如官方文档标 5,内部经验分享标 3,用户投稿标 2。检索时可信度高的加权。被引用次数是系统统计的,被引用越多说明越有用,也加权。

适用版本这个字段特别重要。知识库里的内容可能只适用于某个版本,如果用户问的是新版本,旧版本的方案就不能直接用。检索时把版本作为过滤条件,或者作为降权因子。

4.3 混合检索策略:向量加关键词

纯向量检索有个问题,对精确匹配不敏感。比如用户问“错误码 E5021”,向量检索可能召回一堆语义相近但错误码不同的内容。这时候关键词检索就派上用场了。

我的做法是向量检索和关键词检索并行跑,然后融合结果。关键词检索用 BM25 或者简单的倒排索引,对错误码、产品名、专有名词这类精确信息效果好。

融合时用 RRF(Reciprocal Rank Fusion)算法,简单说就是把两个结果列表的排名做倒数加权求和。这个算法不需要调参,效果稳定,我一直在用。

具体公式是:对每个文档,分数等于它在各列表中的排名倒数的和。比如文档 A 在向量检索排第 3,在关键词检索排第 5,分数就是 1/3 + 1/5 = 0.533。按分数排序取 top。

4.4 检索结果的重排序

召回之后要重排,这一步决定最终给 Agent 的依据质量。我用的是交叉编码器,把 query 和候选文档拼在一起输入模型,输出相关性分数。

交叉编码器比向量相似度准,因为它能看到 query 和文档的交互,而不是各自独立编码后算距离。缺点是慢,所以只用在精排阶段,候选数量控制在 20 以内。

重排时除了相关性分数,我还会叠加几个因子:时效性因子(越新越高)、可信度因子(等级越高越高)、解决率因子(工单的解决状态)。最终分数是相关性乘以这些因子的加权和。

权重怎么定?我的经验值是相关性 0.7,时效性 0.1,可信度 0.1,解决率 0.1。这个比例可以根据业务调整,比如客服场景更看重解决率,可以调到 0.2。

5. Agent 如何调用检索能力

5.1 工具定义与调用时机

Agent 要查历史工单和知识库,得把检索封装成工具。我一般定义两个工具:search_tickets和search_knowledge,参数都是 query 和可选的过滤条件。

调用时机很关键。不是用户每问一句都去查,那样又慢又浪费。我的策略是让 Agent 先判断问题类型:如果是通用常识或者闲聊,不查;如果是业务问题,查;如果问题描述模糊,先追问澄清再查。

判断逻辑可以写在系统提示词里,也可以用一个小分类模型。我倾向用提示词,因为灵活,改起来不用重新训练。

工具返回的结果要结构化,包含内容、来源、相关度分数、元数据。Agent 拿到后不是直接复述,而是综合多条结果生成回答,并标注来源。

5.2 多轮检索与查询改写

用户的问题往往一次检索不到位。比如用户问“导入失败怎么办”,这个描述太泛,检索出来的结果可能覆盖多种失败原因。这时候 Agent 应该能改写查询,做多轮检索。

我的做法是让 Agent 先做一次宽泛检索,看结果的相关度分布。如果最高分低于阈值,说明没找到好结果,Agent 就根据已有结果里的关键词改写查询再查一次。比如第一次查“导入失败”,发现结果里频繁出现“编码”“格式”“权限”,就改写成“导入失败 编码错误”再查。

查询改写也可以用大模型做,给它原始 query 和第一轮结果,让它生成更精确的查询。这个方式效果好但成本高,我一般限制最多改写两次,避免无限循环。

5.3 结果融合与冲突处理

工单和知识库的结果可能冲突。比如知识库说“重启服务”,工单说“清除缓存后重启”,哪个对?我的处理原则是:知识库优先,因为它是人工整理的;但如果工单的解决率很高且时间更新,可以覆盖知识库。

具体做法是给两类结果分别打分,然后比较。如果知识库最高分明显高于工单最高分,用知识库;如果接近,两个都呈现给 Agent,让它综合判断;如果工单明显更高,用工单但标注“来自历史工单,供参考”。

冲突处理还要考虑版本差异。如果知识库是旧版本的方案,工单是新版本的,即使知识库分数高也应该降权。这个靠元数据里的版本字段判断。

5.4 引用与可追溯性

Agent 的回答必须能追溯到来源,这是“有依据”的核心。我的做法是要求 Agent 在回答里标注引用编号,比如 [1][2],然后在回答末尾列出对应的工单号或文档链接。

这样用户能点进去看原文,确认方案是否适用。同时系统也能统计哪些知识被引用得多,反过来优化知识库。

实现上,工具返回结果时带上唯一 ID,Agent 生成回答时把 ID 带上,后处理时替换成可点击的链接。如果 Agent 没带引用,后处理可以强制补上,或者标记为“未引用来源”供人工审核。

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

6.1 检索召回率低的排查思路

召回率低是最常见的问题,表现是 Agent 找不到相关工单,回答质量差。排查我按这个顺序走。

先看向量化是否正常。拿一条已知相关的工单,手动向量化后和 query 向量算相似度,如果分数很低,说明 embedding 模型不适合这个场景,考虑换模型或者做领域微调。

再看切块是否合理。如果工单被切得太碎,语义不完整,检索自然不准。检查切块后的文本,看是否每块都有完整意思。

然后看query 本身。用户的问题可能太短或者太模糊,比如“不行”“报错”,这种 query 向量化后信息量太少。解决办法是做 query 扩展,用大模型把短 query 补全成完整描述再检索。

最后看过滤条件。有时候是过滤条件太严,把相关结果过滤掉了。比如版本过滤,如果工单没标版本,就被过滤了。这种情况要把过滤改成降权而不是硬过滤。

6.2 相似问题匹配不准的调优

匹配不准分两种:召回了不相关的,或者没召回相关的。

召回不相关的,通常是语义漂移。比如“登录失败”和“支付失败”在向量空间里可能很近,因为都有“失败”。解决办法是加场景过滤,用产品线或类目把范围缩小。另外可以在 embedding 时加入场景信息,比如把产品线拼在文本前面一起向量化。

没召回相关的,通常是表述差异大。用户说“登不上去”,工单里写“无法登录”,语义相近但字面不同。这种情况靠同义词扩展解决,维护一个业务同义词表,检索时把 query 里的词替换成标准词再查。

调优是个迭代过程,我一般每周看一批 bad case,分析原因,针对性优化。积累下来,准确率能提升 20 到 30 个百分点。

6.3 性能瓶颈与优化手段

检索链路的性能瓶颈通常在向量检索和重排序两步。

向量检索慢,一般是索引没建好。用 HNSW 或者 IVF 索引能大幅加速,代价是轻微损失召回率。我一般用 HNSW,参数 M 设 16,efConstruction 设 200,efSearch 设 100,这个配置在千万级数据下延迟能控制在 50ms 以内。

重排序慢,是因为交叉编码器计算量大。优化手段是减少候选数量,粗排后只留 10 到 20 条给精排。另外可以用小模型做精排,比如蒸馏过的交叉编码器,速度快很多,效果损失不大。

还有一个容易忽略的点是并发。多个用户同时查询时,检索服务要能扛住。我的做法是检索服务独立部署,加缓存,相同 query 短时间内直接返回缓存结果。

6.4 常见问题速查表

问题现象可能原因排查方法解决手段
Agent 答非所问检索结果不相关检查 top 结果的相关度分数优化 embedding 或加过滤条件
找不到历史工单召回率低手动测试已知相关工单的相似度换模型、改切块、query 扩展
回答没有引用工具返回格式不对检查工具返回是否带 ID修正返回结构,后处理补引用
检索很慢索引或重排瓶颈分别测向量检索和重排耗时建 HNSW 索引,减少精排候选
新旧方案冲突版本元数据缺失检查工单和知识库的版本字段补全元数据,检索时按版本降权
短 query 效果差query 信息量不足看短 query 的检索结果用大模型做 query 扩展

实操心得:我习惯在检索服务里加一个 debug 模式,输入 query 后返回完整的召回、粗排、精排结果和每步分数。排查问题时直接看这个,比猜快得多。

7. 我踩过的坑和几条实用建议

第一个坑是过度依赖向量检索。早期我觉得向量检索万能,结果发现对错误码、订单号这类精确信息完全不行。后来加了关键词检索做混合,效果才稳定。所以别迷信单一方案,混合检索是标配。

第二个坑是忽略元数据。一开始只存文本和向量,检索时没法过滤和加权,效果一直上不去。补上元数据后,同样的向量模型,准确率提升明显。元数据的投入产出比很高,值得花时间设计。

第三个坑是不做 bad case 分析。有段时间我觉得效果还行就不管了,结果用户投诉越来越多。后来建立每周 bad case 复盘机制,才发现很多问题是系统性的,比如某类工单从来没被正确召回。持续优化比一次调好更重要。

几条建议:embedding 模型选型时一定要用自建测试集评估,别只看榜单;切块策略要根据业务调整,没有通用最优解;检索阈值要动态调整,不同类目可以用不同阈值;Agent 的提示词里要明确要求引用来源,否则它经常偷懒不标。

最后分享一个小技巧:我会定期把检索日志里 Agent 没找到依据的问题捞出来,人工看一遍,把能补进知识库的补进去,把能优化检索的记下来。这个习惯坚持下来,知识库和检索质量会形成正向循环。

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

复盘四步法:从总结到经验资产化的团队落地指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 4:00:39

ChatGPT 与 GitHub Copilot Chat 哪个更强?用 TaoToken 统一 Key 实测对比

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/29 4:00:33

Harness:AI Agent 的“操作系统”与 TaoToken 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华