ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于规则与大模型混合的Issue智能分诊助手设计与实现

基于规则与大模型混合的Issue智能分诊助手设计与实现

1. 为什么需要给 Issue 做分诊

维护过开源项目的人都有一个共同感受:真正消耗精力的往往不是写代码,而是处理源源不断涌进来的 Issue。一个新 Issue 提交上来,里面可能混杂着 Bug 报告、功能请求、使用咨询、重复提问、甚至完全跑题的灌水内容。如果每一个都靠人工逐条阅读、打标签、分配负责人、回复追问,一天下来光"分诊"就能把维护者的耐心磨光。

我维护过几个中小型项目,高峰期一天能收到二三十条 Issue。最开始我坚持每条都亲自看,结果就是白天处理 Issue,晚上才有时间写代码,长期下来非常疲惫。后来我开始琢磨:这些"第一轮脏活"有没有可能交给 AI 来做?所谓"第一轮脏活",指的是那些重复性高、判断逻辑相对固定、但又必须有人做的初步处理工作——判断类型、打标签、识别重复、提取关键信息、给出初步回复。这些工作不需要深厚的领域知识,却极其消耗时间。

这个 Issue 分诊助手的核心目标很明确:在人工介入之前,自动完成 Issue 的初步分类、标签建议、优先级判断和回复草稿生成,让维护者打开后台时,看到的是已经整理好的信息,而不是一堆原始文本。它适合所有在维护开源项目、内部工具仓库、或者任何使用 Issue 跟踪系统的团队,尤其适合人手有限、维护者身兼数职的场景。

需要提前说明的是,这个助手不是要取代人的判断,而是把维护者从机械劳动中解放出来。最终的标签确认、优先级调整、是否关闭,仍然由人来做决定。AI 负责的是"把脏活干完,把干净的信息摆到你面前"。

2. 整体设计思路与方案选型

2.1 分诊助手到底要解决哪几个问题

在动手之前,我先把"分诊"这件事拆解成几个独立的子任务,因为不同子任务的技术难度和实现方式差别很大:

  • 类型识别:这条 Issue 是 Bug、功能请求、使用咨询,还是其他?这是最基础的判断,也是后续所有处理的前提。
  • 标签建议:根据类型和内容,建议应该打哪些标签,比如bug、enhancement、question、documentation。
  • 优先级初判:结合关键词、影响范围、是否有复现步骤等信息,给出一个粗略的优先级参考。
  • 重复检测:判断这条 Issue 是否和已有 Issue 高度相似,避免维护者重复劳动。
  • 信息完整性检查:检查提交者是否提供了必要的环境信息、复现步骤、期望行为等,如果缺失就自动追问。
  • 回复草稿生成:针对不同类型,生成一段得体的初步回复,告诉提交者"我们已经收到,正在处理"或者"请补充以下信息"。

把这六个子任务分开看,就会发现它们的实现难度是递增的。类型识别和标签建议相对简单,用规则加轻量模型就能做得不错;重复检测需要向量化比对;回复草稿生成则对语言质量要求较高。我的策略是分层处理:能用规则搞定的绝不上模型,能用小模型搞定的绝不上大模型,把成本和质量控制在合理区间。

2.2 为什么选择"规则 + 模型"的混合方案

一开始我也想过纯用大模型一把梭:把 Issue 内容丢进去,让它输出类型、标签、优先级、回复。实测下来有几个问题。第一是不稳定,同样的输入,模型两次输出的标签可能不一样,这对需要一致性的分诊场景很致命。第二是成本,如果每条 Issue 都调用大模型,量一大费用就上去了。第三是不可控,模型可能给出一些你根本没定义过的标签,导致后续统计混乱。

所以最终我采用的是混合方案:先用规则做快速筛选和兜底,再用模型处理规则搞不定的模糊情况。具体来说,类型识别优先用关键词规则匹配,比如标题里出现"崩溃""报错""无法"等词,大概率是 Bug;出现"希望""建议""能不能支持"等词,大概率是功能请求。规则命中就直接出结果,规则不确定的才交给模型判断。这样既保证了大部分情况下的稳定性和零成本,又保留了处理复杂情况的能力。

标签体系也是同样的思路。我预先定义了一套受控的标签词表,模型只能在词表范围内选择,不允许自由发挥。这样既利用了模型的语义理解能力,又避免了标签泛滥。

2.3 重复检测为什么用向量而不是关键词

重复检测这块我踩过坑。最开始用关键词匹配,比如两条 Issue 标题都包含"登录失败",就判定为可能重复。结果误报率极高,因为"登录失败"可能对应完全不同的原因。后来改用向量相似度:把每条 Issue 的标题和正文拼接后做嵌入,计算余弦相似度,超过阈值才提示可能重复。

这里有个细节值得说:嵌入的时候要把标题的权重调高。因为 Issue 的正文往往很长很杂,标题才是核心信息的浓缩。我的做法是把标题重复拼接两次再和正文合并,相当于给标题加权。实测下来,这样能把重复检测的准确率提升不少。

阈值的选择也很关键。设太高会漏掉真正的重复,设太低会误报。我试了几组值,最后定在 0.82 左右比较平衡。当然这个值跟你的嵌入模型有关,需要根据自己的数据调。

2.4 回复草稿的生成策略

回复草稿是最容易翻车的环节。如果让模型自由发挥,它可能生成一段看起来很礼貌但实际上毫无信息量的废话,甚至可能承诺一些维护者根本做不到的事情。我的策略是模板 + 槽位填充:预先写好几种回复模板,对应不同类型和场景,模型只负责填充关键信息,比如缺失的字段、相关的文档链接、预计处理时间等。

这样做的好处是回复质量稳定可控,不会出现奇怪的承诺。同时模板也让维护者容易审查,因为结构是固定的,扫一眼就知道有没有问题。对于确实需要个性化回复的情况,再让模型基于模板做适度改写。

3. 核心模块的细节与实操要点

3.1 类型识别的规则设计

类型识别的规则不是随便拍脑袋写的,而是从历史 Issue 里统计出来的。我翻了自己项目过去半年的 Issue,把每个类型的高频词汇整理出来,形成了下面这张规则表:

类型高频触发词权重
Bug崩溃、报错、异常、无法、失败、错误、不生效高
功能请求希望、建议、能否、支持、增加、添加、期待高
使用咨询怎么、如何、请问、求助、为什么、是否中
文档问题文档、说明、示例、教程、不清楚、误导中
性能问题慢、卡、超时、内存、CPU、耗时中

规则匹配的逻辑是:统计每个类型命中的关键词数量和权重,得分最高的类型作为初步判断。如果最高分和次高分差距很小,说明规则判断不确定,就转交给模型。

注意:规则词表需要定期更新。新出现的术语、项目特有的表达方式,都要及时补充进去。我一般每个月回顾一次误判的案例,把新的触发词加进去。

这里有个实操心得:标题的权重应该比正文高。因为很多 Issue 的正文会大段粘贴日志,里面可能包含各种类型的词,干扰判断。我的做法是标题命中关键词算 2 分,正文命中算 1 分,这样能有效降低日志噪音的影响。

3.2 标签体系的受控设计

标签体系最忌讳的就是"想打什么打什么"。我见过一些项目,标签有几十个,互相重叠,维护者自己都记不清该用哪个。所以我在设计时遵循几个原则:

  • 标签数量控制在 15 个以内,太多了没人记得住。
  • 每个标签有明确的定义和适用场景,写成文档放在仓库里。
  • 标签分维度:类型维度(bug、enhancement、question)、状态维度(needs-info、confirmed、wontfix)、优先级维度(P0、P1、P2)。
  • 模型只能在预定义词表里选,不允许创造新标签。

模型输出标签时,我会在提示词里把完整的标签词表和定义都给它,要求它只返回词表里的标签。如果模型返回了词表外的内容,直接丢弃并记录日志,方便后续排查。

3.3 信息完整性检查的字段设计

信息完整性检查是提升 Issue 质量的关键。我在仓库里放了一个 Issue 模板,要求提交者填写环境信息、复现步骤、期望行为、实际行为。但总有人不按模板来,所以助手需要自动检查这些字段是否齐全。

检查逻辑分两步:先看 Issue 正文里有没有对应的章节标题(比如"复现步骤"),如果没有,再用模型判断正文里是否隐含了这些信息。比如有人用自然语言描述了操作过程,虽然没有"复现步骤"这个标题,但内容其实是完整的,这种情况就不应该误判为缺失。

缺失字段的追问也要讲究方式。不能生硬地说"你没填复现步骤",而要友好地引导,比如"为了更快定位问题,能否补充一下具体的操作步骤?"这种细节直接影响提交者的配合意愿。

3.4 优先级初判的参考维度

优先级判断是最容易引起争议的环节,因为不同人对"紧急"的定义不一样。我的做法是只给参考,不做决定,并且把判断依据列出来,让维护者自己权衡。

参考维度包括:

  • 影响范围:是所有人都受影响,还是特定环境?关键词如"所有""全部""普遍"提示影响面大。
  • 是否有绕过方案:如果提交者说"目前只能通过 XX 临时解决",说明有 workaround,优先级可以降。
  • 是否阻塞核心功能:涉及登录、支付、数据丢失等核心流程的,优先级高。
  • 提交者情绪:多次追问、语气急切的,往往是真的着急,值得优先看。

这些维度综合起来给一个 P0/P1/P2 的初判,同时在 Issue 里附上一段说明,解释为什么这么判断。维护者可以一键采纳或修改。

4. 完整实操流程与关键环节实现

4.1 环境准备与依赖安装

整个助手我选择用 Python 实现,因为生态成熟、上手快。核心依赖包括:

pip install requests numpy scikit-learn

如果要用嵌入模型做重复检测,还需要一个嵌入服务。我建议先用轻量方案跑通流程,比如用 TF-IDF 做初版相似度,等流程稳定了再换成更好的嵌入模型。这样能避免一开始就陷入模型选型的纠结。

Issue 数据的获取通过平台提供的 API 完成。你需要一个访问令牌,配置成环境变量,不要硬编码在代码里。这一点很重要,我见过有人把令牌直接写在脚本里然后不小心提交到公开仓库,后果很麻烦。

import os TOKEN = os.environ.get("ISSUE_TOKEN")

4.2 数据拉取与预处理

第一步是把待处理的 Issue 拉下来。我一般只拉取最近一周内新开的、还没有被处理过的 Issue,避免重复劳动。判断"是否已处理"可以通过检查是否已经有维护者打的标签来实现。

拉取到的原始数据需要做清洗:去掉多余的空白、统一换行符、截断过长的日志内容。日志截断很关键,因为有些 Issue 会粘贴几千行日志,直接送给模型既浪费又可能超出长度限制。我的做法是只保留日志的前 50 行和后 50 行,中间用省略号代替,并在提示词里说明这是截断后的内容。

预处理还包括提取标题、正文、提交者、创建时间等结构化字段,方便后续处理。

4.3 分诊主流程的实现

主流程是一个流水线,每个环节的输出作为下一个环节的输入:

  1. 拉取 Issue:获取待处理列表。
  2. 类型识别:先规则,规则不确定再模型。
  3. 标签建议:基于类型和内容,从受控词表里选。
  4. 优先级初判:综合多个维度给出参考值。
  5. 重复检测:和已有 Issue 做相似度比对。
  6. 完整性检查:检查必要字段是否齐全。
  7. 生成回复草稿:根据以上结果填充模板。
  8. 写回结果:把建议以评论或草稿形式写回 Issue,供维护者确认。

每个环节都要有日志记录,方便出问题时回溯。我习惯把每一步的输入输出都存下来,尤其是模型的原始返回,这样当结果不对时能快速定位是哪个环节出了问题。

4.4 提示词的设计要点

提示词的质量直接决定模型输出的质量。我总结了几个要点:

  • 明确角色和任务:开头就说清楚"你是一个 Issue 分诊助手,负责判断类型和标签"。
  • 给出完整的标签词表和定义:不要让模型猜。
  • 提供几个示例:few-shot 能显著提升稳定性。
  • 规定输出格式:要求返回 JSON,字段固定,方便程序解析。
  • 强调边界:明确告诉模型"如果不确定,返回 unknown,不要瞎猜"。

一个典型的提示词结构是这样的:

你是一个开源项目的 Issue 分诊助手。 请根据以下 Issue 内容,判断它的类型,并从给定标签词表中选择合适的标签。 标签词表: - bug: 代码缺陷导致的功能异常 - enhancement: 新功能或改进建议 - question: 使用咨询 ... Issue 标题:{title} Issue 正文:{body} 请以 JSON 格式返回,字段包括 type、labels、confidence。 如果不确定,type 返回 unknown。

提示:提示词要版本化管理。每次修改都记录改了什么、为什么改、效果如何。否则改着改着就忘了哪版效果最好。

4.5 结果写回与人工确认

助手处理完的结果,我选择以草稿评论的形式写回 Issue,而不是直接打标签。原因是直接打标签风险太大,万一判断错了,提交者看到错误的标签会困惑。草稿评论则不同,它只有维护者能看到,确认无误后再一键转为正式操作。

草稿评论的格式要清晰,把类型、标签建议、优先级、重复提示、缺失字段都列出来,让维护者一眼就能看全。我还会附上每个判断的依据,比如"因为标题包含'崩溃',判定为 Bug",这样维护者能快速判断是否合理。

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

5.1 模型输出不稳定怎么办

这是最常见的问题。同样的 Issue,模型这次说是 Bug,下次说是 question。解决办法有几个:一是降低温度参数,把 temperature 调到 0 或接近 0,让输出更确定;二是增加示例,few-shot 能显著提升一致性;三是加校验层,如果模型输出和规则判断冲突,以规则为准,并记录冲突案例。

我实测下来,温度调到 0 加上 3 个示例,稳定性就能满足大部分场景。如果还是不稳定,说明这个任务本身就有歧义,那就应该交给人工。

5.2 重复检测误报太多怎么调

误报通常是因为阈值设太低,或者嵌入质量不够好。排查步骤:先看误报的案例,判断是"真的相似但不算重复"还是"完全不相似却被判重复"。如果是前者,说明阈值需要调高;如果是后者,说明嵌入模型不适合你的数据,需要换一个。

还有一个容易被忽略的点:短文本的相似度天然偏高。两条只有几个字的 Issue 标题,余弦相似度很容易超过阈值。我的做法是对短文本单独处理,要求标题长度超过一定字数才参与重复检测,或者对短文本用更严格的阈值。

5.3 提交者不配合补充信息怎么办

这是人的问题,不是技术问题。助手的追问要尽量友好,并且给出明确的示例,告诉提交者"应该补充成什么样"。比如不要只说"请提供复现步骤",而要说"请按以下格式补充:1. 我做了什么操作 2. 期望看到什么 3. 实际看到什么"。

如果追问两次还没补充,就自动打上needs-info标签并降低优先级,避免维护者被反复打扰。这个策略要提前在仓库的贡献指南里说明,让提交者有预期。

5.4 成本控制的实际经验

如果全部用大模型,成本会随 Issue 量线性增长。我的控制策略是:规则能搞定的绝不调模型。实测下来,大约 60% 到 70% 的 Issue 能靠规则完成类型识别,只有剩下的才需要模型。这样成本能降一大半。

另外,重复检测用的嵌入模型可以选择轻量级的,因为相似度计算对模型能力要求没那么高。回复草稿生成可以用小模型,因为模板已经固定了大部分内容,模型只需要做少量填充。

5.5 常见问题速查表

问题现象可能原因排查方向
类型判断总是错规则词表过时更新词表,补充新术语
标签超出词表提示词没约束好检查提示词是否包含完整词表
重复检测漏报阈值太高适当调低阈值,观察效果
重复检测误报阈值太低或短文本调高阈值,短文本单独处理
回复草稿生硬模板太死板增加模板变体,允许适度改写
处理速度慢模型调用串行改成并发调用,注意限流
成本超预期模型调用过多增加规则覆盖,减少模型调用

5.6 几个踩过的坑

第一个坑是过度依赖模型。最开始我什么都交给模型,结果不仅贵,而且不稳定。后来把大量逻辑改成规则,反而效果更好。这让我意识到,AI 不是万能的,该用规则的地方就用规则。

第二个坑是忽略日志。有段时间分诊结果时好时坏,但因为没有记录中间过程,排查了很久才找到原因。从那以后我养成了记录每一步输入输出的习惯,排查效率提升明显。

第三个坑是没有人工兜底。有次模型把一个紧急 Bug 判成了普通咨询,差点耽误处理。后来我加了规则兜底:只要标题包含"数据丢失""无法登录"等关键词,无论模型怎么判,都强制标记为高优先级。

6. 后续可以怎么扩展

这套助手跑通之后,能扩展的方向其实不少。比如可以接入项目的 CI 状态,如果某个 Issue 关联的代码最近有失败记录,自动提示维护者优先看。再比如可以做趋势分析,统计每周各类型 Issue 的数量变化,帮助维护者了解项目健康度。

还有一个我觉得很有价值的方向是知识库沉淀。把已经解决的历史 Issue 和对应的解决方案整理成知识库,当新 Issue 进来时,除了判断类型,还能自动检索相似的历史案例,把解决方案一并附上。这样很多常见问题甚至不需要维护者出手,提交者自己就能找到答案。

不过这些扩展都要建立在基础流程稳定的前提上。我的建议是先把类型识别、标签建议、重复检测这三个核心功能做扎实,跑上一两个月,积累足够的反馈数据,再考虑扩展。否则基础不牢,扩展越多问题越多。

最后分享一个我在实际使用中的体会:这个助手最大的价值不是省了多少时间,而是让维护者重新找回了处理 Issue 的掌控感。以前打开后台是一堆未知,现在打开后台是已经整理好的信息,心理负担完全不一样。技术上的优化固然重要,但对长期维护项目的人来说,这种心理上的轻松可能才是坚持下去的关键。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进