基于Hermes的自动化代码评审:部署、配置与落地实践
2026/9/8 23:09:25 网站建设 项目流程

把团队代码评审里最耗精力的那部分活儿交给一个机器人,这事我们惦记了很久。起因其实挺朴素: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 从创建或被更新开始,会经历下面这些阶段:

  1. 事件监听:服务收到pull_requestwebhook 事件,判断actionopenedsynchronizereopened,确认这是一个需要审查的新版本 PR。
  2. 数据拉取:调用 GitHub REST API 拉取 PR 元数据、patch diff、提交列表、变更文件列表、当前 base commit sha。
  3. 上下文构建:这一步是 Hermes 的核心。它会把 diff 按文件切分,同时从仓库里检索这些文件对应的完整内容,以及 PR 中可能引用到的关联文件,构建出一个“变更上下文包”。
  4. 推理分析:将上下文包交给配置好的大模型,按我们预设的评审维度逐条分析,输出结构化结果(问题等级、所在文件、所在行号、问题描述、建议修复方式)。
  5. 结果回传:调用 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 Task<Result> ExecuteAsync(string input) { - if (string.IsNullOrEmpty(input)) + if (string.IsNullOrWhiteSpace(input)) { return Result.Fail("input can't 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 Action

Hermes 可以以三种形态运行,各有优劣。我们的选择是容器部署在两台 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(用于提交 review)
  • Checks: Read & write(如果要用 Check Run 做状态检查)
  • Contents: Read-only(用于读取仓库文件内容)
  • Metadata: Read-only(GitHub 强制要求)

在 GitHub App 设置里还可以把 App 的访问范围限制到指定的几个仓库,避免一个 Hermes 实例能操作组织内的所有代码。

模型的 API Key 也不要直接写在 Hermes 的配置文件里。我们用的方式是存在环境变量文件中,服务启动时注入,配置文件里只引用环境变量名。

3.3 成本与性能预估

大语言模型驱动的代码审查,最大的顾虑是 token 成本。这里给出一个我们实测的参考:

  • 一个改动 20 个文件、涉及 800 行 diff 的 PR,Hermes 需要约 2 万 token 的输入(包含 diff + 相关文件片段)。
  • 如果使用主流大模型 API,每百万 token 的输入成本按当前市场价折算,单个 PR 的推理成本大约在几毛到几块钱人民币之间。
  • 加上输出部分,一个中型 PR 的单轮审查成本,基本能控制在 5 块钱以内。

性能方面,Hermes 的响应时间主要消耗在大模型推理上。文件分组后并行分析,一般 PR 平均 2 到 4 分钟返回结果,大型 PR(50 个文件以上)可能需要 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 支持自定义审查提示词,这部分是拉开效果差距的关键。同样一个模型,用不同的提示词,审查质量天差地别。我们使用的核心提示词包含以下要点:

  1. 要求基于证据评论:只允许基于 diff 内容和仓库代码结构中的明确事实做评论,禁止猜测性、泛泛而谈的意见。
  2. 明确输出格式:每条评论必须包含severitylineissuesuggestion四个字段。
  3. 禁止语言暴力:不允许使用“差劲”“糟糕”等评价性词汇,评论只描述问题本身和建议。
  4. 边界优先:优先关注边界条件、空值处理、并发安全、错误处理路径。

这样设计的原因之一是,模型在零样本情况下倾向于输出“这个函数逻辑清晰,建议添加注释”这种废话型评论。把所有废话维度掐掉,只保留真实问题,机器人输出的每条意见才有说服力,时间久了开发者才会认真看它的评论。

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设为 false,Hermes 只提意见,不做通过/拒绝的决策,让人工 reviewer 主导结论。
  • 阶段三(稳定期):当团队的开发者开始主动回复 Hermes 的评论、甚至引用它的建议时,再打开 check status,把它作为可选的审查反馈接入流程。

我们走完这三个阶段花了一个月,这一步是整个自动化评审项目里最值得的投入。

5. 实际跑一批 PR,发现的问题和针对性调整

配置完成到真正稳定之间,有一段“调优地狱”。这里把我们遇到的最典型的几个问题和对应调整列出来,给后来者省点时间。

5.1 第一次跑通:Review 显示“未发现问题”,但代码里明显有 bug

上线初期我们发现 Hermes 对 PR 的通过率异常高,一度让我们怀疑它的审查能力。后来抓日志才发现问题不在模型,而在上下文构建。默认配置下,Hermes 会把仓库里的 README、项目简介等文件塞进上下文,再加上 diff 本身的 token 占用,真正留给核心代码分析的上下文反而被压缩了。模型在没有足够代码上下文的情况下,只能依据 diff 里的一两行做肤浅判断,自然输不出有价值的结论。

调整方案有两个:

  1. 上下文优先级:把 diff 列为最高优先级,其他文件的读取改为按需按引用链检索,而不是全量加载。
  2. 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 说“你看看我这次改得行不行”,它从工具变成了一种协作角色。这个过程让我意识到,自动化和人不是替代关系,而是协作关系。只要把边界画清楚,机器人参与评审不仅不会引起反感,反而能让团队把时间花在真正需要人脑的判断和讨论上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询