用AI自动分诊GitHub Issue:标签、优先级与信息补全实战 1. 为什么我想把 Issue 分诊这件事交给机器维护过开源项目的人都有一个共同体会写代码不是最累的处理 Issue 才是。一个中等活跃度的仓库每天醒来可能就躺着十几条新 Issue标题五花八门内容质量参差不齐。有人把使用问题当 Bug 报有人贴了一堆日志但没说复现步骤还有人提了个 Feature Request 却写得像投诉信。你一条条点开、读、打标签、回复请补充环境信息、再判断优先级——这套动作重复几十次之后人会变得非常暴躁而且很容易漏掉真正重要的那条。我维护的项目规模不算大但过去半年 Issue 数量涨得很快。最夸张的一周我花了将近六个小时纯粹在做分诊这件事判断类型、打标签、标记优先级、给提问者回复模板化的追问。这六个小时里没有写一行代码没有任何创造性产出全是机械劳动。更麻烦的是有些 Issue 我隔了两天才看到提问者已经失去耐心走了。所以我就想第一轮的脏活能不能交给 AI 来做。注意我的措辞是第一轮不是全部。我不指望 AI 直接替我关闭 Issue 或者给出最终结论那不现实也不安全。我要的是当一条新 Issue 进来时AI 先帮我读完给出一个初步判断——它大概是什么类型、可能涉及哪个模块、信息是否完整、优先级大概在什么档位然后把这些结果以标签和评论的形式落到 Issue 上。我只需要扫一眼 AI 的判断确认或修正即可。这个想法落地之后我把它做成了一个Issue 分诊助手。它本质上是一条自动化流水线新 Issue 触发 → 拉取内容 → 调用大模型分析 → 结构化输出 → 回写标签和评论。整套东西不复杂但里面有不少细节值得说清楚比如怎么设计 Prompt 才能让输出稳定、怎么防止 AI 乱打标签、怎么处理模型偶尔的幻觉、成本怎么控制。下面我把整个设计和实操过程拆开讲如果你也在维护仓库、也被 Issue 淹没这套思路可以直接抄。2. 整体设计分诊助手到底该做哪几件事2.1 先想清楚分诊的边界在哪里医院的分诊台护士不会给你做手术她只做一件事根据你的症状把你分到对应的科室并判断你是不是急诊。Issue 分诊助手也应该遵守同样的边界。我给它划定的职责范围只有四项分类这条 Issue 是 Bug 报告、功能请求、使用咨询、文档问题还是重复/无效内容。打标签根据分类和内容自动打上对应的 label比如bug、enhancement、question、docs。信息完整性检查Bug 报告有没有复现步骤、环境信息、期望行为与实际行为缺了就自动回复追问。优先级初判结合关键词、影响范围描述、是否有崩溃/数据丢失等信号给出一个粗略的优先级建议。超出这四件事的一律不做。不自动关闭 Issue不自动分配给人不对技术方案下结论。这个边界非常重要因为一旦 AI 越界做了它不该做的判断维护者要花更多精力去纠偏反而更累。2.2 为什么选标签 评论作为输出形式AI 的分析结果总得有个地方落地。我考虑过几种方案写进数据库自己看、发到聊天群、直接回写到 Issue。最后选了回写到 Issue原因是上下文不能割裂。Issue 本身就是一个天然的协作现场提问者、其他维护者、路过的贡献者都在这里。如果 AI 的判断只存在于我自己的后台那别人看不到协作就断了。具体来说AI 的输出分两部分落地标签是给机器和筛选器看的方便后续用label:bug这样的条件快速过滤评论是给人看的用一段简洁的话说明我判断这是 XX 类型因为 XX建议补充 XX。评论的措辞我特意调过让它读起来像是维护者助手在说话而不是冷冰冰的机器判定这样提问者的接受度会高很多。2.3 触发时机为什么不在 Issue 创建瞬间就跑一开始我把触发点设在 Issue 创建的那一刻结果踩了坑。有些用户创建 Issue 之后会立刻连续编辑好几次补充内容、改标题、贴日志。如果我在创建瞬间就分析拿到的往往是半成品判断自然不准还会因为重复触发浪费调用。后来我改成延迟触发 去重Issue 创建后等 60 秒再启动分析如果这期间有编辑事件就重置计时器。同时用一个基于 Issue 编号和更新时间的去重键保证同一条 Issue 在短时间内只分析一次。这个改动之后误判率明显下降调用次数也省了不少。60 秒这个值是我试出来的太短了用户还在编辑太长了提问者等待感明显60 秒是个比较舒服的平衡点。3. 核心细节让 AI 输出稳定可用的几个关键3.1 Prompt 设计结构化输出是生命线让大模型自由发挥写一段分析结果每次格式都不一样程序根本没法解析。所以第一原则是强制结构化输出。我要求模型返回一个 JSON 对象字段固定不允许有多余文字。大致长这样{ category: bug | feature | question | docs | invalid, labels: [bug, needs-info], priority: high | medium | low, missing_info: [reproduction_steps, environment], summary: 一句话概括这条 Issue 的核心诉求, reply: 给提问者的回复草稿 }字段设计上有几个讲究。category用枚举值而不是自由文本方便程序映射到标签labels是数组因为一条 Issue 可能同时是 bug 又缺信息missing_info单独列出来是为了让追问评论能精准指出缺什么而不是笼统地说信息不足reply是给提问者的草稿我会在程序里再包一层礼貌用语避免模型偶尔语气生硬。提示枚举值一定要在 Prompt 里写死并给出每个值的判定标准。比如question和bug的边界最容易混我明确告诉模型如果用户描述的是我不确定怎么用而非功能坏了归为 question。 这一句话让分类准确率提升了不少。3.2 用少样本示例把判断标准喂进去光靠文字描述判定标准模型还是会有理解偏差。我的做法是在 Prompt 里塞三到五个少样本示例每个示例给一段典型的 Issue 文本和对应的正确输出。这几个示例是我从历史 Issue 里精挑出来的典型病例覆盖了最容易混淆的场景一个伪装成 bug 的使用咨询、一个信息完整的优质 bug 报告、一个只有一句话的功能请求。示例的作用是锚定判断尺度。比如我放了一个用户说保存按钮点了没反应但没给任何环境信息的例子标注为bug needs-info模型看到之后遇到类似情况就知道既要打 bug 标签又要标记缺信息。实测下来加了少样本示例之后标签的准确率比纯描述高了大概两成尤其是needs-info这个标签的召回率提升明显。3.3 防幻觉让模型有据可依大模型有个毛病就是喜欢脑补。你给它一段模糊的描述它可能自己编出一个根本不存在的模块名或者错误原因。在分诊场景里这种脑补是危险的因为它会误导维护者。我的应对策略是约束模型的推理依据。在 Prompt 里我明确要求你的所有判断必须基于 Issue 文本中明确出现的信息如果某个判断缺乏文本依据宁可不给也不要猜测。 同时summary字段我要求它尽量用原文词汇改写而不是引入新概念。另外对于可能涉及的模块这类容易脑补的信息我干脆不要求模型输出——因为模型对代码库结构一无所知让它猜模块纯属添乱。模块归属这件事留给后续基于文件路径关键词的规则匹配去做比模型靠谱得多。3.4 标签白名单绝不让 AI 创造新标签这是血泪教训。早期我没限制标签范围结果模型自己发明了critical-bug、ui-issue、maybe-duplicate这种仓库里根本不存在的标签导致标签体系一团乱。后来我加了一道白名单校验模型返回的labels数组里任何不在预定义集合中的标签都会被程序丢弃只保留合法的。预定义标签集合我控制在十个以内太多了反而没人用。核心就是bug、enhancement、question、docs、invalid、needs-info、priority-high、priority-low这几个。白名单机制相当于给 AI 套了个笼子它可以在笼子里自由判断但出不了圈。4. 实操落地从零搭起这条流水线4.1 技术选型与整体架构整套系统的组件不多我尽量用现成的东西不自己造轮子。核心是四块事件接收、内容拉取、模型调用、结果回写。事件接收用平台自带的 Webhook 机制Issue 创建和编辑都会推送事件到一个我部署的轻量 HTTP 服务。这个服务用 Python 写框架选了 FastAPI因为它处理异步请求方便而且自带请求校验。内容拉取就是拿着 Issue 编号去调平台的 API 拿完整正文和元数据。模型调用走大模型的 API我用的是支持 JSON 模式输出的接口。结果回写同样是调平台 API打标签、发评论。部署上我选了一个最便宜的云函数方案因为这条流水线的调用量不大一天几十次用常驻服务器纯属浪费。云函数按调用计费冷启动那点延迟对分诊场景完全无感。整个服务的代码量大概三百行一个下午就能写完。4.2 关键步骤一Webhook 接收与签名校验Webhook 端点暴露在公网上第一件事是校验请求来源。平台推送的事件通常带一个签名头用共享密钥对请求体做 HMAC 计算和签名头比对不一致的直接拒绝。这一步不能省否则任何人都能伪造事件把你的模型调用额度刷爆。import hmac import hashlib def verify_signature(payload: bytes, signature: str, secret: str) - bool: expected hmac.new( secret.encode(), payload, hashlib.sha256 ).hexdigest() return hmac.compare_digest(fsha256{expected}, signature)校验通过之后解析事件类型。我只关心issues相关的opened和edited动作其他一律忽略。解析出 Issue 编号后不立即处理而是丢进一个延迟队列等 60 秒再去重分析。这个延迟队列我用的是云平台自带的消息延迟功能没引入额外的中间件。4.3 关键步骤二内容拉取与预处理拿到 Issue 编号后调 API 拉取标题、正文、作者、已有标签。这里有个细节正文可能非常长有些用户贴了几百行日志。直接全塞给模型一是浪费 token二是长文本里噪声太多反而干扰判断。我的处理是截断 提取正文超过一定长度就截断但优先保留开头部分通常是问题描述和包含关键词的行比如带error、exception、stack的行。预处理还包括去掉明显的模板残留。很多仓库的 Issue 模板会留下!-- 请在此填写 --这类注释这些内容对判断没帮助反而可能让模型误以为用户填了东西。我用正则把这些 HTML 注释清掉再送给模型。4.4 关键步骤三模型调用与结果解析模型调用这块参数设置有讲究。temperature我设成 0因为分诊需要的是稳定判断不需要创造性max_tokens给足避免 JSON 被截断导致解析失败。调用时开启 JSON 模式让接口保证返回合法 JSON。response client.chat.completions.create( modelyour-model, temperature0, response_format{type: json_object}, messages[ {role: system, content: SYSTEM_PROMPT}, {role: user, content: issue_text} ] ) result json.loads(response.choices[0].message.content)解析之后立刻做白名单过滤和字段校验。如果category不在枚举里降级为question如果labels里有非法值剔除如果 JSON 解析失败记录日志并跳过这次分析绝不把半成品写回 Issue。这一步是最后的安全网宁可这次不处理也不能污染 Issue。4.5 关键步骤四标签回写与评论生成标签回写要注意幂等。同一条 Issue 可能被分析多次比如用户编辑后重新触发如果每次都无脑追加标签会越堆越多。我的做法是先拉取当前标签和 AI 建议的标签做并集只添加缺失的不删除已有的——因为已有标签可能是人工打的不能动。评论生成则是在模型给的reply草稿外面包一层固定模板加上这是自动分诊结果仅供参考的说明并 提问者。模板化有两个好处一是语气统一二是明确告知这是机器判断避免提问者误以为是维护者本人的最终回复。5. 踩过的坑与排查技巧实录5.1 常见问题速查表问题现象可能原因排查与解决模型返回的 JSON 解析失败输出被截断或混入多余文字开启 JSON 模式调大 max_tokens解析失败时记录原始返回标签越打越多、体系混乱未做白名单校验加标签白名单非法标签直接丢弃同一条 Issue 被分析多次编辑事件重复触发加延迟队列和去重键短时间只处理一次分类总把咨询判成 bug判定标准描述不清补充少样本示例明确 question 与 bug 的边界模型编造不存在的模块名要求了超出文本依据的输出去掉模块推断字段只基于原文判断调用成本超预期长文本全量送入、重复触发正文截断提取加去重控制触发频率5.2 独家避坑心得第一别信模型的自信。模型有时候会用非常肯定的语气给出错误判断尤其是当 Issue 文本很短、信息很少的时候。我的应对是给短文本 Issue 加一个特殊标记如果正文长度低于某个阈值就在 Prompt 里提示该 Issue 信息极少请谨慎判断优先标记 needs-info。这样模型会更倾向于保守而不是硬猜。第二优先级判断要克制。一开始我让模型直接输出 high/medium/low结果它动不动就给 high因为很多用户自己就在 Issue 里写这是严重 bug。后来我改成基于信号判断只有文本里明确出现崩溃、数据丢失、安全相关、影响多人等信号时才给 high否则默认 medium 或 low。优先级这东西宁可低估让维护者自己上调也不要高估导致真正紧急的 Issue 被淹没。第三一定要留人工复核入口。我在评论里加了一句如果判断有误欢迎直接回复纠正并且定期回看被人工修改过标签的 Issue把这些案例补充进少样本示例里。这套系统不是一劳永逸的它需要随着你的仓库特点不断微调。我大概每两周会花二十分钟回顾一次误判把典型错误加进 Prompt准确率就一点点爬上来了。第四成本要算清楚。每次分析的 token 消耗取决于 Issue 长度和 Prompt 大小。我的 Prompt 加上少样本示例大概一千多 tokenIssue 正文平均几百 token一次调用成本很低。但如果你的仓库 Issue 量很大比如一天上百条那就要考虑给 Prompt 瘦身或者对明显重复的 Issue 先做去重再送模型。我实测下来加了正文截断和去重之后整体调用量降了大概三成。6. 这套东西跑起来之后我的实际感受上线一个多月最直观的变化是我每天早上处理 Issue 的时间从原来的一个多小时压缩到了二十分钟左右。打开仓库新 Issue 上已经挂着 AI 打的标签和一条追问评论我只需要快速扫一遍确认判断对不对不对就手动改一下标签。真正需要我深入思考的只剩下那些 AI 标了 high 优先级、或者分类模糊的少数几条。提问者的体验也变好了。以前一条信息不全的 Bug 报告可能要等我一两天才回复请补充环境信息现在几分钟内就能收到追问很多人当天就把信息补齐了整个 Issue 的生命周期缩短了不少。有意思的是因为 AI 的追问措辞比较礼貌且具体提问者的配合度比我手动回复时还高一些。当然它远不是完美的。偶尔还是会把功能请求误判成 bug或者漏掉一些隐含的优先级信号。但我觉得这个投入产出比非常划算——用几百行代码和一点模型调用成本换回了每天大半个小时的时间和更快的响应速度。如果你也在被 Issue 淹没我建议你先从最小可用版本做起只做分类和打标签两件事跑顺了再加信息检查和优先级。别一上来就追求全自动分诊这件事人机配合才是最优解。