AI驱动的GitHub Issue自动分诊:从提示词到工作流自动化
我是被自己的 Issue 列表逼到写这个工具的。参与维护的一个开源小项目Star 数不算多但 Issue 活跃度出奇高新人提问题、老用户报需求、还有人拿着别的项目的报错截图直接贴过来。每天早上一睁眼仓库里就多了十几条 Issue我必须挨个点开判断是 bug、需求还是求助检查有没有版本号、日志、复现步骤再决定要不要先追着提问者补齐信息。这套流程我连续做了一个月整个人像客服。终于有个周五下午我对着第 37 条为什么运行不了的标题决定动手把第一轮脏活交给 AI。这篇文章不聊什么宏大的AI 重构开源协作只把这件事原原本本写下来我做的这个 GitHub Issue 分诊助手到底承担了什么、技术上怎么落地、踩了哪些坑、跑了几周后真实效果如何。如果你也在维护仓库或者想给自己的团队工单系统做一个类似的智能分诊这篇可以直接拿来当参考。1. 为什么需要分诊维护者的一天大半时间被垃圾桶问题吃掉1.1 所谓第一轮脏活到底是什么我统计过自己两周之内处理的 86 条 Issue真正需要我动手改代码的不到四分之一。剩下的时间里我在做大量重复、机械、低判断力的事情区分问题类型这到底是 bug、功能需求、文档问题还是单纯的用法咨询。检查信息完整性版本号、操作系统、复现步骤、相关日志经常有四到五样是缺的。重复识别同一个问题被三个人用不同标题各报一遍措辞差别大得根本搜不出来。优先级判断有的问题影响核心链路有的只是美化诉求但只看标题根本分不出轻重。给出第一次回复通知提问者请补充如下信息或者安抚一句我们知道了会尽快看。这五项工作我习惯叫它第一轮脏活。它不产生核心代码但不做就会被后续更高优先级的问题拖延最终全部变成维护者的隐性成本。一旦 Issue 池超过 300 条光是维护者的心理压力就会明显上升处理积极性下降新人也更容易被忽略。1.2 人工分诊为什么难以坚持团队刚成立的时候大家说好谁提的 Issue 谁先贴标签。实际上执行不到两周就崩了贴标签的人会下意识把问题归到最熟悉的模块催人补信息时语气控制不好提问者会觉得被冒犯而重复问题根本没人有精力去逐个搜索比对。另一个隐藏原因是时区差异。我们几个维护者分布在不同的时区欧洲那边睡着的十几个小时里北美用户狂提 Issue等维护者醒来面对的可能是一大串没有经过任何处理的原始问题。没有分诊紧急问题被淹没在普通问题里等注意到时已经过去一晚。1.3 我给分诊助手定下的目标做这个助手之前我先给自己定了三条硬指标防止把项目做成过度工程第一它必须能在 Issue 创建的几分钟内做出首次响应不需要维护者在线。第二它只做读、标、问、提醒四件事绝不尝试直接修复或关闭 Issue。第三它的判断结果必须带置信度低置信度时必须把问题留给人工而不是强行接管。后来证明这第三条比技术本身更重要。AI 分诊的定位不是替代维护者而是把最费时间的阅读与分类压缩掉让人只处理真正需要判断力的那部分。2. 分诊助手的能力边界该自动化的自动该守住的人来定2.1 第一版能力矩阵设计能力边界时我把所有能想到的行为列成一张表然后逐个标记可以做和不能做。最终确认的自动行为只有四类全部围绕降低后续人工阅读成本展开行为具体操作默认策略问题分类打上 bug / feature / docs / question 等标签置信度低于 0.6 时不打标签完整性检查识别版本、环境、日志、复现步骤是否缺失只列缺失项不代替用户生成信息初步建议给出排障方向或提供相关文档链接标准话术不包含断言式结论提醒维护者低置信度或疑似紧急问题时 维护者通过评论触发通知不改动里程碑比如一条只写完全不能用求求了的 Issue助手会打上 question 标签评论里礼貌列出请提供系统版本、使用场景、简单日志然后 维护者。它不会像某些人做的极端方案那样直接关闭问题——那样太容易误伤新人。2.2 哪些看起来能自动的事我故意不做技术实现上自动关闭重复 Issue、自动给 Issue 排优先级、甚至自动生成补丁都是可以做的但我第一版全部砍掉了。原因很朴素自动关闭的风险在于AI 误判重复的代价不对称。假如模型把一条真实的新 bug 误判成重复并自动关闭提问者会非常沮丧很可能流失掉一个潜在的深度用户。重复问题应该被标记但关闭动作必须由人来按按钮。优先级也是一样。一个模型说这个影响很严重实际上它是从几个关键词里猜出来的情绪强度而不是对代码库结构的真实理解。优先级涉及资源分配一旦自动排错比不排更麻烦。我给助手加了一条原则硬件规则优先AI 判断只作为参考字段输出到评论里不直接触发工作流。所以整个设计的内核是AI 参与分诊但它的所有输出都先落在 Issue 评论和元数据上任何有后果的动作关闭、指派、移除标签都需要维护者二次确认。开源社区的自由度很高但信任必须一步一步建立。3. 事件触发、幂等控制与成本这不只是一个提示词工程3.1 选型用托管工作流而不是额外起服务动手写代码之前我先决定用事件驱动的托管工作流而不是自己部署一个常驻进程。对一个小型开源仓库来说独立服务意味着要处理服务器、密钥、日志监控这些运维成本远超收益。托管 CI 自带的 workflows 可以监听issues事件在 Issue 打开或 reopened 时自动触发脚本这种方式免费、免运维、和我们现有的代码审查流程放在同一套体系里维护者不用多学一个平台。它唯一的限制是执行超时和运行环境但分诊任务是纯文本处理根本没有这些压力。3.2 完整触发链路与权限配置触发链路的配置我基本照着这个模板来name: issue-triage on: issues: types: [opened, reopened] permissions: issues: write jobs: triage: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-pythonv5 with: python-version: 3.11 - run: pip install requests - env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} AI_API_KEY: ${{ secrets.AI_API_KEY }} REPO_FULL_NAME: ${{ github.repository }} ISSUE_NUMBER: ${{ github.event.issue.number }} run: python triage.py这里有两个细节很容易被忽略。一是permissions: issues: write必须显式声明否则默认 token 只有读权限脚本里所有写入 Issue 的调用都会 403。二是触发器里我刻意没监听edited事件后来发现用户编辑正文很频繁如果每次编辑都重新跑一遍分诊既浪费钱又容易让评论区刷屏。关于幂等控制我采用了一个最简单的方案脚本第一步先检查 Issue 是否已经带上了triage-robot-commented标签如果带上了就直接退出。这个标签是我们初始 seed 进去的相当于给每条 Issue 做了一个已分诊标记。这样即使工作流被手动重跑或者和别的自动化工具撞车也不会重复评论。3.3 单条 Issue 的真实成本估算成本是很多人第一眼就会问的问题。我实测了不同长度 Issue 的 token 消耗一条普通咨询大约 900 到 1500 tokens带一大段日志截图的 bug 报告则是 2000 到 3000 tokens。如果用的是按量计费的托管模型把输入输出和工具调用都算上单条成本基本在 0.01 到 0.03 元之间。为什么能压到这么低因为我选了轻量级模型版本。分诊这个任务不需要复杂的代码生成只需要从文本中抽取信息、套用分类、按固定格式输出小模型完全够用。上下文也做了裁剪只把 Issue 的标题、正文、用户填写的表单字段拼成文本不把仓库历史 Issue 全部塞进去省下大量输入 token。我跑了一个月的账总共处理 240 多条 Issue成本加起来不超过 6 块钱。对于任何活跃仓库来说这点开销完全可以忽略哪怕是个人项目也承担得起。4. 提示词模板与输出约束让模型稳定输出可消费的结构化结果4.1 系统提示词怎么写才不会自嗨很多 AI 工具的翻车现场往往不在模型能力而在于提示词让模型想说什么就说什么。我用的是系统提示词 严格 JSON 输出双重约束。系统提示词的长这样你是一个开源仓库的 Issue 分诊助手。你的任务是根据用户提交的 Issue 内容完成四项工作 1. 判断问题类别bug、feature、docs、question、other。 2. 检查关键信息是否缺失运行环境、版本号、复现步骤、日志、报错信息。 3. 判断是否可能为重复 Issue只判断与已有 Issue 标题描述的明显相似度。 4. 给维护者写一段不超过 80 字的初筛建议。 约束 - 绝不编造任何用户未提供的细节。 - 如果不确定category 务必返回 other缺失项给空数组。 - 只输出 JSON不要输出任何额外文字。 已知仓库中常见关键词库名 demo-utils服务端版本 2.x 系列。这里最关键的是只输出 JSON不要输出任何额外文字那句。如果没有这句话模型经常会在 JSON 外面包一层好的我将为你的 Issue 进行分类之类的废话解析起来虽然能处理但每次都要写容错逻辑很麻烦。另外仓库名和版本范围这类硬信息我选择直接写进提示词而不是让模型自己猜。有一次模型把demo-utils 1.3.2 崩了理解成运行环境是 Python 3.8把我坑得好苦。把关键常量喂给模型让它做选择题而不是做推断题准确率立刻上一个台阶。4.2 用 JSON Schema 做二次校验光有提示词还不够。我在脚本里直接要求模型按 JSON Schema 输出并在收到结果后做二次校验schema { type: object, properties: { category: {type: string, enum: [bug, feature, docs, question, other]}, missing_fields: {type: array, items: {type: string}}, duplicate_candidate: {type: string, description: 疑似重复的 Issue 编号或标题没有则留空}, summary: {type: string, maxLength: 80} }, required: [category, missing_fields, duplicate_candidate, summary] } response client.chat.completions.create( model轻量级小模型, response_format{type: json_object}, messagesmessages, temperature0, ) data json.loads(response.choices[0].message.content)有一点值得注意temperature我设成了 0。分诊任务更需要确定性而不是创造力允许模型自由发挥只会让同一类 Issue 拿到五花八门的总结对后续人工排查没有任何帮助。实践下来温度 0 的同时偶尔会出现模型过于保守、全部归类到 other 的情况所以我在提示词里补充了如果存在明显技术词汇报错、崩溃、版本异常倾向归类 bug作为缓解策略。4.3 将结果写回仓库评论、标签、 的取舍拿到结构化 JSON 后我按严格顺序调 GitHub API先打标签再写评论最后做一次日志审计。标签不能打到不存在的标签上否则 422 报错。所以我维护了一个labels.py里面存着所有允许使用的标签名和颜色脚本每次运行前先同步一次仓库的 label 列表把不存在的过滤掉。评论模板我写得像下面这样感谢反馈这个 Issue 已进入自动分诊流程。 - 分类bug - 疑似缺失信息运行环境、版本号 - 初筛建议请先尝试升级到最新版本后重试 如果是维护者可在确认后再补充处理。看似简单但这句话背后有个心理学考虑对提问者来说AI 的回复也要像人话不能一句AI 自动分诊就把人晾在那儿。语气里带上已进入流程会让提问者知道自己的反馈被看见了。至于 维护者的时机我保守了很多只在前 20% 的高置信度 bug 场景才 。因为 GitHub 的 提醒是会打扰人的如果每一条人工智能就 一遍维护者收到的通知噪音反而比之前更大分诊就变味了。5. 实测反而最费精力的三类坑重复、死循环、幻觉5.1 机器人给自己制造新 Issue上线第二天就翻车了但不是因为模型而是因为我自己的设计。某条 Issue 被处理时脚本自动创建了一个 triage-robot-commented 标签而我当时图方便没有给工作流排除机器人账号自己的操作。结果是脚本在打标签时产生的事件又触发了issues事件工作流被无限循环调度。我最后排查到原因时哭笑不得修复方案是在自己身上加了一层判断actor os.getenv(GITHUB_ACTOR, ) if actor github-actions[bot]: print(机器人自身操作跳过分诊) sys.exit(0)这样无论我们内部哪个自动化脚本动了 Issue都不会再触发分诊。后来我又放宽了一层只要 Issue 作者是机器人账号分诊脚本直接跳过。因为真实用户的问题才值得分诊机器人提的 Issue 通常来自其他自动化流程不需要 AI 再来插一脚。5.2 幻觉出来的重复 Issue 推荐分诊助手上线第一周评论里出现过的最大笑话是它把一个只包含了支持图形化配置界面的功能需求判定为与 3 天前的 bug 报告重复并贴出了那个 bug 的编号。我看了一眼两者除了都提到界面这个词之外毫无关系。问题出在哪里我最初给模型提供的上下文太简单只让它看仓库已有 Issue 的标题列表没有任何相似度特征。模型学到的模式是既然提到了界面那就跟之前界面相关的重复。这个方向完全错了。我做的第一处改进是把search能力收回来不让模型自己决定是否去查历史 Issue而是由脚本用显式文本相似度算法在本地先筛出前 3 个候选再把候选标题塞给模型做判断。本地筛选用词频和关键命中的组合简单可靠模型只做选一个还是都不选的判断题准确率立刻提升了不少。第二处改进更关键模型输出duplicate_candidate后脚本只会在评论里写可能存在重复Issue #xxx请维护者确认不添加自动关闭标签。把怀疑和行动分开用户和维护者都不会觉得被冒犯。5.3 模型对附件内容的盲区GitHub Issue 里大量附带的是截图和日志文本日志如果是粘贴到正文里的模型处理得很好但如果用户把日志作为 .txt 附件传上来内容根本不会出现在文本参数里。最开始的版本模型看到只有一行详见附件的 Issue会一本正经地编造出一个看起来很合理的 bug 分析。这个问题我用了非常朴素的方案解决在提示词里加上一条强约束——如果 Issue 正文中的关键信息全部来自附件直接返回 missing_fields 为 [附件内容无法自动读取]。同时对脚本做了后置校验当模型返回 summary 中没有任何附件无法读取的关键字但 Issue 正文又确实存在附件时将以脚本判断为准。人类写的规则优先级永远高于模型判断。这也给我提了个醒机器人第一版上线的核心任务不是什么都做而是知道自己不知道什么。分诊助手可以假装看懂一张截图但维护者后续要花五倍时间纠正得不偿失。6. 运行三周后的数据与这次改造的意外收获6.1 一组可以复现的对比数据我挑了同样规模的两周样本做对比改造前两周和上线后两周我看同一仓库的 Issue 处理情况得到的数据如下指标改造前改造后未分诊原始 Issue 占比100%12%平均首次响应时间约 18 小时32 分钟缺少复现步骤的比例43%未明显变化但已被标记重复标记数3 个人工发现16 个AI 识别并标记人工复核成本每条都要看约 1/4 的 Issue 可一键跳过最让我意外的是最后一行经过三周学习之后每条新 Issue 我真正需要通读全文的比例下降了大约三分之一。因为 AI 给出的 summary 已经足够让我在要等电梯的时候快速判断要不要插手。缺点也明显AI 总分不清这个功能我应该不会做和这个需求根本不可能实现的区别归根结底它不理解代码库的演进方向。6.2 意外收获内部流程开始跟着改造这个助手跑起来之后仓库的 Issue 模板反而成了重点优化对象。以前我们有个十年前的模板里面十几个字段用户根本不想填。为了让 AI 分诊看得懂我们把模板改成 GitHub Issue Forms 的 YAML 结构化表单让用户下拉选择运行环境、问题类型、影响模块AI 的分类准确率立刻提高了不少。这是最典型的一举多得结构化表单提高了 AI 分诊质量实际上也提高了用户自己描述问题的质量。以前随便写一句话的人现在被表单引导着把关键信息补齐了维护者的阅读体验同步改善。说白了工具开始影响人与人之间的协作习惯这比 AI 本身更有价值。另一件小事也值得一提我们把 AI 的置信度分数写进了评论低于 0.6 的 Issue 会被标注为低置信度请人工优先查看。这个机制推出后我们发现维护者处理低置信度问题的速度反而比处理高置信度问题快——因为人天生觉得AI 都拿不准的一定有什么细节值得注意。虽然是心理效应但确实提高了问题发现率。6.3 到最后仍然不能交给 AI 的部分三周实验做下来我最大的感受是分诊已经变成这个仓库的地基但地面上的房子还是人盖的。任何涉及沟通语气的问题我不会交给模型。我记得有一条 Issue用户情绪很差就一句我再也不用这个破库了。模型给的建议是通过标准话术回复但我最后手写了回复先道歉再问情况因为那一刻用户需要的是一个活人的共情而不是一个标签和一个 JSON。另外判断这个Issue 是否值得进一步投入开发这种偏产品决策的问题我也完全不让 AI 碰。它可以总结用户诉求、聚合并行需求、甚至估算用户活跃度但决定项目往哪走的是维护者对社区生态的理解不是 token 预测。最后分享一个实操层面的小技巧把分诊机器人的所有输出都留一条审计记录我是在团队的讨论频道里单独开了一个机器人频道把所有分诊结果、置信度、模型版本号同步过去。不只是为了查问题更关键的是当质疑你怎么能这样回复用户时你可以翻记录说是哪一版提示词、哪一个模型、哪一次运行产生的输出避免所有锅都变成AI 的锅。如果你要做一个类似的分诊工具我建议从最小版本开始只做分类和信息缺失检查跑两周贴出数据让你自己和维护者们看看再去谈优先级、去重这些进阶功能。先让 AI 替你抄最脏的那些活再慢慢把边界往外扩。这样它得到的不只是一堆好评而是大家真的离不开它。