
1. 为什么我要自己写一个PR审查机器人Hermes的诞生背景先说结论这项目解决的是代码评审环节里最费人力的那一部分——重复劳动。如果你所在的团队PR特别多每次review都在跟“缩进对不对”“命名规不规范”“有没有把密钥打印到日志里”这种事较劲那Hermes就是替你扛下这些脏活的助手。我最初想做个自动化代码评审工具是因为团队项目进入稳定迭代期后每天平均有十几个PR挂着等人看。代码量大不说很多PR的核心改动其实没多少但reviewer得先花十分钟把上下文捋清楚再花五分钟挑一些其实可以靠工具发现的问题。时间久了大家都有点倦怠有些PR直接点了approve根本没细看这比没有review还危险。当时考察过市面上的现成方案。商业化产品确实做得不错比如CodeRabbit、Cursor的review功能、以及GitHub原生的一些code scanning能力但有几个问题绕不开第一核心规则引擎不可见没法针对团队自己的代码规范做深度定制第二一些企业项目没法把代码提交给第三方服务做语义分析第三费用也不便宜。综合权衡下来我决定基于GitHub的开放生态自建一个轻量级方案,名字就叫Hermes——希腊神话里替众神传递消息的信使正好贴合“帮人传话给PR提意见”这个场景。这套方案最适合下面这些团队技术栈统一、PR量中等偏上、团队有明确编码规范但缺少执行手段、希望在GitHub生态内闭环、并且愿意花一点部署成本换来长期的人效提升。如果你只是偶尔有几个仓库想加个CI检查那直接用GitHub Actions加现成linter就够了Hermes属于“更进一步”的方案。2. 整体架构与工作流程Hermes如何介入PR生命周期2.1 事件驱动GitHub App 还是 GitHub Actions自动化评审类工具最基本的架构选型就分两条路走GitHub Actions做定时或事件触发或者注册成GitHub App订阅webhook事件。我最终选的是GitHub App原因有三点。第一App的权限粒度细。Actions跑在单独的runner上它需要权限时往往要把token给整个仓库而App可以只申请“Pull requests: Read Write”“Checks: Read Write”这种最小权限。代码评审场景需要读取diff、创建评论、更新检查状态这些权限都能精确匹配不需要碰代码本身的写入权限。第二事件驱动更实时。Actions的触发有排队延迟而且要对每个push都重新跑一遍工作流App直接用webhook订阅pull_request事件PR一开、一更新、一评论服务端立即感知响应基本是秒级。实时性对这个场景很重要——开发者刚把PR推上去五秒后机器人就开始挑毛病这个体验和“跑完CI再看结果”完全不一样。第三跨仓库复用方便。用App可以一次安装到组织下所有仓库规则配置按仓库或按组织级别下发。Actions虽然有reusable workflow但每个仓库要各自引用维护起来散。当然Actions也不是一无是处。如果你的评审逻辑就是“跑几个linter然后展示结果在check里”那Actions是零成本的方案不需要额外部署服务。但要做语义审查、上下文理解、跨文件分析这类重逻辑必须要有一个长驻服务这就是App架构的不可替代性。2.2 完整流水线从webhook到review评论Hermes的完整处理链路是这样设计的GitHub webhook事件 ↓ 事件验证与路由 ↓ 拉取PR元信息 diff ↓ 静态规则引擎linter聚合 ↓ 语义分析模块可选按需调用 ↓ 结果聚合、去重、定位到行 ↓ 以review comment形式回写到PR事件进来之后第一步是验签。GitHub App的webhook请求头里带了一个X-Hub-Signature-256是用App的webhook secret对请求体做的HMAC-SHA256签名。服务端得先验签防止别人伪造请求往你的PR里塞评论。这块实现不复杂但漏掉就是安全隐患。验签之后是路由。pull_request事件有十几个action我们实际关心的只有四个opened新PR、synchronize推送了新commit、reopened重新打开、ready_for_review从draft转正式。其它action比如assigned、labeled直接忽略减少无效计算。接着拉PR元信息包括标题、描述、base分支、head分支、changed files列表。然后根据changed files列表去GitHub API拉取每个文件的patch内容也就是diff片段。这一步是整个评审的数据基础后面所有规则引擎都跑在这份diff之上。静态规则引擎跑完后会产生一堆初步意见。这些意见要经过结果聚合模块处理同一个文件同一行多个规则同时报错合并成一条已经在上一个commit提过、且本次代码没动的意见去重跳过。最后把所有意见按文件、行号组织成review comments通过POST /repos/{owner}/{repo}/pulls/{number}/reviews接口一次性提交。我踩过一个很隐蔽的坑GitHub API创建普通issue comment和创建review comment是两套不同的接口前者只要issue写权限后者需要pull_request写权限。刚开始权限没配对App静默失败查了两天才发现是权限范围少了Pull requests。3. 核心功能拆解与实现细节3.1 自动化静态检查与规则引擎Hermes的规则引擎沿用了“linter聚合器”的思路但做了一层重要的封装每一种语言对应一个规则集规则集内部既可以调用现成的linter工具也可以定义纯函数形式的自定义规则。规则匹配的目标不只是单个文件而是把整个PR的diff汇总成一份结构化数据规则可以感知到“这个改动同时触碰了接口定义和调用方”“这个文件在本次PR里被重命名了”这一类跨文件信息。比如JavaScript/TypeScript仓库默认聚合了ESLint核心规则加上团队自己沉淀的十几条约定。约定包括禁止直接console.log提交主干禁止any类型显式出现在新代码里禁止TODO/FIXME注释遗留函数参数超过四个必须改成对象传参。这些规则本身不复杂实现起来就是一个AST遍历加正则匹配的组合但难在定位准确——意见必须精确到是哪一行、哪一段代码违反了哪条约定。还有一个很多工具没做但很影响体验的点只报告本次PR新增的代码问题。一个历史遗留的坏味道文件PR只改了一行工具不应该把文件里十几个老问题全抛出来。Hermes在生成意见前会先过滤diff上下文只有命中added_line的部分才进入规则判定这样每条意见都跟本次改动强相关开发者不会淹没在老账里。3.2 语义差异审查让机器人看懂“改了什么”纯静态扫描只能解决格式、命名、明显反模式这类问题真正决定代码质量的是改动对程序行为的影响。Hermes的语义审查模块核心能力是识别“跨文件变更是否一致”。举一个实际发生过的例子。后端接口把某个字段从userId改成了userAccountId按说这种改动会牵扯到前端调用方、接口文档、mock数据、类型定义。纯静态规则根本无法判断这是不是一次完整的重构但Hermes的语义审查模块会在PR里做一次“引用追踪”找出所有涉及userId的文件判断它们是否都在本次变更范围内如果有文件仍然引用旧字段而未被修改就给出警告“该字段在接口定义中已改名以下文件仍在使用旧名称疑似遗漏”。这个能力听上去高级但实现并没有用到什么玄乎的AI技术。核心是一种轻量级的跨文件符号索引对每个PR涉及的代码文件做一次解析提取出类型的字段、函数签名、变量名构建一个仓库级别的临时符号表然后在diff上做一致性校验。规则是显式的而非模型推断的所以可解释性强、误报率低。为什么不直接用大模型做全面语义理解因为成本和稳定性都不划算。大模型在“给代码挑刺”这件事上目前还有两个无法容忍的问题一是幻觉它可能会为正且合理的代码给出误导性的修改建议二是响应延迟不稳定一次review等上几分钟开发者早就不耐烦了。Hermes的策略是大模型只做辅助摘要——把PR的变更内容和文件结构喂给它让它生成一段面向reviewer的“变更摘要”帮人快速进入上下文。3.3 审查结果输出与自动修复审出来的意见最终要回到GitHub上输出形式上我做了三种分层。第一层是inline review comment定位到具体行随代码展示这是主力。这类意见通常要求开发者必须处理比如“新增代码里出现了console.log请删除或替换为logger”。第二层是body评论汇总PR首页会出现一条Hermes的汇总评论包含每个文件的检查结论、总问题数、变更摘要还有一份“需要人工重点review的点”清单帮维护者快速决策。第三层是check run状态在PR合并前将Hermes的结果设为required check只要有不通过的项合并按钮就被锁住。这样即便有人想跳过review也绕不过去。自动修复这个功能我一开始加了后来有一段时间把它默认关闭了。原因很现实自动修改代码格式、调整import顺序这种低危项机器做了确实高效但涉及业务逻辑的“建议修复”哪怕改对了也容易给人造成“这个PR不过是机器人修过的”这种错觉反而降低了人对代码的责任感。现在Hermes的自动修复只对两类问题开放——安全的格式化修复和明确的模块替换修复其余一律只提意见不动手。4. 部署配置与踩坑实录4.1 环境准备与GitHub App配置Hermes的服务端我直接用Docker部署在一台4核8G的云主机上数据库用SQLite就够了。整个部署的核心流程分四步。第一步是在GitHub上注册App。进入GitHub组织或账号的Settings → Developer settings → GitHub Apps点New GitHub App。名称随意比如hermes-review-bot。关键的配置在Permissions这一栏Pull requests要开Read WriteChecks开Read WriteContents只开ReadMetadata只开Read。Webhook URL填你自己服务端的公网可达地址加/webhook路径Webhook secret随便生成一串强随机字符串。第二步是生成私钥。App创建成功后在General页面底部可以生成一份PEM格式的私钥文件。这份私钥是用来给App签发JWT的后面拿JWT换installation token所有API调用都用这个token。私钥一定要放安全的地方我见过有人把PEM文件传到仓库里等于把App的钥匙交给了全世界的陌生人。第三步是部署服务端。把仓库里配好的docker-compose.yml拉下来填入环境变量docker compose up -d就完事。有一点提醒服务端必须能用HTTPS接收webhookGitHub不会向HTTP地址重复发送过多请求而且签名校验里header和body的解析跟HTTPS没有关系但如果没有HTTPSwebhook配置页面直接不给保存。国内部署的话建议直接把服务放在有公网IP的机器上加一层Nginx做TLS终止。第四步是安装App到目标仓库。App创建完后默认只有你自己可见把它安装到组织并选择需要生效的仓库。仓库里放一份.hermes.yml配置文件里面声明启用哪些规则集、哪些路径要忽略、是否开启自动修复服务端每次处理PR前先拉取这份配置。4.2 常见问题排查速查表跑了一段时间我把遇到过的典型问题都记了下来整理成一张速查表现象原因解决办法webhook完全收不到webhook URL不可达或secret不匹配先看GitHub App页面里Recent Deliveries点进去能看到投递状态和响应体确认200且签名校验通过评论创建成功但PR页面看不到权限少了Pull requests WriteAPI静默失败检查GitHub App的Permissions改完记得保存新权限会立刻生效机器人评论内容重复同一个commit被多个事件反复触发在代码里用commit SHA做去重记录每个SHA已经处理的检查项重复事件直接跳过评论行号不准直接用了position参数提交commentGitHub新版API已废弃position改用line加side组合commit_id必须传对大批量PR同时触发时请求超时拉取diff和提交comment的API调用有速率限制在服务端做队列按仓库维度串行处理同时把review comments合并成一次请求提交App的token失效忘了刷新installation tokenGitHub App的token有效期只有1小时需要维护一个定时刷新任务这里面最值得展开的是行号的问题。GitHub的review comment定位老版本API用的是position表示在diff中第几行新版本API必须用line表示在文件中的第几行配合side指定是左侧还是右侧。如果你传的commit_id跟当前HEAD不一致GitHub也会拒绝创建评论。所以我在实现里做了一次对齐先拿最新commit SHA再对每个文件计算diff行号与文件行号的映射关系最后用映射后的坐标提交评论。这个映射逻辑是整个评论模块最容易出错的地方建议在集成测试里覆盖“多行评论”“文件尾部行号”“删除行评论”这三个边界场景。另外部署时还要注意一个安全细节如果规则引擎里的某个规则需要把代码片段发给外部服务做分析比如调用大模型API一定要对发送的内容做脱敏。我会先把字符串字面量里看起来像token、密钥的东西用占位符替换掉再决定是否外发。代码评审这种场景隐私合规问题不能忽视。5. 效果数据与我的取舍思考5.1 落地效果真实使用了一个季度后的数据Hermes在团队内部跑了差不多一个季度覆盖6个主力仓库、38个活跃开发者累计处理了超过700个PR。我统计了几个核心指标。机器人有效拦截的高频问题里排名靠前的是这些无意提交的密钥或敏感信息、debug日志残留、不符合规范的文件改动、跨文件字段改名遗漏、以及毫无信息量的PR描述。其中最有价值的是密钥拦截这个季度里Hermes至少四次在PR合入前拦下了误提交的API key每次这种事故如果流到生产环境代价都是按小时计的审计和轮换成本。从人效角度看每PR的首次review响应时间从之前的平均4小时以上缩短到了机器人上线后的即时反馈人工reviewer拿到手上的已经是一份带批注的代码不用再从零开始通读。团队里review的“第一遍粗筛”基本被机器人接管了人工只需要聚焦在逻辑设计、架构合理性这些机器不擅长的事情上。但我也要坦白一个数字机器人的意见采纳率也就是开发者真正按建议修改的比例大概是75%左右。剩下25%里有真误报也有“团队默认允许”的例外情况。开始的时候我很难接受这个数字后来想通了作为一个辅助工具80%左右已经是一个健康的采纳率剩下的恰好可以作为规则迭代的输入。5.2 这条路的边界与下一步做了这么久我对“自动化代码评审”的边界有了更清醒的认识。机器人能保证的是规范的执行和明显错误的拦截但它读不懂产品的商业模式也判断不了“这样改是不是更符合调用方的使用习惯”。这些属于人的经验判断机器可以辅助无法替代。如果按影响力排序我觉得自动化评审工具真正改变的不是“查bug”这个动作而是团队对待code review的心态。有了Hermes之后开发者在提交PR前的自检意识明显变强了因为知道有一个严格的机器人会盯着规范的细节与其等它提意见不如自己在本地跑一遍规则。这种反向促进是我最开始没预料到的额外收益。下一步我计划做的三件事第一把规则引擎的配置做成可视化界面让非技术背景的owner也能调整规则第二增加更智能的跨文件影响分析覆盖更复杂的重构场景第三把Hermes的审查结果接入团队的度量看板用数据帮助管理层看到评审流程的改进空间。如果你也想在团队里落地类似的自动化评审能力我的建议是从“最痛的规则”开始——先把你最不希望看到的那类错误写进规则里让机器人严格把关再慢慢丰富规则集。不要一上来追求大而全那样只会得到一个所有人都讨厌的吹毛求疵的机器人。让工具从解决一个真实痛点起步它的价值才会被团队真正认可。