
作为GitHub重度用户我每天最耗精力的其实不是写代码而是review别人的PR。尤其是项目活跃起来之后PR一个接一个涌进来每个都要点开看diff、查逻辑、试运行太消耗时间了。后来我在GitHub上看到有人把Hermes这个智能体接到仓库里做自动化代码评审实测下来效果相当能打于是自己也在几个项目里部署了一轮。这篇文章就把我踩过的坑、调过的参数、想明白的设计思路全部写出来给需要给开源项目或者团队仓库做自动化PR审查的朋友做个参考。1. 项目整体设计与核心思路拆解1.1 为什么选择Hermes做PR审查先说背景。传统做法里PR审查一般靠两类工具一类是静态检查比如ESLint、SonarQube、CodeQL它们擅长抓语法错误、安全漏洞、代码风格问题但理解不了业务逻辑更不会判断这个改动“设计得合不合理”另一类是纯人工review质量高但速度慢一个人一天能认真看完的PR数量有限而且很容易被琐碎问题分散注意力——漏掉真正要紧的逻辑缺陷。Hermes解决的正是这个中间地带的问题。它本质上是一个大模型驱动的智能体能读懂代码变更的语义理解PR的意图然后以类似真人reviewer的方式给出评审意见。比如它会指出“这个函数在异常分支里没有释放连接”“这里新增的循环在数据量大时可能退化成O(n²)”“依赖变更里出现了两个版本的JSON库建议统一”。这些判断静态检查工具做不了人工做又太费时间交给Hermes正合适。我选择Hermes而不是自己写一套脚本的另一个原因是它本身就是按“智能体”架构设计的不是简单的“调API生成评论”。它会先分析PR的元信息再规划评审步骤分文件、分模块去读取上下文最后汇总成结构化意见。这种“先规划、再执行、后汇总”的工作模式比一次性把整个diff塞给大模型要靠谱得多因为大模型对超长上下文的处理能力再强也会在信息过载时出现“只看到局部、忽略全局”的情况。1.2 事件驱动的自动化流程设计Hermes在GitHub里以App的方式运行核心逻辑是事件驱动。它监听仓库的Webhook事件主要处理三种openedPR新建、synchronizePR有新提交、reopenedPR重新打开。每次事件过来Hermes会执行一套完整的“感知-分析-反馈”流程。整套流程大致是这样接收GitHub Webhook事件校验签名确保请求确实来自GitHub。从事件负载中提取PR编号、仓库信息、提交SHA等元数据。调用GitHub API拉取本次PR的完整diff和基础信息标题、描述、关联的Issue等。对大PR做拆分处理——超过设定阈值的diff会被拆成多个文件组分批送进评审引擎。智能体分阶段工作先理解PR目标再逐文件分析变更内容遇到需要追溯历史的地方还会额外查一下相关文件的历史提交。汇总所有分析结果去重、过滤低价值评论按严重程度分级。以GitHub Review的形态发布评审意见可以一次性发布也可以按文件行内评论。如果开启了“必须通过检查才能合并”的配置还会同步更新Check Run状态。这套设计最核心的考量是“尽量真人工review的节奏”。真人看PR不是看一眼整体就下结论的而是先看描述、再逐个文件过、最后综合判断。Hermes把这条链路模拟了出来而不是简单粗暴地“拉diff然后生成一段评论”。1.3 和传统CI审查工具的定位差异很多第一次接触Hermes的人会问我有ESLint了为什么还要它我做了个对照方便理解定位差异维度传统静态检查ESLint/SonarQubeHermes自动化评审抓语法/风格问题强规则明确误报少中等依赖模型能力理解业务逻辑弱只能做模式匹配强能理解代码意图发现逻辑漏洞有限需要自定义规则较好能跨文件推理评审意见可读性偏技术化、碎片化类人表达带原因分析和修改建议误报风险低但噪音多中等可通过配置和模型调优降低覆盖范围单文件为主跨文件、跨模块、甚至跨PR历史所以我现在的实践是两者都开ESLint这类工具负责把“低级错误”挡在门外Hermes负责“逻辑层”的审查。两者不是替代关系而是互补关系。这个定位想清楚之后你就不会对Hermes产生不切实际的期待配置起来也更有针对性。2. 动手部署前必须搞清楚的准备工作2.1 创建GitHub App才是正确姿势我自己第一版偷懒直接用个人Token写的脚本结果没跑几天就出问题了Token权限太大万一泄露整个仓库都完蛋而且个人Token触发的评论在PR里显示的是你自己的头像会把个人号和机器人混在一起时间久了连自己都分不清哪条评论是真人提的。正确做法是创建一个独立的GitHub App。创建路径是GitHub首页右上角头像 → Settings → Developer settings → GitHub Apps → New GitHub App。创建时需要填写几个关键信息GitHub App name全局唯一比如hermes-review-bot建议起一个能代表用途的名字。Webhook URL指向Hermes服务对外暴露的接口本地调试时可以用内网穿透工具把本地端口映射到公网地址。Webhook secret自己生成一段随机字符串Hermes和GitHub都配置这一段用来做签名校验。Permissions按最小权限原则勾选。一般需要Pull requests: Read write、Checks: Read write、Contents: Read如果要根据改动文件推断影响范围可能还需要Issues: Read。Subscribe to events勾选Pull request事件即可如果希望PR更新时自动重新触发评审还要确认Pull request事件里包含了synchronize子类型。创建完成后GitHub会生成App ID和私钥文件.pem格式。这两个信息要保存好后面配置Hermes要用。私钥是用来签发访问Token的绝不能提交到代码仓库里。2.2 权限模型与安全边界设计这里多说一句权限。Hermes需要以App身份访问仓库但不需要也不应该拥有整个组织的权限。我实际部署时只把这个App装到了需要用自动化评审的仓库而不是所有仓库。安装时也刻意选择了“Only select repositories”。权限模型上我的建议是只读仓库内容写PR评论和Check Run。不授予合并权限合并动作永远留给人来操作。Webhook Secret必开。这一步能防止伪造请求。Hermes收到请求后会拿GitHub的签名和本地计算的结果做比对。私钥文件权限设成600。多个用户可读在服务器上就是灾难。模型API Key通过环境变量注入不要写进配置文件。万一配置文件被提交API Key就裸奔了。还有一个值得注意的点当Hermes以App身份运行OpenAI兼容接口时大模型服务商那边的API Key和GitHub App私钥要分开放置不要放在同一个环境变量文件里。这样即便某一边泄露攻击者也无法同时拿到两边的权限。2.3 本地开发调试环境怎么搭如果打算在自己电脑上开发调试Hermes而不是直接部署到服务器最简单的方式是本地起服务用内网穿透工具比如ngrok、frp或cloudflared把本地端口暴露到公网然后在GitHub App配置里填上这个公网地址。但这里有几个坑我逐一说明一下不要在微信或聊天工具里发Webhook地址。内网穿透工具生成的临时域名很容易过期一旦过期GitHub就回调失败而GitHub不会自动重推只会记录Delivery失败排查时容易一头雾水。本地调试时要能打印完整请求体。Webhook事件负载结构复杂尤其synchronize事件和opened事件里pull_request对象的字段有细微差异。建议在开发阶段把原始JSON打出来看一眼。我经常用一段小脚本直接把GitHub发送的payload存成本地文件分析完再删掉。版本选择建议用Node.js 18或Python 3.10两者Hermes的生态都支持。我自己用Node.js比较顺手但如果你Python更好完全可以用Python版本逻辑是一样的。2.4 大模型API选型与切换Hermes的设计支持多模型供应商切换因为它内部调用的是OpenAI兼容协议所以只要你的模型服务商提供/v1/chat/completions接口就可以直接配置。我自己实测下来主流的大模型产品都有不错的表现。如果项目对代码理解能力要求高建议使用推理能力强一些的模型档位如果只是做轻量级风格点评用便宜的模型就够了。国内可直连的DeepSeek等API我也测过代码评审场景表现扎实而且成本低很多适合高频次触发、仓库体积又大的项目。切换模型时只需要改环境变量里的baseURL和model字段不影响其他逻辑。比较惊喜的是开源社区近期出现了专门针对代码评审场景优化的模型版本我搜热词时看到Hermes相关的模型下载量增长很快这类模型在“发现潜在Bug”和“给出可执行建议”两个维度上比通用对话模型更聚焦但是对提示词敏感度也更高需要根据自己的代码风格微调一下提示词模板。3. 核心实现Hermes的PR审查流程逐段拆解3.1 Webhook接收与请求验签Hermes对外暴露的唯一接口就是Webhook入口。GitHub每次推送事件都会带两个关键HeaderX-Hub-Signature-256和X-GitHub-Event。前者是HMAC-SHA256签名后者表示事件类型。校验签名的逻辑很简单用Webhook Secret对请求体做HMAC-SHA256计算然后与Header里的签名做对比。Node.js里大概是这样const crypto require(crypto); function verifyWebhookSignature(payload, signature, secret) { const hmac crypto.createHmac(sha256, secret); hmac.update(payload, utf8); const digest sha256 hmac.digest(hex); const expected Buffer.from(digest); const received Buffer.from(signature || ); return expected.length received.length crypto.timingSafeEqual(expected, received); }这里有两个容易犯的错误。第一个是签名比较必须用timingSafeEqual不能用普通的字符串比较否则存在时序侧信道攻击风险虽然GitHub App的Webhook并不算高价值目标但习惯一定要养成。第二个是原始body和解析后的对象不能混用。签名是对原始byte stream做的如果框架已经帮你解析了JSON再用JSON.stringify(req.body)去计算签名结果一定不匹配因为序列化后的字段顺序、空白都和原始请求不一致。所以务必在中间件里拿到req.rawBody再验签。事件类型判断上只处理pull_request事件即可其他全部返回200并忽略。3.2 拉取PR Diff与上下文组装验证通过之后Hermes会拿着事件里的pull_request对象去GitHub API拉取完整信息。这里有一个细节事件负载里的diff可能是不完整的尤其是大PR建议统一调用API重新拉取。用GitHub REST API拉取diff的方式是curl -H Authorization: Bearer $TOKEN \ -H Accept: application/vnd.github.diff \ https://api.github.com/repos/{owner}/{repo}/pulls/{number}返回的diff文本会比较大。这里有几个处理原则单文件超过300行的diff建议截断后分段提交给模型因为大模型对超长文本的注意力会分散越到后面越容易忽略细节。新文件、重命名文件、纯格式调整的文件要区分对待。纯格式调整比如Prettier全量格式化导致的diff可能几十个文件但实际没有逻辑变化如果全部送去评审既浪费Token又会产生大量无用评论。Hermes的思路是先看diff的行数分布对疑似格式化类改动做一次快速预判跳过或者只做总结性评论。依赖文件锁文件单独处理。package-lock.json、pnpm-lock.yaml、go.sum这类文件diff通常巨大送上模型评审意义不大。Hermes会把这些文件单独摘出来只做版本变更对比不做逐行评审。组装上下文时一个很实用的技巧是将diff按“文件维度”组织成结构化数据每个文件附带文件名、变更类型、新增/删除行数、以及变更内容。关键函数附近如果有未变更的上下文代码也可以截取前后几行一起发给模型这样模型能理解函数在整体中的位置。3.3 提示词与评审规则设计这是整个Hermes配置里最核心的一环。提示词写得好不好直接决定评审质量。我调试了很多版最终沉淀下来一套比较稳定的思路。第一层是系统提示词设定角色和评审准则。大意是你是一位资深代码评审专家请从正确性、安全性、可维护性、性能、测试充分性五个维度审查以下PR对每个发现的问题说明严重程度、原因和修改建议避免无实质内容的客套话。第二层是用户提示词包含PR元信息和diff内容。关键是把“评审规则”嵌入进去比如请审查以下Pull Request的变更内容 仓库hermes-demo/core-service PR标题feat: add user profile cache PR描述新增用户画像缓存降低数据库压力 请严格按照以下规则 1. 只评论有实质价值的问题不要做无关痛痒的风格建议。 2. 安全相关的问题输入校验、越权、注入、敏感信息泄漏标记为critical。 3. 性能问题需要给出依据说明为什么是问题以及如何验证。 4. 对于可疑但不确定的问题使用建议语气提出不要武断下结论。 5. 如果某个文件没有发现问题不输出评论。 6. 最后给出一段整体评价说明这个PR是否可以合并以及合并前必须解决的问题清单。第三层是few-shot示例。模型吃“例子”比吃“规则”快我给Hermes喂了少量“错误示例”比如“这个变量命名不太规范”这种低质量评论以及“这里在循环体内调用了外部API如果接口响应变慢可能导致整个请求超时建议批量获取或异步处理”这种高质量评论。有了对比例子之后输出质量提升非常明显。还要在提示词里强调不得对PR作者进行人身评价或使用负面情绪词汇只谈代码不评价人。这个既是职业素养也是为了避免在团队里引发不必要的摩擦。3.4 评审结果的发布策略Hermes支持两种评论模式单条汇总评论和逐文件行内评论。我强烈建议使用后者因为行内评论可以直接定位到代码行作者在GitHub网页端就能看到具体位置Review体验和真人提意见几乎一致。GitHub API提供了创建Review的接口await octokit.pulls.createReview({ owner, repo, pull_number, event: COMMENT, comments: [ { path: src/cache.js, position: 45, body: 这里在异常路径下没有释放Semaphore可能导致并发控制失效。 }, ], body: Hermes自动评审完成发现3个问题1个严重2个建议。请查阅行内评论。 });这里有几个细节值得注意position参数需要精确对应diff中的行号。很多新手在这里踩坑他们用了文件的实际行号结果评论贴到错误的代码行上。GitHub API要求的是“在diff视图中的位置”而不是源文件里的行号。Hermes返回的每个问题都会带上这个position值从diff文本中计算得来。不要每条评论都单独调一次API。这样既慢又容易触发限流应该把所有评论组装进一次createReview的comments数组里。回复或者删除之前的机器人评论。如果PR更新后重新评审旧评论不会自动消失。解决方案是生成新评审时先查一次该PR上Hermes之前留下的评论用minimizeComment或deleteComment接口清理掉旧评论再发布新的。还有一个我后来才加上的功能终结性总结。每条review的最后Hermes会输出一段汇总“这个PR整体逻辑清晰主要风险集中在缓存一致性上。建议先处理第2个问题再合并。”这条总结对maintainer来说是极好的快速筛选依据不用把每条行内评论都看完就能了解概况。3.5 自动化程度的三种模式并不是所有仓库都适合一上来就“机器人在检查不通过就不让合并”步子迈得太大容易引起团队反感。Hermes支持三档自动化程度我建议每一个新接入仓库都按照这个顺序逐步推进评论模式机器人只发评论不做任何强制校验。适合第一次接入让大家观察它的意见是否靠谱有没有误报和噪声。建议模式机器人发布评审意见的同时向Check Run提交一个“通过”或“中立”状态但合并保护不强制依赖它。适合跑一段时间、大家认可度提升之后。拦截模式Check Run发现问题时返回失败状态合并保护设置成必须通过Hermes的检查才能合并。这种模式下机器人的误报会直接卡住合并流程所以一定要在前两种模式都验证稳定之后才能开启。建议模式到拦截模式之间至少要跑过30个以上PR并且人工reviewer的采纳率稳定在70%以上再考虑升级。我见过一个团队第一天就上拦截模式结果机器人几条角度清奇的评论把作者惹毛了第二天就关掉了功能。自动化评审的底线是“不添乱”。4. 完整实操从零跑通一个Hermes评审实例4.1 基于Docker的部署步骤Hermes提供了官方Docker镜像这是最简单、也是我比较推荐的部署方式。不管是在云服务器还是在自己的NAS上只要Docker环境可用基本能一键起服务。我先写好一个docker-compose.ymlversion: 3.8 services: hermes: image: hermes-agent/hermes:latest container_name: hermes-review restart: unless-stopped ports: - 8080:8080 environment: # GitHub App配置 HERMES_GITHUB_APP_ID: 123456 HERMES_GITHUB_PRIVATE_KEY_PATH: /app/key/private-key.pem HERMES_WEBHOOK_SECRET: your-webhook-secret # 大模型配置 HERMES_LLM_BASE_URL: https://api.openai.com/v1 HERMES_LLM_API_KEY: sk-xxxx HERMES_LLM_MODEL: gpt-4o-mini # 行为配置 HERMES_REVIEW_MODE: comment HERMES_MAX_DIFF_THRESHOLD: 10000 volumes: - ./keys:/app/key:ro - ./config:/app/config:ro我把私钥文件放到./keys/private-key.pemWebhook Secret和模型API Key都通过环境变量注入。启动命令就是docker compose up -d启动后用docker compose logs -f看日志出现类似Hermes is listening on 8080的字样就说明服务起来了。然后回到GitHub App的配置页把Webhook URL指向这台服务器的公网地址加上路径比如https://bots.example.com/hermes-webhook保存即可。4.2 最小配置文件示例Hermes支持通过一个YAML文件做更细粒度的规则配置。我通常放在/app/config/hermes.config.yml里# Hermes评审规则配置 rules: # 跳过锁文件只对实际代码做评审 ignoreFiles: - *.lock - package-lock.json - pnpm-lock.yaml - go.sum - yarn.lock # 这些关键词触发critical级评审 securityKeywords: - password - token - secret - api_key - authorization # 单文件超过该行数时强制分段处理 largeFileThreshold: 300 # 单次评审最多处理文件数 maxFilesPerReview: 50 # 评论策略 commentStyle: # 行内评论 最终汇总 mode: inline_with_summary # 重复问题合并:相同文件、相似建议只保留一条 deduplicate: true # 低于该严重程度的问题不显示blocker/critical/warning/suggestion minSeverity: suggestion # 自动关闭旧评论 cleanup: enabled: true strategy: minimize这里的minSeverity和deduplicate是噪声控制的关键。如果模型输出里一堆建议级别的问题可以把minSeverity调到warning只保留真正值得关注的问题。4.3 跑通第一个自动化评审的完整记录部署完成之后我创建了一个测试PR内容和缓存相关。PR提交后不到10秒Hermes的日志里出现了一连串输出“Webhook received: pull_request.opened”、“Fetching diff for PR #57”、“Building context...”、“Calling LLM...”、“Review created successfully”。去PR页面一看机器人已经在Files changed页签下挂了一整排行内评论检查认真程度真的超出预期。最典型的一条评论是“this.userCache.get(userId)在缓存未命中时直接返回null调用方没有判空就执行了userInfo.getName()这里会抛空指针。建议在缓存工具类里用Optional包裹返回值或者在调用处补一个空值判断并明确日志输出。”这种评审意见已经接近高级开发者的水平了而且它还能明确指出代码行和修复方向团队成员的反响普遍正面。另一个让我印象深刻的案例是Hermes在某个PR里发现了一个跨文件的问题A文件新增了一个枚举值B文件里对应的switch分支没有更新该枚举结果导致某个配置在前端展示时永远走默认分支。这种牵一发动全身的问题靠人眼review很考验精力靠静态检查工具又完全查不出来Hermes却稳稳拿住了。5. 常见问题与排查技巧实录5.1 高频问题速查表现象可能原因排查与解决办法Webhook收到了但没触发评审事件类型判断错误或pull_request内的action不是opened/synchronize/reopened确认事件负载的action字段看Hermes日志是否输出了“Skipping action: xxx”评论一直发不出来GitHub App没有Pull requests写权限Token没有正确生成检查App权限看GitHub API返回的错误信息确认私钥对应App ID正确PR更新后重复评论没有实现旧评论清理开启cleanup.enabled: true或用API查询机器人历史评论并删除大模型返回内容被截断单次评审内容过多超出模型输出max_tokens调大max_tokens或对超大PR启用分段模式限制单次审查文件数评论定位到了错误代码行position用了源文件行号而非diff行号确保position来自diff解析推荐用line参数配合side参数做更稳定的定位GitHub API请求频繁被限流单次review通过循环逐条调用创建评论接口改为一次createReview传多个comments开启批量模式模型对某语言认知偏差大通用模型对冷门语言支持不足在提示词中补充该语言的特定规范或切换代码模型版本场内网络访问GitHub API超时服务器与GitHub之间网络不稳定在服务侧配置代理或重试机制确认DNS解析正常适当增大超时时间到15秒以上5.2 几个必须亲测才能摸清的细节第一个细节是diff分段的分界线。我刚开始把阈值设成500行结果大文件还是经常出现上下文丢失。后来发现关键不是总行数而是“单个逻辑块的变更密度”。如果一个函数改了200行即使整个文件只有250行diff也应该单独拉出来精读。Hermes的智能体会看diff里的标记把每个hunk视为一个逻辑块这比简单的行数拆分科学得多。第二个细节是避免评论“爆炸”。早期测试时模型经常在一个文件里输出十几条评论其中一半是似是而非的“风格建议”。后来我做了三个改进第一在提示词里明确“只评论导致Bug或重大维护问题的事项”第二配置deduplicate: true第三对相似问题做聚合——比如“多个文件里都用了同一种不安全的字符串拼接”合并成一条评论列出文件清单而不是每个文件各发一条。评论数量从平均9条降到了4条左右可读性提升很大。第三个细节是评审历史对模型的重要性。Hermes在分析PR时会把这个文件最近的提交历史拉出来作为参考。举个例子如果某个文件历史上频繁出现“遗漏空指针判断”的bug模型在审查新PR时会刻意重点检查这一点。这个“根据仓库历史动态调整关注点”的能力是静态工具完全没有的也是Hermes体现“智能”的关键点。第四个细节是人工兜底机制。我的原则是Hermes的评审意见仅供参考所有合并操作必须由真人执行机器人永远不拥有合并权限。这个原则保证了两点一是代码质量最后的责任人在人那里不会出现“机器人说行但人觉得不行”的甩锅现象二是如果Hermes出现误报也不会阻塞开发流程。自动化评审的目标是“辅助人”不是“替代人”。5.3 提高评审准确率的调优思路我这里还有一个自己整理的调优清单分享出来让模型先审PR描述再审代码。很多PR的代码本身没问题但描述里没写清楚动机导致评审方向跑偏。Hermes的处理是先把PR的标题和描述单独发送一次让模型总结“这个PR想解决什么问题”再把总结作为上下文注入到代码评审里。在提示词里注入团队的代码规范摘要。比如你们团队约定“禁止在for循环里发HTTP请求”就把这条规范写进提示词模型会很听话地遵守。比事后review更高效。建立“高频问题-反馈”闭环。如果人工reviewer发现Hermes漏掉了某类问题可以把这个问题补进提示词的few-shot例子里下次它就会优先关注。这个闭环跑起来之后Hermes的准确率会像滚雪球一样提升。注意成本控制。大模型按Token计费一个大型PR的diff翻译成Token量很大。建议给Hermes设置单次评审的预算上限超出上限就做“快速评审模式”只对核心文件做深度分析其余文件做抽样。我个人的经验是评审一次普通PR的成本大约在0.1到0.5元人民币之间用DeepSeek类API会更省完全可以放开用。但如果仓库特别大、提交特别频繁单月成本可能到几十上百元这时就一定要做预算控制。定期抽查机器人的意见是否被采纳。GitHub API可以查到每条评论下方的回复和点赞情况如果某个文件的评论长期无人问津说明那部分提示词或模型设置可能需要调整。这相当于给评审系统本身做一个review。6. 从“能用”到“好用”的一些体会我实际操作下来的感受是Hermes这类自动化代码评审工具真正改变的不是“机器人帮我看了代码”而是“我知道有一双眼睛始终在盯着每个PR不会因为reviewer忙就漏看”。它尤其适合个人维护者和小型团队——没有专职的Code Reviewer但又不想把代码质量搞得稀烂。如果你准备在自己的项目里接入我的建议是不要追求一步到位先以评论模式跑两周让团队适应有机器人在PR下面说话这件事然后根据大家反馈逐步调整提示词和严重级别最后再启用强制拦截模式。这个节奏虽然慢但远比一开始就全自动要稳。最后分享一个我踩过最深的坑不要用最新最贵的模型要用你最熟悉那个团队代码风格的模型。我测试时发现一些模型评审风格特别“学院派”喜欢长篇大论论述代码风格对团队引入的临时性约定完全不懂换成一个更了解项目上下文、支持自定义指令的模型后评审意见贴合度反而大幅提升。模型能力上限只是地基配置和调优才是决定这个系统好不好用的天花板。