
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 模型拿工单数据继续训练效果明显提升但需要标注数据成本高。如果工单量足够大比如十万条以上值得投入。混合方案用通用模型打底对识别出的行业术语做同义词扩展后再向量化。这个方案成本低效果也不错我目前用得比较多。具体选型时我会看几个指标在自建测试集上的 recall20、推理速度、向量维度。维度不是越高越好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 或者简单的倒排索引对错误码、产品名、专有名词这类精确信息效果好。融合时用 RRFReciprocal 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]然后在回答末尾列出对应的工单号或文档链接。这样用户能点进去看原文确认方案是否适用。同时系统也能统计哪些知识被引用得多反过来优化知识库。实现上工具返回结果时带上唯一 IDAgent 生成回答时把 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 设 16efConstruction 设 200efSearch 设 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 没找到依据的问题捞出来人工看一遍把能补进知识库的补进去把能优化检索的记下来。这个习惯坚持下来知识库和检索质量会形成正向循环。