
1. 项目动机与整体设计思路1.1 代码评审为什么需要自动化先说一个我自己的体验。以前在团队里做 Code Review遇到大 PR几十个文件上千行 diff前后拖上两三天很常见。reviewer 累开发者也焦虑合并窗口一拉长冲突就来了——Git 里 PR 被别的提交“插队”导致冲突的情况团队里几乎每周都要处理一次。代码评审的核心价值其实很清晰保证代码质量、传递团队规范、提前发现缺陷。但人力和精力是有限的常规性的检查项反复靠人肉盯特别浪费。比如有没有把密钥提交进仓库、commit message 格式规不规范、改动的文件范围是否失控、异常分支有没有覆盖等等。这些问题技术含量不高但漏掉一个就可能出事。后来我开始琢磨把这块自动化。正好接触到了 Hermes——一个能自主执行复杂任务的智能体框架。它跟普通脚本最大的区别在于脚本只能按固定规则跑Hermes 可以理解上下文、做判断、再触发后续动作。而且它不是只能跑在本地GitHub 上的 PR 事件可以通过 Webhook 或 Actions 直接唤醒它等于给团队配了一个不知疲倦的 reviewer而且每次审查的标准都稳定。1.2 Hermes 在 PR 审查中的角色定位明确一下 Hermes 的定位它是审查的执行大脑不是仓库的门禁系统。什么意思呢我见过一些团队直接把自动化评审做成“不通过就不让合并”的硬卡点结果规则稍微激进一点整个团队怨声载道。我个人的方案是把 Hermes 审查结果分两个层级第一层是机器必查项包括密钥泄漏、依赖漏洞、明显的语法错误、超大数据量 diff。这类一旦命中就让 PR 直接带红牌。第二层是建议项包括代码风格一致性、函数拆分是否合理、命名是不是容易误解、有没有明显的重复逻辑。这类只作为评论输出提醒开发者自查不阻断合并。这个设计的核心逻辑是自动化审查替代掉低级重复劳动把人的注意力集中在真正需要人判断的地方而不是试图完全取代审查者。这样既提高了效率又不会让团队对自动审查产生对抗情绪。1.3 适合什么团队与场景如果你满足下面任意两条这套方案就很值得参考PR 数量多平均每天超过 5 个但核心 reviewer 就一两个人团队里新成员较多代码规范落地困难review 时经常要重复说同样的话出现过敏感信息泄漏、依赖投毒等“低级但致命”的事故希望给新人提供即时反馈而不是等 3 天后合并时才发现方向错了单独一个人做开源项目其实也适用。我自己维护的几个开源仓库低频但偶尔也会收到外部贡献者的 PR。用 Hermes 过一遍比自己逐行看得高效尤其是那些改动跨多个模块的 PR机器人先把有明显问题的位置挑出来我再针对性地看核心逻辑节奏舒服很多。2. 环境准备与工具链搭建2.1 仓库、Token 与 Actions 的三方关系先理清整体架构不然配置起来容易晕。整个自动化链路涉及三方仓库是事件来源它会产生 PR 打开、同步、评论等事件GitHub Actions 或 Webhook 是事件的传递通道Hermes 是消费事件并执行审查的执行者。我实测下来最省事的方案是直接用 GitHub Actions 来触发 Hermes让 Hermes 以容器的方式跑在 Actions 的临时环境里。这样带来的一个额外好处是根本不需要自己维护一台常驻服务器也不依赖本地网络的稳定性。关注隐私的仓库比如组织内部代码则建议把 Hermes 部署在自托管的 runner 上代码不出内网更可控。需要一个 GitHub Token用来拉取 PR 的 diff 内容和提交审查评论。建议用 GitHub App 而不是个人 Token因为 App 的权限粒度更细可以只授权目标仓库机器人身份和成员身份分离后续审计也清楚。2.2 Hermes 的安装与配置文件结构Hermes 的安装并不复杂。如果只是想先在本地跑通核心审查逻辑可以直接用容器镜像docker pull hermes-agent/hermes:latest docker run -d --name hermes \ -v $(pwd)/hermes-config:/etc/hermes \ -e HERMES_GITHUB_TOKENghp_your_token_here \ hermes-agent/hermes:latest建议把配置文件放在独立目录后续改规则不需要重建容器。目录结构我习惯这样组织hermes-config/ ├── config.yml # 主配置仓库列表、触发事件类型 ├── review-rules/ # 审查规则的存放目录 │ ├── security.yml │ ├── style.yml │ └── structure.yml ├── prompts/ │ ├── review-system.md │ └── review-user.md └── logs/config.yml 的核心内容大致是这样repository: owner: your-org name: your-repo trigger: events: - pull_request - pull_request_review_comment review: diff_max_files: 100 comment_max_lines: 300 rules_dir: /etc/hermes/review-rules prompts: system: /etc/hermes/prompts/review-system.md user: /etc/hermes/prompts/review-user.md2.3 Actions 工作流把 PR 事件接到 Hermes在仓库里新建.github/workflows/hermes-review.yml这段配置的作用是告诉 GitHub只要有 PR 打开或更新就去跑一次 Hermes 审查。name: hermes-pr-review on: pull_request: types: [opened, synchronize, reopened] jobs: hermes-review: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkoutv4 with: fetch-depth: 0 - name: Run Hermes Review uses: docker://hermes-agent/hermes:latest env: HERMES_GITHUB_TOKEN: ${{ secrets.HERMES_TOKEN }} HERMES_PR_NUMBER: ${{ github.event.pull_request.number }} HERMES_REPO: ${{ github.repository }} with: args: --review-pr注意fetch-depth: 0必须把完整历史拉下来不然 Hermes 做增量 diff 分析时拿不到目标分支的最新提交对比会出错。如果你是自托管的部署不使用 Actions那就在 GitHub 仓库的 Settings - Webhooks 里添加一个 webhookpayload URL 指向 Hermes 服务的/webhook/github接口然后选择Let me select individual events勾选Pull requests即可。2.4 一次实测的完整前置准备工作本地跑通前我建议先准备一个测试仓库别直接拿生产仓库试。fork 一个测试仓库到自己的命名空间或者新建一个私有仓库全部用测试代码。创建一个机器人专用 GitHub Token授予reposcope如果结构更清晰优先考虑 GitHub App。把 token 添加到仓库的 Actions secrets 里管理入口在仓库的 Settings - Secrets and variables - Actions。本地先运行一次 Hermes 容器确认它能正常启动。实测中常见的坑是时区与桌面环境的差异Actions 环境中默认 UTC日志里看到的时间与自己本地对不上不要慌注意转换即可。3. 核心审查流程与实现细节3.1 一次审查从触发到评论的完整链路整个自动审查从 Push 到 PR 开始到评论落在 PR 上结束共经历五个环节第一步事件捕获。PR 的 opened 或 synchronize 事件由 Actions 监听这一瞬间代码快照还比较新鲜。如果 PR 经历了多次反复修改只会对每个新版本触发一次新的 review而不会重复 review 旧版本。第二步差异提取。Hermes 调用 GitHub 的 compare API 拿取diff。注意要拿base...head的三点差异而不是两点差异。三点对比可以确保base分支本身新增的提交不会被误判为 PR 的改动。第三步规则加载。Hermes 读取 review-rules 目录下的规则配置把与当前 PR 语言、框架相关的规则筛选出来。第四步模型分析。合并 diff 内容与系统提示词交给语言模型推理。这也是 Hermes 作为 agent 的强项它会逐文件列出问题点与文件路径精度明显高于粗略的“这段代码有问题”式输出。第五步结果上报。Hermes 遍历分析结果非阻断性问题会以 review comment 形式贴在对应代码块上阻断性问题则附加Request changes状态标记。这五步全部通过 API 完成实际跑一轮 PR30 个文件上下从触发到评论出结果大概 40~90 秒。如果只做增量评论——就是只评论新增或修改的行——时间还能缩短 30% 左右。3.2 diff 获取的核心 API 细节Hermes 内部使用这样的逻辑去获取差异import requests headers { Authorization: fBearer {token}, Accept: application/vnd.github.v3.diff, } url fhttps://api.github.com/repos/{repo}/compare/{base}...{head} resp requests.get(url, headersheaders) diff_content resp.text这里有一个关键小技巧Accept头务必要带application/vnd.github.v3.diff才会返回一个紧凑的 diff 文本。如果不带这个头返回的是 JSON 格式的 commit 元数据没有具体的代码行内容变动。这个 header 在 GitHub REST API 里属于“自定义媒体类型”用普通 JSON accept 拿不到 diff。3.3 审查输出的结构化处理拿到 diff 后Hermes 不会把整段文本一股脑丢给模型而是先做切分。diff 过大时超过配置阈值比如上面 config.yml 里的 100 个文件就只审查改动最大的前 100 个文件并在结果中提示其余文件未覆盖避免超出模型上下文限制。切分后的每个文件片段会标注这三个信息原始行号范围新增行号范围变更类型新增、删除、修改、重命名这个信息直接影响到审查意见怎么定位到代码位置。GitHub 的 review comment API 要求按 position 或 line 参数来定位如果不用新增行号去计算评论就会贴错位置。3.4 评论上报与并发安全评论阶段用的 API 是POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews请求体结构{ commit_id: 被审查的那个 head 提交的 sha, event: COMMENT, comments: [ { path: src/main.py, line: 45, body: 这里的异常捕获过于宽泛建议缩小范围。 } ] }关于并发安全有一个容易踩的坑当一个 PR 短时间内连续 push 好几次Actions 会同时触发多个 Hermes 任务导致重复评论。解决办法是在 workflow 里加concurrency配置让同一 PR 的新任务自动取消旧任务concurrency: group: hermes-review-${{ github.event.pull_request.number }} cancel-in-progress: true4. 审查规则定制与提示词工程4.1 规则文件怎么设计才有用Hermes 的审查规则我推荐用 YAML 声明式描述它比纯提示词更可控规则之间的优先级也能明确标出来。先给一个安全规则的示例id: security/secret-leak severity: error pattern: - (?i)(api[_-]?key|secret|token)\\s*[:]\\s*[\][A-Za-z0-9_\\-]{16,}[\] message: 检测到疑似密钥硬编码请改用环境变量或密钥管理服务。这个正则专门匹配常见的key 一串长字符模式命中即报 error阻断合并。风格类规则则要克制。我见过团队把“禁止超过 80 字符”这样的规则也设为 error结果大量历史代码频繁被卡开发体验很糟糕。风格规则设成 warning 级别就够用了。4.2 结构审查与技术栈版本绑定再说一个建议规则必须跟技术栈绑定不要写一套规则打天下。比如 Python 项目重点看 import 位置、except 异常粒度、with 语句使用是否规范前端项目则关心 useEffect 依赖数组、memo 是否滥用、CSS-in-JS 的对象稳定性等。配置文件里可以这样声明语言适配enabled_languages: - python - javascript python: max_function_length: 80 disallow_bare_except: true javascript: forbid_any_console_log: false规则引擎会自动根据变更文件的扩展名只加载对应语言的规则子集。4.3 提示词让 Hermes 真正读懂代码意图规则匹配只能解决“查得出明显问题”但要审查代码逻辑本身还得靠模型分析。这就涉及到提示词设计我拿实际在用的 system prompt 做一个简化范例你是一名资深代码评审专家。你将收到一个 GitHub Pull Request 的 diff 内容。 请从以下维度进行审查 1. 正确性是否存在潜在的空指针、越界、并发竞争或逻辑错误 2. 可维护性函数是否过长、依赖是否混乱、命名是否准确 3. 安全性是否存在注入风险、路径遍历、敏感信息泄漏 4. 一致性与仓库现有代码风格是否一致 输出要求 - 每个问题必须有对应的文件路径和行号 - 按 severity 降序排列error/warning/suggestion - 没有问题时回复 LGTM 而不是输出空列表 - 如果 diff 内容不足以判断请指出缺少的信息不要猜测这里有个细节值得强调提示词里明确写了“没有问题时回复 LGTM”很多模型在不确定时会倾向于硬找几个问题凑数不加这句的话假阳性率会明显偏高。user prompt 则是把改动的文件和 commit message 放进去请审查以下 Pull Request 仓库{repo} 分支{head} - {base} Commit 标题{commit_title} Commit 描述{commit_description} Diff 内容 {diff_content}4.4 规则更新与灰度发布机制审查规则的更新要像代码发布一样走版本控制。我维护规则时会给每条规则加一个since字段记录生效日期测试仓库里专门放一些“故意写错”的代码样例任何规则更新后先跑一遍回归测试看看预期问题能否命中、是否有误报然后再推到正式仓库。这步工序只多花大概几分钟但可以避免规则误伤团队正常代码后引发的信任危机。5. 常见问题与排查技巧实录5.1 GitHub 网络连接的若干实操对策很多人一开始配置这套链路卡在最基础的一步本地拉代码或调用 API 都不顺。这里纯讲网络环境下的工程解法。如果仓库托管在 GitHub但服务器或本地网络对 github.com 的 API 访问不稳定我建议的思路是尽量让任务发生在 GitHub Actions 环境里因为 Actions 的执行环境本身就处于 GitHub 的机房网络天然不存在链路问题。Webhook 方式则需要 Hermes 服务所在的机器能够稳定访问 GitHub API。对于需要下载依赖包的场景比如仓库里用了 PyPI npm 等大体积依赖可以在 Actions 里加一层缓存减少外部请求次数。另一种有效的落地方式是配置镜像源比如 Python 项目里使用国内的开源镜像清华 TUNA、阿里云镜像这些源对公网开放也没有复杂的合规风险。写法如下- name: Install Python dependencies run: | pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install -r requirements.txt注意这类镜像只在 Actions 的临时环境里生效不会污染开发者本地配置比较干净。5.2 PR 被“插队”导致冲突怎么处理GitHub 上 PR 冲突是最常见的问题之一。当 base 分支在 PR 创建后新增了提交而 PR 分支没有跟上时合并按钮旁边就会出现 “This branch has conflicts”。用命令行梳理完整解决方案# 切到 PR 分支 git checkout feature/my-pr # 拉取最新的目标分支 git fetch origin main # 把目标分支合入当前分支 git merge origin/main合并时 Git 会标出冲突文件打开文件解决冲突——手工调整、保留必要内容后重新提交即可git add . git commit -m resolve merge conflicts with main git push origin feature/my-pr如果要避免频繁手动处理冲突更推荐 PR 分支频繁同步 base 分支至少保证每次提交前做一次 rebase 或 merge。还有一个团队层面有效的办法把大 PR 拆成小 PR多个负责人并行开发同一区域时冲突概率会明显下降。实测中 Hermes 审查的 PR 如果超过 500 行变更我会建议开发者拆分成多个逻辑独立的 PR既有利于人工 review也减少了持续合并阶段“插队”引发的连锁冲突。5.3 Flutter 构建失败与 PR 状态关联评论区里经常有人提到类似Failed to apply plugin dev.flutter.flutter-gradle-plugin这样的错误与 PR 状态关联的问题。这类错误通常不是代码本身的错误而是构建环境中 Gradle 依赖下载失败、Flutter SDK 版本与 Gradle 插件版本不匹配引起的。在 PR 审查链路里遇到这类构建失败Hermes 能做的不是修复它而是识别出失败原因属于构建环境还是代码变更。实现上可以让 Hermes 读取 GitHub Actions 的日志输出对常见错误分类并把分类结果附在 review 评论里。比如若是网络拉取依赖超时提示“尝试配置镜像源或重跑”若是 Flutter 版本约束不匹配提示“检查 pubspec.yaml 中的 sdk 约束与 CI 里 setup 的版本是否一致”这样能大幅减少“构建失败 - 无脑重跑”甚至“构建失败 - 提一个新 PR 来修”的错误操作。5.4 PR 时间轴上的 v1/a1 标记是什么如果你在 GitHub 桌面端或 PR 时间轴里看到类似 v1、a1 的标记不必惊慌这其实是审查迭代中的修订代次标记。一些自动评审机器人包括我配置的 Hermes 状态机在每次 PR 更新后会以评论时间点给 diff 打上快照标签方便追踪同一 PR 不同版本的审查状态。a1 表示第一轮新增的变更v2 表示第二轮整体版本。这个机制的实际作用是如果开发者在收到 review 意见后连续 push 了两次第二次没有把自己标记为“已回应”reviewer 很难快速定位哪些意见已在最新代码中被处理。Hermes 在处理时会读取 commit messages查找包含fix review、address feedback等关键词的提交并自动将对应的 opinion 标记为已解决剩余的保留在 open 状态。5.5 常见问题速查对照表现象最常见原因排查方向Hermes 启动后没有反应Token 无权限或过期检查 Token 的 repo scope 与过期时间评论不贴代码位置diff 行号计算错误确认对比的 base 为三点对比同一 PR 评论重复Actions 并发触发增加 concurrency cancel-in-progresslogger 出现issues could not be postedAPI Rate Limit 超限看 response header 中的 remaining 值或者改用 GitHub App规则文件改了没生效容器镜像未同步挂载目录卷配置-v lokasi并重启容器构建链路报 Gradle/Flutter 插件问题版本不匹配或依赖源失效先看 CI 日志中的“cause”确认是网络还是版本问题5.6 Hermes 审查效果调优的实测心得这套链路跑了大约一个季度后我明显感觉规则维护比规则开发更占用精力。最好的优化是把规则粒度从“项目级”下沉到“模块级”比如支付模块的安全规则比 UI 模块严格得多但通用风格规则各模块保持一致。这样既不会拖慢常规模块的开发速度也能在核心模块上保证高强度审查。关于 Hermes agent 在 PR 上返回意见的语气第一版我用了偏强硬的措辞比如“你是严格的技术负责人”结果开发者的接受度很差后来调整成“你是一位有经验的同事提供建议但保留编辑者的决定权”给出的意见还是同样的内容但协作摩擦明显小了许多。做自动化代码评审不仅要考虑“查得准不准”还得考虑“别人愿不愿意采纳”。机器人在对话中永远没有权限覆盖人但要保证被采纳。设计话术时要把这一点考虑进去。另外一个提升效率的小技巧Hermes 的规则引擎支持 diff 的增量分析只有新修改的行才参与审查。这意味着当一个历史遗留的TODO老代码没有改动过Hermes 就不会持续在每次 PR 上骚扰开发者。这个“朝三暮四”的取舍在真实协作中的价值非常大——我最初的版本是一次性审查整个文件中所有行导致评论中大部分是老问题开发者在无关的评论中看不进真正重要的新问题。后来把范围限制为 diff 新增行后有效问题的定位和沟通效率都翻倍了。