尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于Hermes的自动化代码评审:部署、配置与落地实践

基于Hermes的自动化代码评审:部署、配置与落地实践 把团队代码评审里最耗精力的那部分活儿交给一个机器人这事我们惦记了很久。起因其实挺朴素PR多起来之后reviewer 的时间被切成碎块上午看一个前端改动下午又得切到 Go 服务的并发逻辑等真正进入状态的时候下班时间也差不多了。后来我们基于 Hermes 搭了一套自动化代码评审流程凡是 GitHub 上的 PR 都会被它过一遍按安全、性能、逻辑边界、风格等维度输出一份带内联评论的审查报告。这篇文章就完整分享一下这套东西怎么落地包括部署踩坑、规则配置、评审查找的完整链路以及在团队里灰度上线后的一些真实数据。Hermes 是什么我们用的 Hermes 是一个自托管、大模型驱动的代码审查代理。它和那种在 CI 里跑 lint 或静态扫描的工具不一样它的定位是“有判断力”的代码评审参与者会结合 PR 的 diff、仓库文件上下文以及提交历史来发现问题而不是背一堆正则规则。它能做什么自动分析每个 PR 的变更内容识别明显 bug、边界条件遗漏、错误处理缺失、安全隐患也会对代码可维护性提建议。评审结果以 inline comment 的形式直接落到 GitHub 的 PR 页面上开发者打开 PR 就能在对应代码行看到机器人的意见。适合谁看如果你正在调研代码评审自动化方案或者已经在用 GitHub Actions 做 CI想引入 AI 评审角色这篇文章会很有参考价值。全文不涉及复杂的商业产品对比只关注自部署方案里最容易被忽略的工程细节。1. 代码评审这个环节为什么自动化这么多年还是没做好先说一个反直觉的现状代码评审工具并不少lint、SonarQube、Coverity、CodeQL各类工具轮番上阵但绝大多数团队的 PR 评审仍然是在靠人肉硬扛。这不是工具不够多而是“评审”这件事本身有两个层次现有工具只解决了其中一个。1.1 静态检查工具覆盖不到的盲区第一层叫“代码是否符合规范”。缩进对不对、变量命名规不规范、有没有明显空指针风险这一层 lint 和静态分析工具做得很好规则明确、执行速度快、误报低。第二层叫“这段改动是否真的符合业务意图”。这层要求审查者理解需求上下文能看出某个边界条件被漏掉了能发现一个异步任务的失败重试逻辑写反了能意识到某个对外接口的返回值在异常路径上没被处理。静态工具很难覆盖这个层面因为它们不理解业务只理解语法和数据结构。我们内部统计过过去半年里线上出过的问题有接近四成是在代码评审阶段就应该被拦住但因为 review 的人当时没看出上下文关联或者因为 PR 太大根本没看完就点了 Approve问题就溜过去了。1.2 人工评审的时间成本和注意力损耗人工评审的另一个痛点不是能力而是时间。一个中型团队的研发节奏每天会有十几个 PR 需要 review每个 review 平均要消耗 15 到 30 分钟。如果涉及跨端、跨服务的改动评审者还得先把相关代码调出来读一遍时间成本会直线上升。更麻烦的是注意力切换。人在多个任务之间切换时大脑需要重新加载上下文这个过程的损耗非常明显。早上集中注意力看代码的效率和下午连续开了两个会之后看代码的效率完全不是一个水平。自动化评审的价值不只是省时间更是让代码在一个稳定的、不受情绪和环境影响的标准下被审查。1.3 Hermes 在这里补上的那个关键环节Hermes 这类工具的出现本质上是把“第二层评审”的能力第一次变成了可能自动执行的事。它有一个足够大的上下文窗口去容纳仓库结构和多个文件的内容又通过代码检索把 PR 中涉及到的相关函数、引用关系找出来再把大模型的推理能力附着在 diff 之上。也就是说它不像静态工具那样只看 AST也不像人肉评审那样受限于注意力和时间它站在两者之间既有静态工具的覆盖率和即时性又有接近人的上下文理解能力。把 Hermes 接进 GitHub 之后它在每个 PR 开启时自动触发分析几分钟后返回一份带行号定位的评审意见人工 reviewer 只需要对这份初稿做增补和确认就能省下大量通读代码的时间。2. Hermes 的审查工作流与架构设计要真正把 Hermes 部署好先得理解它的内部工作流。它不是简单地把整个 PR 塞进大模型然后让它输出几条意见而是有一套完整的任务编排逻辑。这里拆开讲。2.1 触发 Pull Request 事件之后发生的事Hermes 通常以服务形式运行通过 GitHub App 或 Webhook 监听仓库事件。以我们用的 GitHub App 模式为例一个 PR 从创建或被更新开始会经历下面这些阶段事件监听服务收到pull_requestwebhook 事件判断action为opened、synchronize或reopened确认这是一个需要审查的新版本 PR。数据拉取调用 GitHub REST API 拉取 PR 元数据、patch diff、提交列表、变更文件列表、当前 base commit sha。上下文构建这一步是 Hermes 的核心。它会把 diff 按文件切分同时从仓库里检索这些文件对应的完整内容以及 PR 中可能引用到的关联文件构建出一个“变更上下文包”。推理分析将上下文包交给配置好的大模型按我们预设的评审维度逐条分析输出结构化结果问题等级、所在文件、所在行号、问题描述、建议修复方式。结果回传调用 GitHub API 生成 review把每条问题以comment的形式关联到具体的commit_id file_path line上。这套流程里比较容易被忽略的是第二步和第三步里的 commit 对齐。GitHub 的内联评论要求你指定一个 commit_id如果你把评论挂到旧的 commit 上PR 页面会显示成 outdated 状态开发者点进去还要手动查看当前版本的代码才知道问题还在不在。2.2 分工机制研判与审阅分离Hermes 在内部会做任务分派。它并不把整个仓库的所有代码一次性扔给模型而是先把 PR 中的文件按变更大小和关联度分成若干组启动多个执行单元去处理。每个执行单元负责一组文件先做逻辑研判最后汇总成一个整体 review。这个设计的意义在于模型的上下文窗口有限把 PR 里所有内容塞进一次请求会导致关键细节被稀释。分文件、分组处理后每一次分析聚焦的范围更小判断的精度更高。2.3 内联评论是怎么精确落到一行代码上的很多读者好奇 Hermes 是如何做到“评论定位到具体行”的。这块涉及到 GitHub 的评论 API 模型。GitHub 的 pull request review comment 接口要求传三个核心参数commit_id当前 PR 最新 commit 的 SHA。path文件在仓库中的路径。line要评论的目标行号。但这里有个坑line必须是 diff 中新增或修改的行context 行不行。模型返回的问题行号必须与 patch 中的新增行号吻合否则 API 会报错。Hermes 的处理方式是在请求模型前先把 patch diff 中的hunk头如 -18,6 18,8 解析出来生成一个“当前版本行号 → patch 行号”的映射表再让模型基于 diff 内容分析并输出新增行号最后服务端做一次校验如果行号没落在新增行上就降级为文件级评论或 code review 汇总评论。举个例子一个标准的 diff 片段是这样的 -12,7 12,7 public async TaskResult ExecuteAsync(string input) { - if (string.IsNullOrEmpty(input)) if (string.IsNullOrWhiteSpace(input)) { return Result.Fail(input cant be null or empty); }模型如果觉得IsNullOrWhiteSpace这个改动改变了原有校验语义应该评论第 15 行这个 15 就是新文件里的行号。Hermes 会把 15 映射到 patch 中 join 的 increment 区间确认它确实是一个新增行后才提交评论。如果模型输出的是一个删除行或未修改行Hermes 会改为追加到最近的下一行新增行上或者放到 review body 里作为综合意见。2.4 审查结果的两种呈现形式Hermes 支持两种输出模式理解它们的区别对后续配置非常重要模式呈现方式适用场景Review 内联评论每条意见都挂在对应文件、对应行上PR 改动量不大意见数量低于 20 条时体验最好Review summary 文件级评论重大问题走内联轻度建议归入 summary大型 PR上百个文件时避免刷屏我们实际体验下来内联评论在小 PR 上非常赞开发者可以对着行号直接修改但在大 PR 上Hermes 一次性输出几十条内联评论会产生“评论雪崩”开发者反而会用“Mark all as resolved”一键清空。所以后期我们强制把内联评论数量上限设为 15 条超出部分自动降级为 summary 里的分级清单。3. 部署 Hermes 之前需要先想清楚的三件事Hermes 的部署没有那么复杂但有几个前置决策会直接影响后续使用效果。这里说的是我们在部署前反复推敲过的三件事可以说是决定成败的关键。3.1 选择驻留方式容器部署、裸机进程还是 GitHub ActionHermes 可以以三种形态运行各有优劣。我们的选择是容器部署在两台 4C8G 的云主机上用 Docker Compose 编排。部署方式优势劣势推荐场景Docker Compose环境隔离、启停快、日志集中需要自己处理升级和持久化大多数中小团队的默认选择裸机 systemd资源占用最低排查网络问题直接环境一致性差依赖 Python 版本临时测试、快速验证GitHub Action零服务器成本和仓库天然集成不适合处理大批量仓库运行时受限webhook 长任务容易超时个人项目或低频项目我们不建议把 Hermes 直接做成 GitHub Action 来跑全量审查原因很简单Action 的执行时长受平台限制大 PR 拿到的上下文多分析过程耗时会超过 Action 的容忍上限容易超时中断。自托管服务才是最稳的形态可以把任务丢进异步队列慢慢跑跑完再回调 GitHub。3.2 权限模型设计只给必要的最小权限这是最容易踩坑的地方。很多人图省事直接生成一个 classic token勾上repo权限就给 Hermes 用。这样做的隐患显而易见token 一旦泄漏攻击者等于拿到了整个仓库的写权限。正确做法是使用 GitHub App 模式为 Hermes 单独创建一个 App只授予它需要的权限Pull requests: Read write用于提交 reviewChecks: Read write如果要用 Check Run 做状态检查Contents: Read-only用于读取仓库文件内容Metadata: Read-onlyGitHub 强制要求在 GitHub App 设置里还可以把 App 的访问范围限制到指定的几个仓库避免一个 Hermes 实例能操作组织内的所有代码。模型的 API Key 也不要直接写在 Hermes 的配置文件里。我们用的方式是存在环境变量文件中服务启动时注入配置文件里只引用环境变量名。3.3 成本与性能预估大语言模型驱动的代码审查最大的顾虑是 token 成本。这里给出一个我们实测的参考一个改动 20 个文件、涉及 800 行 diff 的 PRHermes 需要约 2 万 token 的输入包含 diff 相关文件片段。如果使用主流大模型 API每百万 token 的输入成本按当前市场价折算单个 PR 的推理成本大约在几毛到几块钱人民币之间。加上输出部分一个中型 PR 的单轮审查成本基本能控制在 5 块钱以内。性能方面Hermes 的响应时间主要消耗在大模型推理上。文件分组后并行分析一般 PR 平均 2 到 4 分钟返回结果大型 PR50 个文件以上可能需要 8 到 10 分钟。这就是为什么必须用异步任务机制webhook 收到事件后立刻返回 200后台任务慢慢跑结束后再通过 API 写入 review。4. 完整配置一份可落地的 Hermes 审查规则部署只是第一步真正决定自动化评审价值的是规则配置。这一节给出我们实际使用的配置方案可以直接抄。4.1 配置文件结构与核心字段Hermes 的主配置文件是一个 YAML 文件我们命名为hermes.yml放在仓库根目录下。核心字段如下version: 1.0 trigger: events: [opened, synchronize, reopened] branches: [main, release/*] review: model: deepseek-hermes # 也可以是其他兼容 OpenAI 协议的模型名 temperature: 0.2 max_comments: 15 max_file_comments: 3 dimensions: - bug_risk - security - performance - test_coverage - maintainability ignore: paths: - *.lock - *.min.js - dist/** - vendor/** - generated/** keywords: - chore(deps) - WIP comment: language: zh-CN style: concise # concise | detailed | educational severity_levels: [critical, warning, suggestion] auto_approve: enabled: true threshold: no_critical_issues其中review.max_comments是防刷屏的关键参数我们设置为 15。ignore.paths用于过滤掉不需要审查的自动化生成文件避免模型在 lock 文件或 dist 产物上浪费时间。keywords则用于识别某些特殊意图的 PR比如依赖升级类的 chore PR不需要走完整审查。4.2 提示词设定让模型输出更有价值Hermes 支持自定义审查提示词这部分是拉开效果差距的关键。同样一个模型用不同的提示词审查质量天差地别。我们使用的核心提示词包含以下要点要求基于证据评论只允许基于 diff 内容和仓库代码结构中的明确事实做评论禁止猜测性、泛泛而谈的意见。明确输出格式每条评论必须包含severity、line、issue、suggestion四个字段。禁止语言暴力不允许使用“差劲”“糟糕”等评价性词汇评论只描述问题本身和建议。边界优先优先关注边界条件、空值处理、并发安全、错误处理路径。这样设计的原因之一是模型在零样本情况下倾向于输出“这个函数逻辑清晰建议添加注释”这种废话型评论。把所有废话维度掐掉只保留真实问题机器人输出的每条意见才有说服力时间久了开发者才会认真看它的评论。4.3 多语言适配的细粒度规则我们团队的代码栈是 Python Go TypeScript 三件套Hermes 对不同语言需要不同的审查重点。以 Python 为例我们的 checklists 里包含是否有未处理的KeyError或过窄的except。是否有可变默认参数def f(x[])。并发场景是否用了线程不安全的全局状态。是否在finally里 return 吞掉了异常。Go 的重点则是error是否被正确传播有没有_ 吞错。goroutine 是否存在泄漏风险channel 是否有 close 保证。锁的粒度和顺序是否可能引发死锁。是否需要考虑 context 取消。TypeScript 侧则是any的使用是否必要。async 函数里的错误是否被 catch。可选链?.是否掩盖了逻辑错误。这些规则我们整理成了一份 markdown 审查清单放进 Hermes 的规则目录里模型在分析对应语言的文件时会自动加载对应的规则段。这部分工作前期需要一些投入但一次配好之后收益是持续性的。4.4 灰度上线策略先评论别直接卡流程关于自动化评审最大的抵制力量往往来自团队内部“一个 bot 凭什么给我的代码提意见”面对这种心态强行把它设为合并卡点基本等于引爆团队情绪。更稳妥的做法是灰度。我们把灰度分成三个阶段阶段一观察期Hermes 只发 summary 评论不开内联不跑 check status拉群观察一周收集开发者的反馈。阶段二信任期开放内联评论但把auto_approve设为 falseHermes 只提意见不做通过/拒绝的决策让人工 reviewer 主导结论。阶段三稳定期当团队的开发者开始主动回复 Hermes 的评论、甚至引用它的建议时再打开 check status把它作为可选的审查反馈接入流程。我们走完这三个阶段花了一个月这一步是整个自动化评审项目里最值得的投入。5. 实际跑一批 PR发现的问题和针对性调整配置完成到真正稳定之间有一段“调优地狱”。这里把我们遇到的最典型的几个问题和对应调整列出来给后来者省点时间。5.1 第一次跑通Review 显示“未发现问题”但代码里明显有 bug上线初期我们发现 Hermes 对 PR 的通过率异常高一度让我们怀疑它的审查能力。后来抓日志才发现问题不在模型而在上下文构建。默认配置下Hermes 会把仓库里的 README、项目简介等文件塞进上下文再加上 diff 本身的 token 占用真正留给核心代码分析的上下文反而被压缩了。模型在没有足够代码上下文的情况下只能依据 diff 里的一两行做肤浅判断自然输不出有价值的结论。调整方案有两个上下文优先级把 diff 列为最高优先级其他文件的读取改为按需按引用链检索而不是全量加载。pruning 规则对于超过阈值的大仓库跳过 CHANGELOG、README、docs 目录里的文件优先加载与变更文件有直接引用关系的模块。调整之后相同 PR 的问题检出率有了明显提升。5.2 评论是准的但体验被噪音毁了第二个问题是评论噪音。早期 Hermes 会对“代码没有注释”提建议会对“这个函数稍长”提建议这类建议虽然没错但价值极低而且会淹没真正重要的问题。结果是开发者打开 PR看到满屏的 suggestion 级别评论直接就略过了。我们的对策是在配置里把 suggestion 等级的触发条件调严severity_rules: suggestion: only_on: - potential_bug - missing_error_handling enabled: false也就是说suggestion 只保留那些和“潜在 bug”“错误处理缺失”相关的建议。纯代码风格类建议全部关闭。这个配置上线后Hermes 的平均单 PR 评论数从 27 条降到了 9 条但开发者对每条评论的确认率反而提高了很多。5.3 大 PR 超出上下文窗口时的降级策略一个超过 1000 行 diff 的大 PR即使经过文件分组单个文件的分析也可能因为上下文过长而触发模型限制。Hermes 的做法是把超大文件按函数或 hunk 切割成多个分析单元分别分析后再合并结果。这个方案有个副作用函数间跨引用关系会被切断。比如一个公共函数改了签名调用它的十个文件各自分析时很难发现调用方式不一致的问题。我们的应对是给 Hermes 加了一个“跨文件一致性检查”的独立任务专门扫描公共 API 变更相关的关联文件。5.4 两周 20 个 PR 的实测效果统计为了验证效果我们拿真实 PR 做了两星期对照实验。这里是一部分数据维度Hermes 发现问题数人工确认有效数误报数条件逻辑写反440错误处理缺失981并发安全隐患321测试覆盖不足1266资源泄漏数据库连接/文件句柄541建议类18513从这个表里能明显看出Hermes 在“条件逻辑、错误处理、资源泄漏”这几类硬问题上表现很不错而在“测试覆盖建议”和“通用建议”上误报率偏高。基于这个统计我们后期把测试覆盖建议关掉了因为开发者对它的反感远大于收益。6. 把自动化评审接入团队研发闭环的最后一步配置在单个 PR 上表现稳定之后下一步是考虑它和团队流程的关系。自动化评审不是要替代人而是要做人的第一道过滤器。6.1 与 Branch Protection 的配合方式关于要不要让 Hermes 作为 required status check我们的最终结论是不建议直接设成 required。理由很简单当 Hermes 成为通过合并的必要条件时开发者会有两个反应一个是在提交信息里写[skip review]试图绕开它另一个是把 PR 改小到不足以触发深度审查的程度这两种行为都不是我们想要的。更合理的做法是让 Hermes 以“非阻塞的 review 意见”存在。人仍然是最终批准者但批准前需要回复一句“已确认 Hermes 的 critical 意见已处理”或者“确认该问题不影响本次合并”。这既给了自动化评审地位又保留了人的决策权。6.2 评审结果回写到团队 IM我们还做了一步拓展把 Hermes 的审查摘要通过 webhook 转发到团队的内部群。每次 PR 审查完成后群里会收到一条消息包含 PR 标题、critical 问题数、warning 问题数和一条摘要链接。这样做的价值不在于通知而在于增加透明度开发组长可以每天花两分钟扫一眼有哪些 PR 被机器人标红了及时介入协调。6.3 多模型切换的实践心得Hermes 在设计上兼容多家大模型 API我们初期用的是一个通用模型后来切换到 DeepSeek 系列模型对中文代码注释的理解明显更自然。切换模型时只需要改配置里的接口地址和模型名不需要动 Hermes 本身。不同模型在评审风格上有明显差异。有的模型喜欢挑代码风格有的模型更关注逻辑缺陷。建议在切换后先跑一批历史 PR 做回归对比看它的输出是否保持稳定。模型选型这件事没有绝对最优解关键指标是“输出有效问题率”也就是每条被开发者标记为“确实需要修改”的评论占比。6.4 关于自动化评审的边界最后想分享一个我在整个落地过程中体会最深的事自动化评审的边界不在于模型能力而在于产品设计。Hermes 真正好用是因为我们把它的角色定义得非常清楚——它是 PR 的“初审者”而不是“批改老师”。它可以把最花时间、最需要耐心的通读工作自动完成把意见提交给人工 reviewer 做判定但最终是否通过、哪些问题必须改仍由人来决定。团队里现在的状态是Hermes 评论的问题开发者会认真回应有理有据地接受或反驳。个别前端同事甚至会手工 Hermes 说“你看看我这次改得行不行”它从工具变成了一种协作角色。这个过程让我意识到自动化和人不是替代关系而是协作关系。只要把边界画清楚机器人参与评审不仅不会引起反感反而能让团队把时间花在真正需要人脑的判断和讨论上。
返回列表