前阵子跟一位做研发效能的朋友聊天,他说现在团队里最耗人的环节不是写代码,而是等代码评审。一个PR挂两三天没人看是常事,就算有人review了,也经常是看个大概就approve,真正的问题反而沉淀不下来。我理解他的处境,因为我自己也经历过这个阶段——直到我给团队的GitHub仓库接入了基于Hermes的自动化代码评审系统,让大模型以智能体(Agent)的方式参与PR审查,情况才明显改善。这篇文章不是搬运什么高大上的概念,而是把我从选型、架构到踩坑的完整过程记录下来,给同样想给PR评审提效的团队一个能直接照着搭的参考。
文章里会涉及Hermes这个开源智能体框架的部署方式、它和GitHub API的交互逻辑、Prompt模板的设计思路,以及我在实际运行中遇到的问题和调优经验。无论你是研发效能工程师、技术负责人,还是单纯对“AI写代码评审意见”这件事感兴趣的开发者,这篇文章应该都能给你一些可落地的思路。
1. 整体设计与方案选型
1.1 为什么给PR配上自动化评审
代码评审这件事,理论上是在提质量、守基线,但实际上它经常成为研发流程里的瓶颈。PR越多,每个PR的等待时间越长;评审者一看diff过大、情绪先崩一半,草草扫一眼就approve,等于没审。另一方面,人的注意力是有限的,看前面十几个文件时还精神抖擞,看到后面十几个文件时已经处于“差不多就行了”的状态。这就是我决定引入自动化评审的起点——不是取代人,而是先把那些需要耐心、细致、重复劳动的部分接过去。
自动化评审能覆盖的恰恰是人工最容易漏掉的部分:边界条件没处理、异常路径没覆盖、敏感信息被打进日志、新引入的依赖存在已知漏洞。这些东西不像缩进和命名那样一眼能看到,但它们才是上线后真正会出事故的地方。Hermes做这件事的优势在于,它不是一套写死的规则引擎,而是能理解PR上下文、能调用工具、能按照指令输出结构化结果的智能体。这意味着它可以真正“读”diff,而不是靠正则去匹配问题模式。
1.2 为什么选Hermes而不是现成Code Review工具
市面上不是没有现成的自动化代码评审工具,SonarQube、Codacy、CodeRabbit这些我都试过。它们各有长处,比如SonarQube在静态分析和规则覆盖上非常成熟,CodeRabbit能直接在PR上评论,但用下来总有几个绕不过去的痛点。
最核心的问题是“死板”。规则引擎只能发现你预先定义好的问题,而代码里的坑往往是业务相关的、上下文相关的,规则根本描述不出来。举个例子,一个支付模块的PR,把金额计算从BigDecimal换成了double,静态规则不一定能识别出这是精度风险,但一个能读懂业务上下文的大模型可以。第二个痛点是定制成本高,很多工具想注入团队自己的规范、架构约束、常见反模式清单,需要写插件或者用它们特定格式的规则,维护成本并不低。而Hermes这种Agent框架的玩法完全不同:你给他一个角色、一堆上下文、一份团队规范,它能自己“思考”怎么审。相当于你雇了一个懂业务、懂规范的reviewer,而不是买了一个只会背答案的扫描器。
当然,选择Hermes还有一个很现实的考虑:自托管、数据可控。代码是公司最核心的资产,我不想把所有diff都丢给一个第三方的SaaS服务去分析。Hermes本身是开源框架,模型可以接私有化部署的推理服务,整个链路的数据流都是可控的。这一点对中大型团队来说,可能比功能本身更重要。
1.3 一套PR评审系统的最小闭环长什么样
把整套系统拆开看,其实就是一个非常标准的数据处理闭环:触发、取数、推理、回写。
触发环节,靠GitHub的Webhook或者GitHub Actions的pull_request事件来监听PR动作;取数环节,拿到PR的标题、描述、文件变更列表和完整diff;推理环节,Hermes作为Agent把这些信息整理成上下文,交给大模型去分析,产出评审意见;回写环节,通过GitHub API把评审意见以Review或评论的形式贴回PR页面。
这个概念模型非常重要。很多人在搭类似系统时一上来就陷入“怎么让模型给出高质量意见”的纠结,但其实前面两个环节没做好,后面模型再强也白搭。就像你让一个资深工程师去评审,但你只把目标文件路径告诉他,不给他看diff和需求背景,他能审出什么?所以这套系统里,我最先花时间打磨的其实是“怎么把PR的信息完整、准确地喂给Hermes”,而不是急着调Prompt。下面几个小节我会把每个环节的关键细节展开讲。
2. 核心细节解析:Hermes如何读懂一个PR
2.1 从Webhook事件到上下文构建
GitHub的pull_request事件会在不同时机触发,常见的有opened(新建PR)、synchronize(提交更新)、review_requested(请求评审)。我在设计时并没有让Hermes在每次事件触发时都全量跑一遍,而是做了区分:opened时做完整评审,synchronize时只评审增量变更,review_requested时如果之前已经评论过就静默跳过。这个策略有效避免了“每次提交都疯狂刷屏”的尴尬,也省了不少模型调用费用。
触发之后的关键操作是拉数据。这里有一个顺序问题:GitHub事件payload里其实只带了PR的元信息,不包含完整的diff内容,所以必须用API去拉。我常用的几个接口和参数如下:
- 拉取PR详情:GET /repos/{owner}/{repo}/pulls/{number},用来拿标题、描述、基础分支、目标分支等信息。
- 拉取文件变更列表:GET /repos/{owner}/{repo}/pulls/{number}/files,返回每个文件的文件名、状态、新增行数、删除行数和patch片段。
- 拉取提交历史:GET /repos/{owner}/{repo}/pulls/{number}/commits,用来理解这个PR的开发过程。
这里有一个容易踩的坑:files接口返回的patch有时候是被截断的,尤其当文件变更特别大时。GitHub API对单文件patch长度有上限,如果你直接拿这个片段塞给模型,可能看不出关键问题。我的做法是,检测到patch内容不完整时,再单独用GitHub的compare接口拉一次完整diff:GET /repos/{owner}/{repo}/compare/{base}...{head},这样能拿到全量变更。
2.2 三个关键信息源:PR描述、Diff、Commit历史
喂给Hermes的上下文不是越多越好,而是越“有效”越好。我总结了三个必带的信息源,缺一个都会明显影响评审质量。
第一个是PR描述。一个认真写的PR描述,本身就包含了改动的动机、影响范围、测试计划,这些东西对评审至关重要。但现实是很多PR描述就一句话“fix bug”,遇到这种情况,我会让Hermes先从commit message里尝试推断意图,甚至允许它追问作者补充信息。
第二个是diff本身。这是评审的主体,也是最需要做加工的部分。直接把几万行的diff一股脑丢给模型,效果通常很差,一是上下文窗口不够,二是在海量改动里模型的注意力会被稀释。我的做法是先让Hermes做一次“预筛选”:按文件类型、改动量、风险等级分类,把高风险的改动(比如涉及鉴权、支付、并发控制、数据迁移的代码)优先提取出来,再针对这些重点文件做深度评审。低风险文件(比如改名、格式化、配置文件)统一给一个轻量结论即可。
第三个是commit历史。这是很多人会忽略的信息源。PR是一个演进过程,单个提交可能是有问题的,但最终合入状态可能已经修正了。Hermes在评审时如果只看最终diff,可能会误报“这个错误是从上一版引入的”这种半对半错的问题;而结合commit历史,它能判断某个问题是否已经在中途被修复过,避免重复评论低级问题。
2.3 Prompt模板设计与评审规则落地
Prompt设计是整套系统里最微妙的部分。坦白说,第一次写评审Prompt时我犯了所有新手都会犯的错——要求太多太全,结果模型输出了一堆正确的废话。“注意代码质量”“关注潜在问题”“给出建议”这种话说了等于没说。
后来我总结出一个有效套路:给角色、给流程、给边界、给格式。
角色要具体。不要写“你是一个代码审查助手”,而是要写“你是一名拥有十年经验的资深后端工程师,擅长发现并发、性能、安全方面的问题”。角色越具体,模型调用知识的方式就越接近那个角色的判断习惯。
流程要固定。让Hermes按“先概括PR目标 → 再按文件逐个分析 → 最后汇总风险和阻塞项”的顺序输出。固定流程的好处是评审结果稳定,不会这次输出一段总结,下次输出十条零散评论。
边界要清晰。明确告诉模型“哪些不该管”。比如缩进、命名风格这类问题,除非严重到影响阅读,否则不要提。因为这类低价值评论会拉低整个系统的信任度,开发者的注意力是有限的,被噪声刷屏之后,真正的严重警告反而会被无视。这是我在实际运行中感受最深的一点。
格式要结构化。让每个问题都包含文件路径、行号、严重级别、问题描述、修复建议。只有形成了固定格式,后续才能做统计分析,才能对接门禁系统,才能让开发者快速定位问题。
下面是我整理后一直在用的一份精简版Prompt模板,供你参考:
你是一位资深软件架构师,正在为一个团队执行代码评审。请严格按以下步骤进行: ## 输入信息 - PR标题:{title} - PR描述:{body} - 变更文件:{files} - Diff片段:{diff} ## 评审步骤 1. 用一句话概括这个PR想解决的问题。 2. 逐个分析变更文件,重点检查:逻辑正确性、并发安全、边界条件、安全风险、性能隐患、测试覆盖。 3. 在所有分析完成后,汇总为评审结论。 ## 输出格式 对每个问题,输出如下结构的条目: - 位置:文件路径:行号 - 级别:严重 / 建议 / 疑问 - 描述:问题是什么,为什么是问题 - 建议:给出具体的修复方案或示例代码 ## 约束 - 忽略缩进、命名风格、格式化等非功能性意见。 - 不要重复多个提交中已经修复过的问题。 - 如果某个文件没有实质性问题,不要强行评论。 - 如未发现严重问题,请在最后明确写明“本轮未发现阻塞性问题”。这份模板不复杂,但该有的约束都有了。我第一次上线时用的是冗长版本,模型输出反而发散;换成这个精简版本之后,评审意见的可读性和采纳率都提升了一个档次。
3. 实操过程:从安装Hermes到自动评论上PR
3.1 环境准备与Hermes安装部署
先交代一下我部署Hermes的环境:一台普通的8核16G云服务器,Ubuntu 22.04,Docker已安装。我个人建议有条件的话用Docker部署,原因是Hermes依赖的Python环境和底层库比较多,直接装在宿主机上容易污染系统环境,升级回滚也麻烦。当然如果你只是本地试玩,直接用pip装也没问题。
以我用的版本为例,基础安装命令如下:
# 方式一:pip安装 pip install hermes-agent # 方式二:Docker部署 docker pull hermes-agent:latest docker run -it --name hermes \ -e LLM_API_KEY=your_key \ -e GITHUB_TOKEN=your_token \ hermes-agent:latest --help安装完成后先跑一下hermes --help,看看CLI入口是否正常。不同发行版的命令入口可能有细微差异,有报错的话以项目仓库README里的说明为准。装好之后先别急着配PR审查,建议先用自带的交互模式跑一次最简单的对话,确认Hermes能正常调用大模型接口。我见过很多同学一上来就配全套YAML,结果跑不通都不知道是哪一层的锅。先验证底层连通性,再往上搭业务,这个排错顺序能省掉大量时间。
3.2 配置GitHub访问凭证
Hermes要读取PR信息、提交评论,必须通过GitHub API进行身份认证。这里有两种方式,我分别说下使用场景。
第一种是使用个人访问令牌(PAT),在GitHub账号的Settings → Developer settings → Personal access tokens里创建。创建时勾选repo相关权限,尤其是repo和pull_requests权限。这种方式配置最简单,但令牌挂在个人账号下,处理组织仓库PR时会有权限边界问题,而且如果创建者的账号被禁用,系统就瘫痪了。我建议个人实验或小团队自用用PAT就够了。
第二种是创建GitHub App,权限更细,可以精确到只读代码、写入PR评论,而且运行在应用身份下,不依赖任何个人账号。这种方式适合组织级使用,也更容易通过权限策略保证安全。配置过程稍微复杂一些,需要先注册App、生成私钥、安装到目标仓库,然后把私钥信息传给Hermes。如果你管理的仓库比较多,或者对权限隔离很敏感,强烈建议用GitHub App方案。
无论用哪种方式,凭证都建议通过环境变量或独立的密钥管理服务注入,不要硬编码在配置文件里。下面是我推荐的配置方式:
export GITHUB_TOKEN=ghp_xxxxx export LLM_API_KEY=sk-xxxxx然后在Hermes的配置文件里通过环境变量名引用,不直接写明文。
3.3 编写PR评审Agent的完整配置示例
拿到令牌之后,关键的步骤是写Hermes的配置文件。这个文件定义了Agent的角色、模型参数、评审策略、输出方式等核心行为。我用的配置如下,重点字段的用途我在后面逐个解释:
llm: provider: deepseek # 可替换为 openai、anthropic 或本地推理服务 model: deepseek-chat api_key_env: LLM_API_KEY temperature: 0.2 # 评审场景尽量低,减少随机性 max_tokens: 4000 github: token_env: GITHUB_TOKEN api_base: https://api.github.com agent: name: pr-reviewer system_prompt_file: ./prompts/pr_reviewer.md max_diff_length: 30000 # 超长diff截断阈值,按字符计 context_units: - pr_description - commits - diff - related_issues review: comment_style: review_thread # 以GitHub Review Thread形式输出 max_comments: 20 # 单次评审最多评论数 skip_major_version_diff: false skip_paths: - "*.lock" - "package-lock.json" - "go.sum" danger_only: false配置里几个关键点我展开说一下。
temperature:评审任务需要相对确定、严谨的输出,温度设太低会显得机械,设太高容易胡说。我实测0.2是个不错的平衡点,写代码时我可能用0.7,但评审意见我更希望它“稳”。
max_diff_length:这个阈值关系到成本和质量。diff太大时直接截断会产生垃圾评审,我的方案是不硬截断,而是让Hermes先做分文件分析,再把结论合并。如果你的配置里没有分文件处理能力,那宁可让单文件diff小一点,也不要一口吃成胖子。
skip_paths:把锁文件和带hash的依赖文件从评审范围里排除,省token又省评论额度。第一次跑的时候我忘了配这个,结果每个package-lock.json都收到几十条先生成建议,直接把真实的评审意见淹没在噪声里,翻车翻得很惨。
3.4 用GitHub Actions自动化触发
配置文件准备好了,怎么把“每次新PR产生”和“Hermes跑一次评审”关联起来?有两种思路:一种是自己在服务器上监听Webhook,另一种是直接在仓库里放一个GitHub Actions工作流。我倾向后者,因为Actions本身就是GitHub托管的运行环境,不用自己维护监听服务,而且Secret管理、权限控制都是现成的。
这是我的workflow示例:
name: Hermes PR Review on: pull_request: types: [opened, synchronize] jobs: hermes-review: runs-on: ubuntu-latest permissions: contents: read pull-requests: write steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.11' - name: Install Hermes run: pip install hermes-agent - name: Run Hermes PR review env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LLM_API_KEY: ${{ secrets.LLM_API_KEY }} run: | hermes pr-review \ --repo "${{ github.repository }}" \ --pr "${{ github.event.pull_request.number }}" - name: Post review comment uses: actions/github-script@v7 with: script: | const fs = require('fs'); const body = fs.readFileSync('review_output.md', 'utf8'); await github.rest.pulls.createReview({ owner: context.repo.owner, repo: context.repo.repo, pull_number: context.issue.number, body: body.substring(0, 65000), event: 'COMMENT' });这段workflow有几个细节值得注意。permissions字段里pull-requests: write必须显式声明,否则默认的GITHUB_TOKEN没有写入PR评论的权限。fetch-depth: 0是为了让Hermes能拿到完整的git历史,如果只保留latest commit,部分需要对比历史提交的评审逻辑就会失效。
另外,on.pull_request.types我只保留了opened和synchronize。这样设计是刻意的:PR刚开时做一次全面评审,后续每次push新commit时再做一次增量评审,既不会漏,又不会在每次标签变更、标题修改时重复跑。
3.5 评审结果回写与质量控制
Hermes跑完之后,核心问题是结果怎么呈现给开发者。我建议用GitHub官方的Review功能,而不是普通评论。因为Review功能可以关联到具体的commit和代码行,还能把评论折叠成“review thread”,开发者可以直接在页面里逐个回复,交互体验好很多。我在workflow里用的就是pulls.createReview接口。
这个环节还要考虑一个噪音控制问题:同一个PR反复被synchronize,如果每次都全量评论,评论数会爆炸。我的解决方案是让Hermes把每次评审的结论摘要和已评论的问题ID记录到本地状态文件,在PR更新后只提交“新增问题”和“已修复问题”的变化,而不是重复罗列旧问题。这个状态管理逻辑写在Hermes的Agent脚本里,本质上就是维护一个以PR编号为维度的状态缓存。
质量控制的另一个手段是“人工抽查+反馈闭环”。自动化评审上线初期,我每周会抽几个PR,对比Hermes的评审意见和资深工程师的意见,统计漏报率和误报率。发现问题就往Prompt里补约束或加few-shot示例。这个迭代过程很重要,因为大模型的评审能力是可以通过Prompt调优持续提升的,不调就浪费了。
4. 常见问题与排查技巧实录
4.1 高频报错速查表
这套系统跑了大半年,我把遇到过的典型问题整理成了一张速查表。这些坑不是网上抄来的,都是我一行一行查日志试出来的:
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| 403 / 401 认证失败 | Token权限不足、过期、或GitHub App安装未授权目标仓库 | 先确认Token在curl -H "Authorization: Bearer $TOKEN" https://api.github.com/user下能通;再看scope;最后检查App对仓库的访问权限 |
| 评论没有出现在PR上 | Actions job缺pull-requests: write权限,或createReview接口调用参数不对 | 打开Actions日志看返回错误,重点看Resource not accessible by integration一类报错 |
| Hermes报上下文长度超限 | diff过大,超过模型上下文窗口 | 调大max_diff_length不是根本解,建议改成按文件粒度分批分析再汇总 |
| 每push一次就重复评论 | 没有状态缓存逻辑 | 在Agent脚本里维护“已评论问题ID集合”,只提交增量意见 |
| 评审内容泛泛而谈 | Prompt角色不具体,约束太少 | 按2.3节的模板重写Prompt,给角色、给流程、给边界、给格式 |
| 模型乱报问题 | 上下文里缺少PR描述和commit背景 | 确认context_units里开启了pr_description和commits |
| 单文件patch内容不完整 | GitHub API对超大diff有截断 | 换用compare接口拉完整diff,或让Hermes直接checkout目标分支本地对比 |
4.2 评审质量不及预期的调优思路
“评审质量不高”是上线后最常听到的反馈,具体表现就是误报多、漏报也多。我调优时一般按照下面的顺序来排查,效果比较明显。
先看上下文是否完整。我见过很多人调了半天Prompt,结果问题出在PR描述和commit历史根本没喂进去,模型只能蒙。优先确认Hermes拿到的数据是不是完整、准确的。第二步看温度参数。温度过高会让模型在“可提可不提”的问题上产生大量发散意见,把评审结果调成0.2以下往往立竿见影。第三是看Prompt约束。如果模型总在纠结变量命名、代码风格这类琐碎问题,就在Prompt里明确禁止;如果模型总是漏掉并发、安全这类深层次问题,就需要提供针对性的few-shot示例,先喂一两个典型漏洞案例给模型“打样”。最后还可以考虑换更强的模型。我实测过,同一个Prompt在小模型上输出质量明显低于大模型,尤其对于需要多步推理的跨文件评审任务,模型的逻辑能力差距是实打实的。
4.3 实用避坑清单
最后分享几个长期运行后才领悟到的细节,每条都是拿实际故障换来的。
第一,不要在主干分支上直接测试。刚部署时我在项目的master分支上实验了一次,结果Hermes把存量代码的所有潜在问题全评论了一遍,几百条噪音评论差点把PR页面刷爆。稳妥的做法是先在测试仓库或draft PR上跑通,确认评论策略和噪音控制没问题了,再放到正式流程上。
第二,关注敏感数据。如果你使用的是云端大模型API,送进去的diff必须经过脱敏检查。一个常见场景是代码里出现了数据库连接串、云厂商AccessKey,这些信息会被随diff一起发给模型服务。我的做法是在喂给模型之前做一层脱敏替换,用占位符替换疑似密钥的字符串。即使你觉得“模型不会泄露数据”,也挡不住这种信息的合规风险。这一点对自托管系统尤其需要重视。
第三,给Hermes设置明确的“红线”提示。比如:“如果发现硬编码密钥、SQL注入、越权访问等安全问题,必须标记为严重级别并在结论开头单独列出。”这种明确的安全红线能显著提升高价值问题的召回率,而且它们通常是人工评审时最想第一时间看到的东西。
5. 更进一步的扩展方向
5.1 把评审结论接进门禁机制
评论只是“告知”,门禁才是“约束”。我目前的方案是让Hermes在评审结束后输出一份机器可读的JSON摘要,包含是否发现严重问题、严重问题条数、哪些文件存在高风险等。这份JSON可以被CI脚本读取,当严重问题数量超过阈值时,直接在GitHub的required checks里标记为失败,阻止PR合入。
这个机制要做到可配置。比如新建的draft PR不设门禁,普通PR只有严重级别问题才阻断,核心模块的PR要求所有问题必须闭环才允许合并。这套规则我用一个简单的配置文件维护,Hermes只负责产结果,门禁逻辑完全独立,方便调整和回滚。
5.2 让评审Agent积累团队规范
一篇好的博客值得一个赞,但一套好用的评审系统最花时间的不是部署,而是让Agent“懂你的团队”。我建议把团队的编码规范、架构约束、常见反模式案例整理成文档,挂到Hermes的上下文目录里,让它在每次评审之前自动加载。这样随着团队踩坑经验的积累,评审Agent的“团队记忆”也在不断更新。
我就在自己的项目里维护了一个team_rules目录,里面按模块分类放着“服务间调用必须走网关”“数据库字段不允许直接删除只能标记废弃”“金额字段一律用Decimal”之类的硬性约定。这些内容写进Prompt会显得臃肿,但作为附加上下文单独加载,效果非常好,Hermes在评审时真的会把它们当作判断依据。
这套系统从我搭起来到现在已经跑了几个月,最直观的感受是开发同学真正花在“等review”上的时间明显少了,评审意见的质量也比之前纯人工时期更稳定。踩坑踩多了以后,我最大的体会是:自动化评审的核心不在模型多聪明,而在你把评审的边界定义得多清楚。给Hermes一个明确的角色、一份团队规范、几条硬性红线,它能跑得比大多数泛泛而谈的工具靠谱得多。
如果你也在给团队搭类似的自动化评审链路,或者已经在用其他Agent框架做PR审查,欢迎一起交流实际运行中的经验和坑。这套路子里值得打磨的细节还有很多,后面我会继续分享从门禁机制到评审质量评估的下一阶段实践。