用Hermes搭建GitHub PR自动化代码评审体系,告别手动审查

发布时间:2026/9/9 9:13:29
用Hermes搭建GitHub PR自动化代码评审体系,告别手动审查 刚接手团队里两个核心仓库的代码评审时我一度觉得这事根本做不完。每天早上一打开 GitHubPR 列表里躺着十几条待 review 的变更每条 diff 短则几百行、长则上千行逐个看下来半天时间就没了还经常因为精力分散把真正有问题的改动漏过去。后来我搭了一套以 Hermes 为核心的自动化代码评审体系把 GitHub PR 的整个审查流程接了进去才算从这种机械劳动里挣脱出来。这套体系不是什么玄乎的东西它把读代码、找问题、提意见这件事拆成了一条可自动运转的流水线Hermes 负责监听 PR 事件、拉取变更内容、按规则和模型做分析最后把结构化的评审意见直接写回 GitHub配合分支保护规则形成质量门禁。这篇文章把我从选型到落地、再到踩坑的全部过程整理出来希望能给同样被 PR 审查淹没的朋友一个可以直接抄作业的参考。1. 项目概述与核心需求拆解1.1 手动 PR 审查到底慢在哪先说 PR 是什么。GitHub 的 Pull Request简称 PR本质上是代码合并前的一道闸口提交者把分支上的改动以请求的形式发起由其它参与者审查确认后再合并进主干。代码评审这个环节就是团队在合并前发现设计缺陷、逻辑错误、安全隐患的最后机会但恰恰是这个环节在实际工程里推进最慢。我观察了一段时间手动评审的瓶颈主要在三个地方。第一是量大一个活跃仓库一天能产生五六条 PR每条 PR 又分成若干 commitreviewer 必须把 commit 之间的差异逐行过一遍这种阅读极其消耗精力。第二是维度多一个大改动往往同时涉及代码规范、性能隐患、安全漏洞、可读性、测试覆盖等多个层面人眼很难在连续看十几个文件后保持同样的敏感度。第三是反馈周期长reviewer 看完了还要打字写意见写不清楚还得来回追问一条 PR 从发起到合并拖上两天是常态。这些痛点不是靠更认真一点就能解决的必须引入自动化。但市面上已有的工具也让我犹豫了一阵子比如 SonarQube、Codacy它们擅长做静态扫描能查出很多规范类问题但对这段逻辑有没有 bug这个接口设计合不合理这类需要语义理解的问题基本无能为力。1.2 Hermes 的定位是把机械检查和智能理解放到同一条流水线Hermes 在这个项目里不是一个传统意义上的静态分析工具而是一个 Agent智能体体系的名字。它要解决的核心问题不是再找一个检查器而是如何把各种检查器和理解能力编排成一条完整的自动化评审流水线。具体拆开看Hermes 承担了三层工作。第一层是事件感知它监听 GitHub 上的 PR 事件比如 pr opened、pr synchronize、pr ready_for_review一旦有新动作就立刻被唤醒。第二层是上下文采集它通过 GitHub API 拉取 PR 的描述、相关的 issue、变更文件列表、每行 diff、最近 commits 等信息把这些数据整理成模型可以理解的上下文。第三层是评审执行它按照预设的规则和策略对变更内容做静态扫描和大模型语义分析生成带文件位置、行号、严重级别、修复建议的评审意见再以 review 的形式回写到 GitHub 上。这套体系最适合两类团队。一类是仓库多、PR 频繁的中型团队代码评审已经成了明显的交付瓶颈另一类是希望建立代码质量基线、但又不甘心只靠人海战术去盯细节的团队。如果你只有一两个仓库、PR 量也很低那可能不值得花精力搭这套东西手动看看反而更快。1.3 方案选型为什么选择 Agent 而不是纯规则引擎在我最初的设计里其实考虑过三条路线。第一条是纯规则引擎写一堆正则和 AST 检查能精确识别特定模式但本质还是查表对没见过的写法毫无办法。第二条是纯大模型 Prompt 评审把一个 PR 的 diff 全丢给模型让它自由发挥优点是灵活缺点同样明显模型会一本正经地编出一些不存在的行号给出的建议也可能天马行空操作性和稳定性都差。第三条就是我最终采用的规则 Agent路线。这条路线里规则层负责确定性的检查项比如某个依赖版本是否过旧、是否有 TODO 残留、密钥是否被硬编码Agent 层则负责需要理解和推理的部分比如判断一条新的分支逻辑是否会导致空指针异常、异常处理是否合理、接口改动是否兼容上游调用。两层结果汇总后经过统一的评分逻辑输出。这样做的好处是确定性的问题不会因为模型的随机性而漏报复杂问题又不会被规则库的边界限制住。2. 整体架构与工作流设计2.1 触发链路Webhook、轮询和 GitHub Actions 怎么选整套系统的入口是谁来触发 Hermes我调研了三套主流方案实际用起来各有取舍。第一种是 Webhook 方案。在 GitHub 仓库配置一个 Webhook把 PR 相关的事件推送到我们自己的服务地址服务端收到事件后就启动评审流程。优点是实时性最好事件发生到评审触发基本在一秒内缺点是你必须有一个公网可访问的服务端点而且要考虑重试、签名校验、事件去重这些细节。第二种是轮询方案。写一个定时任务每隔几分钟调用一次 GitHub API查询最新更新的 PR 列表发现有新的就处理。优点是实现简单不要求公网服务缺点是会有延迟而且如果仓库很多API 调用量会很大容易撞到 GitHub API 的速率限制。第三种是 GitHub Actions 方案。把 Hermes 作为 Action 跑在 GitHub 的 CI 环境里PR 事件直接触发 workflow。这种方案最大的好处是不用自己维护服务端基础设施缺点也很明显环境是受限的如果你有自己的私有模型服务、内网依赖仓库就比较难打通。我最终采用的是 Webhook 为主、轮询为辅的混合模式。核心仓库用 Webhook 保证实时性边缘仓库和备份链路用轮询兜底确保即使 Webhook 漏了事件也能在几分钟内被补救回来。2.2 Hermes 的核心模块与职责边界整个系统我按职责拆成了五个模块事件网关、数据采集器、评审引擎、回写执行器和配置中心。它们之间的协作关系可以理解成一条流水线每一段只负责自己的事尽量避免耦合。事件网关负责接收和校验 GitHub 的 Webhook 请求解析事件类型做签名验证然后把标准化的事件消息丢到内部队列里。数据采集器订阅队列每收到一个事件就去 GitHub API 拉取对应的 PR 元数据、变更文件列表和 diff同时把拉取到的原始数据缓存下来避免重复请求。评审引擎是核心它先跑规则层扫描再组装上下文交由模型分析最后汇总打分。回写执行器拿到评审结果后决定是提交一个 PR review、在对应代码行打 comment还是通过 Check API 汇报一个检查状态。配置中心管整个链路的行为参数比如哪些目录跳过检查、哪些规则是 error 级别、多大的 PR 需要人工介入。这个拆法帮助我解决了一个很实际的问题依赖方向明确。评审引擎不需要知道 Webhook 的报文格式数据采集器不关心模型怎么调用任何一个模块都可以单独升级替换。比如最开始我用的是一个通用大模型后来换成了效果更好的专用代码模型改的只是评审引擎内部的一层适配代码其它模块完全不动。2.3 规则配置不能把所有事情都托付给大模型如果你用过代码评审类工具就会知道配置项一旦设计得不好落地时就是灾难。我见过有人把所有规则都塞进一个巨大的 JSON 文件最后根本没人敢改。Hermes 的配置我坚持用 YAML按检查维度 严重级别 中止动作三层结构组织。下面是我项目里实际用的一段简化配置示例review: rules: - id: no_hardcoded_secret dimension: security severity: error action: block_merge pattern: (password|api_key|secret)\\s*[:]\\s*[\][^\][\] - id: no_todo_left dimension: style severity: warning action: comment_only pattern: TODO|FIXME - id: avoid_stdlib_random_for_security dimension: security severity: error action: block_merge detect: semantic special_paths: - path: docs/** enabled: false - path: *.lock enabled: false limits: max_files_in_review: 40 max_diff_size: 2000 large_pr_action: notify_human你可能会问这里同时有 pattern 和 semantic 类型的规则怎么区分呢pattern 类型是纯正则会快速匹配命中即判定semantic 类型则要交给模型去判断是否命中。我在avoid_stdlib_random_for_security这条规则上就用了语义检测因为代码里可能不会直白地出现random但模型能看出来你用的是Math.random()还是安全的加密随机数。这种确定性优先、语义兜底的配置思路比什么都丢给模型要稳得多。3. 实操落地从零搭建 Hermes GitHub PR 审查3.1 环境准备Hermes 安装部署的正确姿势Hermes 本身是一个开源项目你可以把它当成一个可部署的 Agent 服务安装方式有两种一种是源码部署一种是用 Docker 跑容器。我第一次尝试时图省事直接在服务器上pip install hermes-agent结果因为依赖版本冲突折腾了一个多小时后来老老实实改用 Docker反而十分钟就起来了。用 Docker 部署是最省心的方式。拉取镜像、编写 docker-compose 文件、映射端口和挂载配置目录三步走。下面是一个可用的 docker-compose 参考services: hermes: image: hermes-agent:latest container_name: hermes ports: - 8080:8080 environment: - GITHUB_TOKEN${GITHUB_TOKEN} - MODEL_API_KEY${MODEL_API_KEY} - MODEL_ENDPOINT${MODEL_ENDPOINT} volumes: - ./config:/etc/hermes/config - ./logs:/var/log/hermes restart: unless-stopped部署时有一个细节我必须提醒容器里的时区最好显式指定否则日志时间和 GitHub 事件时间对不上排查问题时会很抓狂。另外GITHUB_TOKEN 的权限不要图省事给全部仓库的写权限建议单独建一个机器人账号用 GitHub App 的形式授权把 Token 权限收敛到目标仓库的最小集。我最初用个人 Token 接进去后来有同事离职导致 Token 失效全链路瘫了半个多小时那之后才换成了独立的机器人账号。然后确认两件事目标仓库的 Webhook 能不能访问到这台服务器以及 Hermes 所在的网络能不能正常访问 GitHub 的 API 域名。这两个是部署后的基础连通性检查没通过的话后面所有流程都跑不起来。3.2 GitHub 接入与 PR 数据采集接入 GitHub 的第一步是让 Hermes 具备看到PR 的能力。这里我走的是 GitHub App 路线因为相比个人 TokenGitHub App 的权限粒度更细比如它可以只授权特定仓库的 pull requests 读取权限并且每次请求都带上自己独立的身份。GitHub App 创建完之后要在仓库设置里配置 Webhook。Payload URL 指向 Hermes 的/webhook/pr接口Content type 选择application/jsonSecret 填一段随机字符串Events 勾选 Pull requests 即可。注意如果你是第一次配置建议先把webhook 的 Recent Deliveries 页面打开确认 GitHub 能成功把测试事件 POST 到你的服务。服务端接收 Webhook 后需要先验证签名防止来路不明的请求打进来。我用的签名校验逻辑大概长这样以 Python 为例import hashlib import hmac def verify_signature(payload_body, signature_header, secret): expected hmac.new( secret.encode(), payload_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest( fsha256{expected}, signature_header )拿到合法事件后就要获取这次 PR 的变更内容。不要从 Webhook 的 payload 里直接读 diff因为它给的字段不完整。正确做法是拿到issue编号后主动调用 GitHub API 拉取文件列表。常用的几个接口如下# 获取 PR 基本信息 curl -H Authorization: Bearer $TOKEN \ https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number} # 获取 PR 的文件变更列表 curl -H Authorization: Bearer $TOKEN \ https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/files第二个接口返回的数组中每个元素都会包含filename、status、additions、deletions、patch等字段patch字段就是标准 diff 片段。把这些片段聚合成一个包就是后续评审引擎的输入。需要注意GitHub API 对超大 PR 有分页和截断/files接口默认一页最多返回 30 条超过 300 个文件的 PR 还会直接拒绝。我在这里给 Hermes 定了一个策略单 PR 变更文件数超过 40 或 diff 行数超过 2000 时不自动化评审改为通知人工 reviewer因为这种巨型 PR 本身就是一种代码坏味道模型硬啃反而容易产生大量误报。3.3 让 Hermes 生成可落地的评审意见数据采集完成后真正的重点来了怎么让 Hermes 输出看起来像人写的、又有实操价值的评审意见。这一步的成败很大程度上取决于 Prompt 的组装方式。我先说一个容易犯的错。很多人把整个 diff 直接怼给模型然后说帮我看看有什么问题这种泛泛的提问得到的也是一堆泛泛的答案而且模型还会因为上下文过长而忽略关键信息。我现在的做法是把 Prompt 拆成三段结构系统设定、待审代码、输出约束。系统设定里明确角色和评审目标比如你是一名有十年经验的代码审查员关注正确性、安全性、性能和可维护性不要做出泛泛的风格建议待审代码部分除了 diff 本身还要附上 PR 描述、相关联的 issue 标题、项目使用的技术栈和关键业务背景输出约束是最关键的要求模型严格按照 JSON 结构返回每个问题的文件路径、行号、问题类型、严重级别和具体修复建议。一段典型的 Prompt 模板长这样[SYSTEM] 你正在为一个使用 Python FastAPI 的项目做代码审查。 重点检查空指针/未捕获异常、并发安全问题、SQL 注入风险、 数据校验缺失、明显的性能瓶颈。 忽略纯代码风格问题。 如果某一行你无法确认是否有问题不要输出猜测。 [REVIEW_REQUEST] PR 标题{title} PR 描述{description} 变更文件数{file_count} 以下是变更内容 {diff_content} [OUTPUT_FORMAT] 只输出如下 JSON 数组不要输出任何其他内容 [ { file: src/main.py, line: 42, type: bug|security|performance|maintainability, severity: error|warning|suggestion, reason: 具体问题和原因, suggestion: 带代码示例的修复建议 } ] 如果没问题输出空数组 []。关于行号这一点我要特别强调。模型直接数 diff 里的行号经常数错尤其当文件内容包含多行的新增和删除混合时你让模型从 diff 文本数出原文件的行号基本不可靠。我的解决方案是先让模型输出它看到的是哪个代码段再通过后端脚本把这段代码映射回 diff 的 hunk 行号最后再转成 GitHub review comment 需要的绝对行号和位置参数。一句话不要信任模型输出的行号要把行号计算放到确定性代码里做。3.4 结果回写与质量门禁评审结果生成后推送回 GitHub 的方式会直接影响要不要合并这个流程决策。我第一次做的时候只把意见以普通 issue comment 形式贴上去结果发现这些意见很容易被淹没在讨论流里根本起不到拦截作用。后来我改成了标准的 Pull Request Review Check Run 的组合拳。提交 review 的请求如下curl -X POST \ -H Authorization: Bearer $TOKEN \ -H Accept: application/vnd.githubjson \ https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews \ -d { commit_id: HEAD, event: REQUEST_CHANGES, body: Hermes 自动化评审发现 2 个阻断性问题需要修复后再次提交。, comments: [ { path: src/main.py, line: 42, side: RIGHT, body: 这里存在 SQL 注入风险建议使用参数化查询。 } ] }注意path和line必须能准确对应到 PR 里的某个文件某一行否则 GitHub 会返回 422 错误。side: RIGHT表示注释贴在新增代码侧如果问题在删除的旧代码里这个参数要改成LEFT否则也会校验失败。Review 提交之后用户还必须在 PR 的合并保护里配置分支规则把 Hermes 的 Check Run 设为必查项。我是这样设置的Hermes 审查结束后调用 Check API将检查状态设为completed结论设为success或failure。当 Hermes 发现的 error 级别问题数量大于零时结论就是failureGitHub 的分支保护会直接阻止 merge 按钮warning 或 suggestion 则不会阻断合并但会在 PR 列表上留下一个醒目的提示。这样就把建议和门禁分开了避免因为一个不痛不痒的提示阻塞整个团队的发布节奏。4. 常见问题与排查技巧实录4.1 处理 GitHub 访问异常与请求失败如果你是在本地或者内网环境跑 Hermes大概率会遇到 GitHub 页面打不开、API 请求超时、clone 仓库失败这一类网络问题。我的经验是先把网络是否可达和代码是否有 bug分开。很多新手看到Connection timed out就开始查日志折腾半天发现是环境问题白费力气。排查时可以分三步走。第一步在 Hermes 运行的那台服务器上直接 curl 一下 GitHub API 接口确认基本连通性。第二步查看服务器上的 DNS 解析是否正常如果解析缓慢或者解析到了错误的 IP可以考虑换成可靠的公共 DNS。第三步如果你们公司有内网代理确认代理的白名单里放行了api.github.com和codeload.github.com等必要域名且 Hermes 进程正确配置了代理环境变量。这里我要多说一句不要因为本地访问 GitHub 不稳定就想着把第三方镜像地址写死进生产配置里。代码评审链路一旦依赖了非官方入口首先安全问题不可控你不知道中间经过了什么其次镜像地址的可用性、数据实时性都没保障今天能跑明天可能就挂了最后坑的还是一线工程师。正规做法是把 Hermes 部署在一个网络质量可靠的 CI 环境里让它用官方 API 工作网络层面的稳定性问题在环境层解决而不是在代码层绕过。4.2 PR 数据不完整或评审意见定位失败在接入初期我遇到最多的报错是 GitHub 返回 422提示comments里的行号或路径无效。表面看是行号算错了实际原因往往更隐蔽某一行的内容被Unicode字符影响、文件路径因为大小写问题在 Linux 上对不上、或者是 PR 中的文件在评审期间被新 commit 修改导致原来的 diff 行号已经失效。这里有一个实用的兜底策略提交 review comment 之前先用对应 commit 的 files 接口拿到最新 diff 快照做一次比对如果目标行号和当前快照对不上就把这条意见降级为普通 review body 中的文字描述至少保证意见不会整个丢失。代码评审工具的定位是帮人提升效率不是生成本身完美的艺术品能稳定输出可用意见比偶尔输出完美意见更重要。还有一种情况需要特别注意当一个 PR 里同时有文件重命名和内容修改时GitHub API 返回的filename可能是新路径也可能带previous_filename字段。如果把previous_filename当当前路径去提交评论就会定位错误。我在数据采集层做了一层适配统一使用status为renamed的文件的最新路径作为评论目标避免误伤。4.3 审查结果不准怎么办Hermes 上线第一周团队反馈最集中的问题就是误报太多。模型经常把业务里故意的写法当成 bug比如某些场景下开发者就是希望忽略某个异常规则引擎也会把符合团队约定的代码标记为不推荐。误报多的直接后果是团队不再信任 Hermes再好的工具一旦失去信任就会变成被无视的摆设。我调整了几个参数后情况明显改善。首先是加置信度阈值只有模型输出时置信度高于 0.85 的问题才进入阻断级别低于阈值的一律降级为 suggestion。其次建立了忽略名单机制允许仓库维护者把某些文件、目录、特定规则标记为 ignored比如锁文件、生成的代码、docs 下的示例代码。第三我把 Hermes 的最终结论和详细意见分离只有 error 级别的意见会触发REQUEST_CHANGESwarning 以下全部走COMMENT避免因为一点可改可不改的建议挡住整个发布。另外团队反馈链路也很重要。我在每次 PR 的 Hermes review 里都会附带一个反馈入口提示如果开发者认为某条意见是误报可以一键点关闭这条意见并附带理由。这些反馈数据会定期统计反过来用于调整规则阈值和 Prompt 模板。闭环跑起来之后误报率每周都在肉眼可见地下降。4.4 构建失败与 PR 审查的联动问题还有一个很容易被忽略的场景当 PR 触发的 CI 构建失败时Hermes 的检查结论怎么处理。典型情况是某个 PR 改了依赖版本导致 Flutter 项目的 Gradle 插件加载失败报错信息类似failed to apply plugin dev.flutter.flutter-gradle-plugin。这时候如果 Hermes 只看代码 diff、不看构建日志很有可能会给出代码没问题的结论和实际状态完全相反。我的建议是Hermes 不要试图替代 CI而是要把 CI 的结果当作一个上下文输入。具体做法是在 Hermes 的检查列表里加入构建状态这一项如果 GitHub 上报的持续集成状态是失败或待定Hermes 的最终结论最多给到 warning不允许给 success并会在 review body 里明确写一句当前 PR 的 CI 状态尚未通过请先解决构建问题。这样 PR 的合入门禁逻辑保持唯一且一致不管是人审还是 AI 审都不会越过 CI 这道红线。5. 一些经验与建议整套 Hermes GitHub PR 审查体系上线到现在我最大的体会是自动化代码评审的价值不在于替代人而在于把人的精力从机械检查中解放出来。Hermes 每天帮我处理掉大量重复性、确定性的检查把有没有硬编码密钥有没有明显逻辑漏洞这类问题在我看之前就过滤一遍我真正要看的是那些它判断不了、需要业务判断力和架构思考的深水区。如果你想在自己的团队落地这套东西建议不要一上来就追求完全自动阻断。先跑两周影子模式让 Hermes 在每条 PR 上生成意见但不参与合并门禁收集团队反馈把规则阈值调整到一个大家都能接受的水平再逐步放开阻断能力。自动化评审工具也是需要驯化的它不是装上就能用而是要陪着团队一起磨合。最后再分享一个小技巧可以给 Hermes 生成的意见加一个每周汇总报表统计每个仓库发现的问题类型分布和触发趋势这些数据对团队改进编码规范和补齐测试策略非常有参考价值比单纯做代码审计有效得多。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询