构建自动化代码评审机器人:Hermes如何结合大模型与规则引擎优化PR审查
最近一个月我花了大量业余时间把一个叫 Hermes 的自动化代码评审机器人从零搭起来跑在 GitHub 上专门处理 PR 审查这件事。起因很直接我们团队某个仓库的 review 排队已经排到离谱——一个改动不到 200 行的 PR因为核心 reviewer 一直在忙硬是在队列里躺了两天等合进去的时候早跟主干产生冲突了。当时我就在想为什么 code review 这么重要的一环却从技术问题变成了纯粹的资源瓶颈Hermes 不是一个从零造轮子的静态检查工具它更像一个介于 CI 和 code review 之间的智能助理通过 GitHub App 订阅 PR 事件解析拉取的 diff同时跑规则引擎和大模型两套评审逻辑最终把结论以 review comment 的形式回写到讨论里。它能做的不只是报 lint 错误而是把逻辑缺陷、边界条件、安全风险、可维护性滑坡这些原本依赖人眼的判断用机器先过一遍。这篇文章我会尽量把整个过程说透包括架构设计、事件接入、diff 解析、评审内核、部署踩坑以及最终效果。不管你是想自己搭一个同类机器人还是想优化团队现有的 review 流程这篇都有直接的参考价值。1. 为什么我决定写 Hermes 而不是继续用现有的工具1.1 手动评审的三个排队点先聊聊痛点。很多团队嘴上说重视 code review实际上 review 是整个研发流程里最脆弱的一环。我总结下来有三个排队点第一个是人等评审PR 提交后没人看尤其当核心 reviewer 一个人负责多个模块时其他人都被卡住。第二个是评审等人reviewer 想认真看代码但他的时间被会议、线上问题、需求评审切得稀碎根本凑不出一段连续的专注时间。第三个是标准等人每个 reviewer 的侧重点不一样同一个 PR 有人只看逻辑、有人只看命名、有人只看安全导致同样水平的代码在不同 reviewer 手里得到的反馈天差地别。这三个排队点背后是一个共同问题人的注意力是稀缺资源而且极其不稳定。代码评审本身有大量机械性的检查工作——有没有硬编码密钥、有没有调试输出残留、有没有明显越权接口、有没有事务没回滚。这些检查完全不需要人来做但它们占了 reviewer 很大比例的注意力导致真正需要人判断的架构合理性、接口设计、业务逻辑正确性反而得不到充分思考。1.2 传统工具覆盖不到的死角解决机械性检查市面上不是没有工具。ESLint、SonarQube、CodeQL 这些静态分析工具非常成熟但它们的强项是确定性规则你没加分号它一定报你用了 eval它一定报。但代码问题远不止这些确定性规则能覆盖的。举个例子一个函数里对一个共享列表做了clear()而不是重新赋值可能引发并发问题。这种问题静态规则很难定义因为你没法用一个正则或者 AST 模式去描述这个 clear 是否安全它依赖上下文。再比如一个接口没有对传入的 offset/limit 做上限校验单看代码语法完全合法但真实场景里这就是一个会被扫出 OOM 的风险点。人类 reviewer 看得懂上下文但是慢静态工具速度快但是只看得了表面。中间地带就是 Hermes 想占的位置把能程序化判断的机械问题交给规则引擎把需要上下文理解的问题交给大模型两者结果合并后统一评论回 PR人类 reviewer 只需要看机器筛过一遍后的重点。这也是我给它取名 Hermes 的原因——专职跑腿传信的神把 review 的粗活先干完。1.3 我要做的范围控制做一个 AI 评审机器人听起来很酷但如果不控制范围项目会迅速变成一个无底洞。我在立项时给自己划了几条边界不做代码风格 lint那是 ESLint 的活不做编译和测试那是 CI 的活不做一键合入最终合入权限仍然留给人。Hermes 只做一件事在 PR 事件触发后尽最大努力在 60 秒内产出一份初评报告指出可疑代码位置和理由。这个定位决定了它的技术选型不需要很复杂。我最终用 Node.js 写了个服务核心依赖就三个一个 HTTP 框架接收 Webhook、一个 GitHub 官方 SDK 调 API、一个支持 OpenAI 兼容协议的大模型客户端。整套东西用 Docker 打包加一个 Redis 做任务去重和队列没有引入什么重框架。2. 一次 PR 评审在 Hermes 里要经过哪些环节2.1 四个核心模块的职责Hermes 内部拆了四个模块职责非常清晰。第一个是 Event Listener负责接收 GitHub Webhook 推送的事件完成验签、解析、幂等判断然后把任务丢给队列。第二个是 Analyzer负责拉取 PR 的元数据和 diff把文件列表、变更行号、补丁内容整理成标准结构再分别喂给规则引擎和大模型。第三个是 Policy Center负责管理所有评审规则、提示词模板、严重级别阈值相当于机器人的大脑皮层。第四个是 Reporter负责把 Analyzer 输出的 findings 提交到 GitHub包括行级评论、概览评论和 review 提交。这四个模块之间用 Redis 队列解耦。EventListener 只负责收消息和写任务Analyzer 只负责分析和产出 findingsReporter 只负责回写。好处是任何一个环节出了问题不会拖垮其他环节如果大模型服务不稳定我可以临时把 Analyzer 降级成只跑规则引擎至少机器还能给出基础反馈。2.2 为什么选 GitHub App 而不是个人 Token 或 Actions最开始我考虑过两个更省事的实现方式一个是直接用个人访问令牌调 GitHub API另一个是写成 GitHub Actions。但这两个方案都有硬伤。个人访问令牌的问题在权限粒度太粗。一个 PAT 往往拥有整个账号的仓库读写权限一旦被泄露攻击者能访问的就不只是某个仓库而是你名下的所有资源。即使我把权限缩小到指定仓库GitHub App 仍然是更规范的选择App 的权限是按安装维度分配的并且可以给每个安装方单独签发短期令牌泄露后的爆炸半径小得多。而且用 App 的话用户可以自己选择把 Hermes 安装到哪些仓库不需要把我的账号绑到他们的组织里。GitHub Actions 的问题在运行模型不匹配。Actions 是一个拉取式的执行环境它是在事件发生时由 GitHub 托管启动一个临时容器跑完就销毁。这听起来很适合做评审但实际上有资源限制单次作业最长执行时间有限大仓库的 checkout 和依赖装完耗时巨大并且它没有常驻状态没法做幂等去重、缓存、速率控制这些精细操作。我想要的 Hermes 是一个常驻的、可控的、可观测的服务而不是一个一次性脚本。所以最终选了 GitHub App 自建 Webhook 服务的方案。2.3 并发与幂等同一个 PR 被反复触发怎么办用 Webhook 接入 GitHub 的第一个坑就是事件重复。开发者每推一个 commit 到 PR 分支GitHub 就推送一次 synchronize 事件如果开发者连推了三次Hermes 就会收到三个几乎一样的任务。如果不对任务做幂等处理就会出现一个 PR 收到三条重复评审评论的尴尬场面reviewer 体验极其糟糕。我的方案是给每个任务建立一个唯一键pr:owner:repo:number:head_sha其中 head_sha 是 PR 分支的最新提交哈希。一个 PR 即使被反复触发只要 head_sha 没变新的任务就会被 Redis 的 SET NX 命令挡住不会重复执行。同时在数据库里记录last_reviewed_sha如果 Hermes 把评论回写时发现当前 head_sha 已经不是最新的了就放弃回写等新 sha 的事件到来再做一次完整评审。这保证了任何时刻Hermes 对某个 PR 的评审结论都只针对最新代码。3. 接入 GitHub 的关键细节Webhook、JWT 与安装令牌3.1 Webhook 配置让事件自己找上门在 GitHub App 的设置页面里有一块 Permissions Webhooks 配置区域。你不需要把所有事件都勾上只需要订阅和 PR 评审相关的事件pull_request、pull_request_review、issue_comment如果需要监听 review 请求变化再加一个pull_request_review_requested。pull_request事件里又分很多 action包括 opened、synchronize、reopened、ready_for_review。对于自动化评审来说核心是 opened 和 synchronize前者代表新 PR 创建后者代表 PR 分支有新提交。订阅事件之后GitHub 会在对应事件发生时向你在 App 配置里填写的 Webhook URL 发送 POST 请求。这个 URL 必须公网可达我生产环境是通过 Nginx 反代到 Hermes 服务。收到请求后的第一件事不是解析 body而是验签GitHub 会用你在 App 设置里配的 Webhook Secret 对请求体做 HMAC-SHA256 签名放在X-Hub-Signature-256头里。Hermes 必须用同一个 Secret 重新计算签名并比对否则任何人都可以伪造事件往你的服务里灌垃圾任务。3.2 认证流程从私钥到安装令牌GitHub App 的认证和普通 API Token 完全不一样本质是一个应用身份加安装身份的双层结构。应用身份靠私钥签发的 JWT 来表示安装身份靠 Installation Token 来表示。流程是这样先从 GitHub 生成应用的私钥 PEM 文件然后拿它签发一个有效期很短的 JWTJWT 的 payload 里必须包含issApp ID、iat签发时间、exp过期时间GitHub 要求不超过 10 分钟。拿这个 JWT 去请求POST /app/installations/{installation_id}/access_tokensGitHub 会返回一个 Installation Token有效期 1 小时。之后所有代表该安装方调用的 API 请求都用这个 token 作为 Bearer Token。这套流程的核心价值在权限隔离。同一个 Hermes 服务可能被安装到多个组织每个组织是一个独立的 installation各自的 token 权限范围互不相通。我在代码里封装了一个 token 管理器在内存里缓存 Installation Token接近过期时提前刷新避免每个请求都走一遍 JWT 签发流程拖慢响应。注意JWT 的iat必须使用实际签发时间GitHub 会对时间偏移做校验偏差超过 30 秒会直接拒绝。服务器时钟如果漂移严重第一步就过不去。3.3 本地开发联调Webhook 转发到 localhostWebhook 是推模式本地开发时没有公网地址GitHub 推不过来。官方推荐的做法是用一个叫 smee 的 Webhook 转发服务GitHub 把事件推到 smee 分配的临时公网地址smee 客户端再把事件转发到你的 localhost。这样本地跑 Hermes改代码、看日志、断点调试都非常方便。不过要提醒一句smee 用于开发调试很爽但不建议直接用在生产环境。一是它的公网地址任何人都可以猜到事件内容如果包含敏感信息存在泄漏风险二是转发稳定性无法保障。生产环境还是建议自建公网入口配合 Nginx 做 TLS 终结和 Webhook 验签。3.4 事件丢失补偿靠定期扫描兜底Webhook 是尽力而为的推送偶尔会丢事件。Hermes 接不到 synchronize 事件就意味着某次 push 后的代码永远得不到评审。为了解决这个问题我在服务里加了一个兜底定时任务每隔 5 分钟扫描一次组织里所有仓库的 open PR对比每个 PR 的 head_sha 和last_reviewed_sha发现不一致就补发一个评审任务。这个扫描任务同时解决了另一个问题——刚刚部署 Hermes 时历史遗留的 open PR 也需要做一次评审。没有这个兜底机制Hermes 只对部署之后新建或更新的 PR 生效价值大打折扣。4. diff 是怎么变成评审意见的4.1 先看懂 GitHub 的 diff 数据结构Analyzer 最关键的一步是把 GitHub 返回的 diff 数据解析成结构化信息。通过GET /repos/{owner}/{repo}/pulls/{pull_number}/files可以拿到文件级变更列表每个文件对象包含filename、status、additions、deletions、changes、patch字段。其中patch是带行号信息的 diff 文本格式长这样 -10,6 10,7 export function calculateTotal(items) { let total 0; for (const item of items) { total item.price; total item.tax; } return total; }这里最核心的是 hunk header -10,6 10,7 。它的含义是旧文件从第 10 行开始涉及 6 行新文件从第 10 行开始涉及 7 行。注意不是旧 10 行变成新 10 行而是旧文件第 10 行起的 6 行区域对应新文件第 10 行起的 7 行区域。实际调用 API 时我建议加上per_page100参数并且循环拉取所有页。否则一个改动了几十个文件的 PRGitHub 默认只返回第一页 30 个文件漏掉的完全没有参与评审这个后果比误报严重得多。另一个需要注意的边界单个文件的 patch 内容可能被 GitHub 截断。API 返回的 patch 字段对超大 diff 只保留部分内容如果发现changes字段远大于 patch 实际覆盖的行数就需要退回到GET /contents/{path}?ref{head_sha}拿完整文件内容去分析。我在实现时对 patch 做了长度判断patch 不够长就自动降级拉全量文件。4.2 行号映射为什么评论会挂错位置把评审意见写到代码行旁边是 Hermes 体验很关键的一环。GitHub 的行级评论 API 支持两种定位方式。第一种是传统的position参数它指的是评论在 diff 文本patch中的段内偏移。这个参数理解起来很绕——它不直接对应代码文件的行号而是对应 patch 里 hunk block 的第几行。而且 GitHub 后来标记了这个参数为 deprecated不再推荐使用。第二种方式是linesideside可以是RIGHT新文件或LEFT旧文件line是实际文件中的行号。这个方法更直观也是我最终采用的方案。要从 patch 文本计算出新文件行号需要一个解析器逐行扫描 hunk header用正则提取旧文件起始行和新文件起始行然后维护两个计数器。以 -10,6 10,7 为例解析出 old_line10new_line10接下来 patch 里每出现一行以开头的内容new_line 加 1每出现一行以-开头的内容old_line 加 1上下文行空格开头两者都加 1。这样扫完整个 hunk每个 diff 行都能映射到实际文件行号。这套解析逻辑我在第一版实现时偷懒没写全直接用了定位工具库推荐的position结果踩了个大坑GitHub 的 position 实际上是从 hunk 第一个之后开始计数的行号不是整个 patch 文件全局行号。多个 hunk 存在时 position 会错乱评论被挂到了完全不相干的代码上。后来彻底改成lineside方案才没有再出现问题。提示如果你只需要在新文件上评论绝大多数情况下 side 用 RIGHT 就行。只有当评论针对被删除的旧代码时才需要 sideLEFT。4.3 规则引擎第一版我放了哪些规则进去大模型不是万能的它不稳定、有延迟、还会一本正经地胡说八道。所以在 Hermes 里规则引擎承担的是确定性过滤职责大模型只做需要理解力的开放题。第一版规则引擎我放了几类高确定性规则硬编码密钥检测匹配 AWS Access Key、私钥块、常见 API key 模式命中直接 error 级别。调试残留检测console.log、debugger、print、var_dump等高置信度残留。危险 API 调用eval、exec、child_process.exec、document.write命中后由大模型判断上下文是否真正存在风险。大变更预警单个文件新增超过 500 行或单 PR 变更超过 1000 行时提醒 reviewer 拆小。资源释放检查检测 try-catch 块里是否存在数据库连接/文件流未关闭的隐患。每条规则都会输出一个结构化 finding包含严重级别error/warning/info、文件名、行号、规则编号、可读的 message。规则引擎的产出格式完全确定方便后续做报告合并也方便做回归测试——我后面专门给规则引擎写了一个 fixture 测试集每次改规则都要跑一遍防止把误报修出新误报。4.4 大模型评审提示词决定输出质量规则引擎打底之后大模型负责更开放的推理。但大模型不是把整个 patch 塞给它就行那样既超 token 上限输出质量也很差。我自己的提示词模板分四个部分第一部分定义角色。我会告诉模型你是一位有十年经验的资深代码评审专家你在审查一次 PR 变更。 第二部分提供上下文。包括仓库名、PR 标题、PR 描述、核心文件路径列表、README 里提取的模块简介。 第三部分是变更内容。按文件分组给出 patch并标注每个文件的新旧路径。 第四部分定义输出要求。要求模型严格按照 JSON 数组格式返回 findings每个 finding 必须包含 severity、file、line(optional)、title、description。同时给出负面清单不要评论代码风格、不要重复规则引擎已报的内容、不要在没有把握时给出修复建议。这个提示词模板经过了好几轮迭代。最初我加了请给出修复建议结果模型输出超级长每条评论都在推销自己的方案把整个 PR 讨论区刷得没法看。后来我把输出约束改成只指出问题和理由不提供具体修复代码刷屏问题立刻缓解。置信度字段也很有用模型输出的每个 finding 都会被要求带上 confidence 字段Hermes 只把 confidence 为 high 或 medium 的写进 PR 评论low 的统一丢进后台报告供人工翻阅。对于超过上下文窗口的大 diff我按文件分组后逐个文件调模型最后把所有文件的 findings 合并。合并时做一轮位置校验如果模型给出的文件名在本次 PR 的变更列表里不存在就丢弃如果 line 数为空或者超出文件变更范围也不写行级评论只合并到概览评论里。4.5 去噪与合入策略让评审结果可信而不是可憎自动评审最怕的是误报太高reviewer 打开 PR 看见 30 条机器评论其中 25 条都不成立他以后就再也不看 Hermes 了。信任一旦丢失这个工具就废了。去噪我做了三层。第一层是规则引擎的优先级设计规则引擎的 error 级别 findings 永远保留warning 级别如果和 LLM 的高置信度 finding 重叠合并成一条LLM 只保留 confidence 为 high 和 medium 的结果。第二层是评论上限控制单次评审最多回写 10 条行级评论多余的 findings 合并成一条其他问题汇总以概览评论形式贴出。这样评论区不会被淹没。第三层是延迟评论新版本提交后如果某个问题在上一版本已经评论过了不再重复评论避免开发者在同一个地方被反复提醒。5. 从本地脚本到 7x24 服务部署与运行实践5.1 Docker Compose 拓扑本地脚本跑通之后部署成常驻服务是必须跨过的一道坎。我的推荐拓扑是 Docker Compose 起两个容器一个是 Hermes 主服务另一个是 Redis。主服务内部同时承载 Webhook 接收、异步任务处理、定时扫描三个职责没有单独拆 worker因为初期 PR 量不大单实例完全够用。一个最小的 docker-compose.yml 长这样version: 3.8 services: hermes: image: hermes:latest restart: unless-stopped ports: - 3000:3000 environment: HERMES_APP_ID: ${HERMES_APP_ID} HERMES_PRIVATE_KEY_PATH: /run/secrets/hermes-private-key.pem HERMES_WEBHOOK_SECRET: ${HERMES_WEBHOOK_SECRET} HERMES_REDIS_URL: redis://redis:6379 LLM_API_KEY: ${LLM_API_KEY} LLM_BASE_URL: ${LLM_BASE_URL} depends_on: - redis redis: image: redis:7-alpine restart: unless-stopped前面挂一个 Nginx 做 TLS 和反向代理把/路径转发到容器的 3000 端口。Nginx 有两个参数必须调client_max_body_size要设大一点因为 GitHub 的 Webhook 请求体在超大 PR 时可能到达几 MB默认的 1MB 限制会直接 413proxy_read_timeout也要调大如果某个请求触发了一次长时间同步任务Nginx 过早断开会让 GitHub 以为服务不可用触发它的自动重试机制造成重复任务。5.2 密钥管理最容易被忽视的环节Webhook Secret、GitHub App 私钥、LLM API Key这三个密钥任何一个泄露都是事故。我的做法是GitHub App 私钥永远不放进代码仓库部署时通过 Docker secret 或环境变量注入Webhook Secret 和 App ID 存在环境变量里生产环境用部署平台的密钥管理服务管理。还有一个小细节容易被忽略Installation Token 的有效期只有 1 小时而且有速率限制。如果每次请求都临时获取一个 token频繁调 API 时会收到 403 或 429。我在内存里加了缓存token 用满 50 分钟就主动换新这样既避免过期又不会频繁触发换新请求。LLM API Key 同样要限流限本地缓存。我用的模型服务支持并发限制超过并发会返回 429。Hermes 会对所有发往模型的请求做并发控制默认同时最多跑 3 个请求其余排队。这样即使一个 PR 涉及十几个文件也不会瞬间把模型服务的配额打爆导致关联的所有任务一起失败。5.3 限流、重试与熔断GitHub API 本身的速率限制也要处理。安装令牌的速率限制是按仓库资源配额计算的正常量级够用但定时扫描任务如果每个仓库都翻一遍 open PR容易撞到限制。我的做法是给定时任务单独降频并且根据响应头里的X-RateLimit-Remaining动态调整扫描间隔。收到 429 或 403 时不硬重试而是读取Retry-After头把任务推迟相应时间。LLM 服务的稳定性是另一个变量。线上环境出现过模型服务超时导致整个评审任务悬挂的情况后来做了三件事所有模型请求必须设置超时时间我设 45 秒超时后重试一次重试仍然失败就把任务降级成只跑规则引擎。降级后的结果会附加一行说明告诉 reviewer 当前大模型评审暂不可用。这让 Hermes 在大模型服务故障时仍然能承担一部分基础评审功能而不是完全瘫痪。5.4 日志与灰度先让 Hermes 在一个仓库里跑两周上线第一天我不建议把所有组织仓库都接入 Hermes。风险在于你还没摸清提示词和规则的误报率就让几百个开发者被机器评论轰炸口碑很容易崩。我的做法是先挑一个活跃度中的仓库试点设置环境变量控制 Hermes 只对带有review-hermeslabel 的 PR 生效。所有评审结果自动评论但我每天会人工翻一遍这些评论把明显误报或低质量的反馈记下来用来调整规则和提示词。日志方面Hermes 输出结构化日志每个评审任务带着pr_number、head_sha、task_id、elapsed_ms、findings_count、rules_findings、llm_findings、final_comment_count这些字段。这样后续可以用日志聚合工具按 PR、按时间、按规则维度做分析看误报率趋势、看模型延迟变化。没有这些指标优化提示词只能靠猜。6. 实测数据与翻车记录Hermes 到底靠谱吗6.1 一个月里 Hermes 抓到的典型案例试运行一个月Hermes 在试点仓库一共评审了 80 多个 PR产出了 300 多条评论最终被开发者认可并采纳修改的占比大约六成。这个比例在自动评审工具里已经算不错了。有几个案例我印象很深。一个是我们某服务的部署脚本 PR改动的是清理 S3 旧备份的流程。Hermes 的规则引擎在 diff 里匹配到了形似 AWS Access Key 的硬编码密钥直接报 error。虽然最后人工确认测试环境的假密钥但这个提醒本身没问题——这种密钥如果漏进生产脚本就是安全事故。另一个案例是 Hermes 的大模型在某个异步任务处理代码里发现了一个竞态条件两个协程同时修改一个共享的 map其中一方没有加锁。这种问题靠规则引擎永远查不出来而人类 reviewer 如果在疲惫状态下也容易漏掉。最有意思的是一个 N1 查询问题。PR 里对一个列表循环查数据库每查一次都触发一次 ORM 查询。Hermes 的模型在审查时把这个问题点出来了并标注了疑似 N1 查询的标题。这个 PR 的开发者收到评论后很快就修了还专门找我说这个提示很准。那一刻我觉得整个项目都值了。6.2 翻车现场行号错位与规则误伤当然Hermes 也翻过车而且翻得很精彩。最早一版我用position做行级评论定位时某个 PR 的评审结果把内存泄漏提示挂到了一个完全无关的空函数上。开发者打开评论时一脸问号以为系统出了问题。排查后确认是多 hunk patch 的 position 计算错误从那之后我彻底放弃 position 方案全部改用lineside解析。还有一次误报事故出在密钥检测规则上。我把匹配模式写得过于宽泛把测试代码里的一堆 fake key、示例 key 全报成了 error 级别。结果就是某个 PR 里 Hermes 一口气发了十几条检测到硬编码密钥的错误开发者直接跑来找我问是不是仓库被扒了。这个教训告诉我规则引擎的正则匹配一定要做上下文过滤比如排除测试目录、排除常见示例前缀并且要先用历史真实代码库跑一遍 Before/After 对比确认不会批量误伤。6.3 团队怎么接住 Hermes 的输出工具本身做得好是一回事团队用得顺不顺是另一回事。Hermes 刚上线时开发者看到 PR 里多了很多机器评论第一反应不是感谢而是觉得又来个搅局的。后来我和团队约定了几条规则Hermes 的评论只有 error 级别的会出现在对话流里其他全部折叠到项目概览的机器人评论中Hermes 的结论不 blocking merge只作为提醒如果开发者和 Hermes 的意见相左可以直接回复一条带标签的评论来忽略该条提醒Hermes 不会再次插话。这套交互规则比算法本身更重要。因为自动化评审工具存在的意义是为了减少摩擦如果它反而制造了大量新的沟通摩擦那不管技术多先进都不该上。让机器守好建议者的边界把这个身份守住了团队才会愿意长期使用。6.4 衡量收益的四个指标评估一个自动化工具值不值得做不能靠感觉。我给自己定了四个指标平均首次评审时间从 PR 创建到 Hermes 给出第一条有效评论的时长人工 review 平均处理时长对比接入前后 reviewer 完成一轮 review 的耗时问题提前发现数量统计那些在 merge 后变成线上故障的问题里有没有是 Hermes 提前指出的误报率即开发者标记为无效/不采纳的评论占比。跑了一个月数据比预想的好。试点仓库的平均首次评审时间从原来的几小时缩短到几分钟人工 review 的单轮耗时下降了大概三分之一。误报率经历了一个从高到低的过程——前两周在 40% 左右调整规则和提示词后降到 20% 上下。这个误报率虽然还有优化空间但已经不会让团队产生关掉它的冲动了。7. 从 PR 评审到团队规范的自动执行7.1 把评审结论沉淀成团队知识Hermes 跑着跑着我发现它又产生了一层新的价值所有产出的 findings 都落了库而这些历史数据是团队代码质量的低成本画像。比如某段时间内某个模块的并发问题出现频率急剧上升说明那个模块的架构或人员变动可能有问题某个规则在某类变更里频繁命中说明团队在这类变更上缺乏一个共同的编码规范。我在 Hermes 后面加了一个很简单的日报脚本每天凌晨汇总前一天的 findings按文件和规则维度聚合生成一份极简报告发到团队的即时通讯频道。这份报告不推送给所有人只有架构师和 tech lead 会看到。它的作用不是追责而是让技术决策者能感知代码库的健康趋势。7.2 从评审到自动改动建议下一步我打算在 Hermes 的能力范围里增加自动补丁建议。具体做法是当大模型产出一个高置信度的 bug finding 时再让它额外生成一个建议版本的代码块Hermes 收集后以 diff 形式附在评论后面。然后通过一个 GitHub App 的按钮或者简单的 HTTP 接口让开发者可以选择一键应用建议。这个功能本质上就是把 Hermes 从评审机器人升级成推荐改动机器人它会进一步缩减修 bug 的往返时间。这个扩展对提示词和评论格式要求很高因为补丁代码如果不对比没有建议更坑。所以我在设计时只对 confidence high 且修复路径非常明确的 finding 开放补丁功能其他情况仍然只给建议方向不给具体代码。7.3 monorepo 场景下的差异化评审我们另一个仓库是 monorepo里面同时放着前端、后端、基础设施代码。统一的评审提示词显然不合理——前端的重点在状态管理和渲染性能后端的重点在数据一致性和安全边界基础设施代码的重点在权限和控制面。Hermes 目前支持按路径前缀配置不同的提示词模板和规则输出比如frontend/目录下的改动只跑前端规则backend/目录下的改动只跑后端规则。这算是我踩了 monorepo 的坑之后总结出的经验也是后续迭代优先级很高的一项能力。从最初的review 排队排到崩溃的小抱怨到现在 Hermes 能在每个 PR 发出的几十秒内给出初步评审意见这个项目带给我的收获不止是技术层面的。它让我重新理解了自动化工具在设计时应有的克制哪些事该交给机器哪些事必须留给人机器和人之间的协作边界在哪里。如果你也要做类似的自动评审机器人我最大的建议是——先别急着堆功能把 diff 解析、行号映射、幂等去重这些基础链路做扎实再考虑怎么调用大模型。地基稳了上面长什么楼都好说。