
写自动化代码评审这件事我琢磨了挺长时间。代码评审一直是研发流程里最费人力、又最容易流于形式的一环而把 Hermes 这种智能体接到 GitHub PR 上做审查正好能把“人盯人”变成“人机配合”。这篇文章我会把我搭这套系统的完整思路、具体配置、踩过的坑和实际效果一次性讲清楚适合后端开发、研发效能工程师以及所有想给团队引入 AI 审查的读者。内容里涉及的步骤和配置示例都可以直接照着复制再根据自己的项目情况调整。1. 整体设计思路一套PR审查系统要解决什么问题1.1 传统代码评审的痛点和自动化机会先聊点实际的。做过几年研发管理的朋友应该都有感受PR 评审看着是个“把关”动作实际上大多数时候是负担。开发者在深夜提了 PR第二天 reviewer 打开一看500 行改动其中 300 行是格式化调整真正需要动脑的逻辑只有 100 行。这时候 reviewer 能怎么办大概率是挑几个明显的问题评论一下然后 Approve。不是不负责任而是人的精力和注意力确实有限让工程师把时间花在逐行扫改动上本身就是一种浪费。自动化代码评审的价值就在于把“读代码”和“评代码”这两个动作拆开。重复性、机械性的问题交给机器比如风格不一致、明显的内存泄漏风险、安全敏感函数使用不当、缺少异常处理等等。人只需要看机器标记出来的高优先级问题以及机器无法判断的架构层面的事情。这和我之前折腾过的一些静态分析工具不同那些工具虽然也能发现问题但通常只会报“规则触发了”不会告诉你“这里的并发处理有问题建议加锁并评估竞态条件”。而 LLM 擅长的地方恰好就在这——它能综合上下文理解代码意图给出带解释的、像人话一样的建议。选择 Hermes 来干这件事核心原因是它作为智能体不是简单地“读一段代码然后输出一段话”而是可以编排成一个完整的工作流拉取 PR 元数据、读取 diff、逐个文件分析、汇总问题、调用 GitHub API 提交评论。如果把传统 LLM 比作一个只会回答问题的顾问那 Hermes 更像一个有手有脚的员工能把“审查”这件事从头做到尾。1.2 自动化审查的系统边界和目标设定设计这套系统之前我给自己定了几个边界避免做到一半失控。第一自动化审查的目标不是“替代人做决定”而是“帮人快速定位高价值信息”。所以系统输出必须是结构化的每个问题要标注文件位置、严重级别、修改建议All 的评论要能在 PR 页面上直接看到。第二不要追求“一次审查解决所有问题”先聚焦在逻辑缺陷、安全隐患和明显违反团队规范的问题上把误报率控制在可接受范围内否则 reviewer 每次都要看一堆废话最后肯定会把 bot 关掉。第三响应速度要快PR 提交后 2 到 3 分钟内必须给出初步意见慢了就失去了意义。基于这些目标我确定的系统形态是这样的GitHub 仓库配置 Webhook当 PR 的 open、synchronize 事件触发时把事件推送给本地运行的一个服务服务收到事件后调用 GitHub API 拿到 PR 的元数据和 diff 内容将 diff 切分、分批送入 Hermes 智能体由它按预设的审查规则进行分析最后把分析结果整合以评论的形式提交到 PR 对应的 Commit 或 Conversation 里。这个链路看起来不复杂但真正落地的时候会碰到一堆细节比如 diff 过大怎么切分、并发请求怎么控制、审查结果怎么去重、Hermes 偶发超时怎么处理。这些我后面一步步讲。2. 技术方案深度拆解Hermes在审查链路中的角色2.1 选型对比为什么用Webhook而不是定期轮询立项的时候我第一个纠结的问题是用 Webhook 实时触发还是写一个定时任务去扫所有 open 的 PR。后来我选的是 Webhook原因很直接轮询的延迟不可控。假设每 5 分钟扫一次一个 PR 从提交到被审查最坏情况要等 5 分钟而且随着仓库变多、PR 数量增加轮询会反复拉取没变化的 PR白占 API 配额。GitHub 的标准 API 未认证时一小时 60 次认证后一小时也才 5000 次看起来不少但如果你有几十个仓库每次拉取都要请求多个接口配额消耗非常快。Webhook 的方式是事件驱动的PR 一变就通知你没有多余的请求。这个模式在 GitHub 上非常成熟仓库 Settings 里配置一个 Webhook URL选择推送 eventsGitHub 就会在对应事件发生时把 JSON payload POST 到你提供的地址。我实际配置的时候选择了pull_request事件并且在服务端对 action 做了过滤只处理opened和synchronize也就是 PR 更新了代码两种情况。reopened我觉得也可以处理但实践下来容易和synchronize重复触发所以干脆砍掉。还要注意一件事Webhook 默认对网络要求确定你的服务必须能从 GitHub 外网地址访问到。如果服务部署在有防火墙的内网需要在防火墙开一个公网可达的入口或者用内网穿透类工具把本地服务暴露出去。这个问题后面会专门在常见问题里展开。2.2 Hermes智能体在审查流程中的职责划分整个系统里有几个角色GitHub Webhook 是事件源服务端是一个胶水层我用 Python 写的比较顺手Hermes 是分析引擎GitHub API 是回写通道。这些角色各管各的尽量不要互相越界。我踩过的第一个坑就是把太多逻辑塞进 Hermes。最开始我让 Hermes 自己决定“该审查哪些文件”“跳过哪些文件”“怎么调 GitHub API 回评论”结果整个流程变得不可控它经常做出奇怪的选择比如漏掉关键文件或者干脆在分析中间停下来。后来我把职责重新划分清楚了胶水层负责所有确定性的操作——拉取数据、解析 diff、过滤文件、调用 API、格式化输出Hermes 只负责它最擅长的一件事就是“理解代码并输出评审意见”。这让整个系统的行为变得可预测排查问题也容易很多。用生活里的例子来说Hermes 就好比是医院里的专家门诊医生他只负责看片子、写诊断意见而挂号、拍片、取报告这些流程性的事情是护士和前台干的。你非要让专家去挂号那门诊效率一定乱套。2.3 审查服务的运行形态和部署位置关于 Hermes 部署在什么位置我试过两种方式。一种是直接跑在开发机上简单省事适合个人项目和刚开始试验的阶段另一种是用 Docker 跑在服务器上稳定可控适合团队正式使用。我最终选择了 Docker 部署原因有三一是服务器 7x24 小时在线PR 什么时候提交都能及时响应二是环境隔离不会因为开发机升级、换电脑导致服务中断三是资源可控Hermes 这类 LLM 应用吃内存和 CPU 比较猛用容器可以限制资源上限。如果你只是自己玩开发机上跑完全没问题只要保证服务不关机就行。我见过有人直接挂在个人电脑上后来电脑合盖休眠PR 审查静默失败了好几天才发现。所以正式使用至少也要扔到一台长期在线的机器上。3. 核心细节落地方案审查规则、提示词与实践配置3.1 审查规则的设计原则先定红线再谈风格很多人做 AI 代码审查上来就让模型“全面分析代码质量”结果输出了一堆“建议增加注释”“建议提取公共方法”这种正确的废话Reviewer 越看越烦。我的做法是先把审查规则分级让模型知道轻重缓急。我把规则分成三个级别。第一级是阻断级发现这类问题应该直接 Request Changes包括明显的空指针/未定义变量、SQL 注入/命令注入等安全风险、死循环或递归无出口、密钥硬编码等等。第二级是警告级包括异常被吞掉、资源未关闭、并发访问未加锁、明显的逻辑分支错误。第三级是建议级比如命名不规范、函数过长、缺少必要的注释。这个分级的意义在于给模型一个明确的输出框架让它把评审意见按 Severity 分组用户在 PR 页面上能一眼看出哪些必须处理哪些可改可不改。规则不是一次性定死的。我第一版规则写得非常细结果 Hermes 在“是否算违反规范”上频繁纠结反而忽略了真正重要的逻辑问题。后来我把规则收敛到 10 条以内每条都用“条件 示例 严重级别”的格式写清楚效果反倒更好。3.2 提示词模板的工程化写法Prompt 是这套系统里最值得反复打磨的部分。我现在的提示词大致结构是这样的角色设定、仓库背景信息、评审规则、输出格式要求、兜底指令。角色设定很关键我让 Hermes 扮演“有 10 年经验的后端技术专家”这个设定不是为了玄学而是能明显提升它在输出时的专业度和条理性。仓库背景信息我通过一个配置文件的变量注入包括项目类型、技术栈、常用框架版本、团队约定等。这些信息能让 Hermes 在分析时更有针对性比如知道这是个 Spring Boot 项目它就会主动关注 Bean 注入、事务、循环依赖等问题如果是个 Python 项目它就会关注 GIL、异步、内存引用等问题。输出格式我是这样设计的要求 Hermes 按 Markdown 格式输出每个问题包含文件路径、行号如果 diff 里有、问题描述、严重级别、修改建议。下面是我实际在用的简化版提示词你们可以参考你是一位拥有10年后端开发经验的高级工程师正在为一个{project_type}项目做代码审查。 项目技术栈{tech_stack} 团队规范要点{team_rules} 请审查以下 Pull Request 的代码变更重点关注 1. 逻辑错误和边界条件 2. 安全漏洞注入、越权、敏感信息泄露等 3. 资源管理和异常处理 4. 并发与性能问题 5. 严重违反团队规范的问题 输出格式要求 - 使用Markdown格式 - 每个问题单独一段格式为**【严重级别】文件路径:定位位置** - 问题描述 修改建议 - 严重级别只允许三个值BLOCKER必须修复、WARNING应该修复、SUGGESTION可选改进 - 如果没有发现任何问题输出LGTM未发现明显问题。 下面是需要审查的代码变更内容 {diff_content}这个模板看起来不复杂但实际调试的时候发现一个关键问题如果 diff 内容太长Hermes 容易漏看后面的文件。所以我在胶水层把 diff 按文件切块每个文件单独送一个完整的审查请求最后再合并结果。这样单个请求的内容量小了模型注意力更集中错误率明显下降。3.3 审查速度和成本的控制策略Herman 处理一份 diff 的速度和质量跟你怎么控制上下文有直接关系。我最早图省事把整个 PR 的完整 diff 一次性丢给 Hermes结果改动稍微大一点响应时间就飙到 5 分钟以上而且经常出现“只分析了前几个文件”的情况。后来我做了一个很简单的优化按文件维度拆分每个文件都作为独立的审查任务执行。这样每个任务的数据量控制在合理范围内执行速度稳定在 30 秒到 1 分钟而且可以并发执行多个文件的审查整体耗时反而比串行一次性处理还要短。成本方面也要算账。如果用云端 LLM 接口按 token 计费一次上百行 diff 的 PR 审查大约消耗 1 到 2 万 token。如果团队 PR 比较多一个月几百次审查成本并不算低。我的解决办法是只审查改动的行不把整个文件全量送进上下文。GitHub 的 API 可以直接拿到 PR 的 diff这个 diff 本身就只包含变更内容所以上下文利用率很高不会浪费在没改的代码上。另外对于超过 300 行的超大 PR我设置了自动跳过规则只在 PR 上评论一句“本次改动过大建议拆分为多个小 PR 以便人工审查”。这不是逃避而是因为超大 PR 本身就不符合良好的 Code Review 实践让 bot 硬上也看不过来。3.4 与GitHub API交互的几个关键细节和 GitHub API 打交道认证方式我用的是一开始的 Token 模式创建一个 Fine-grained personal access token只授予目标仓库的 Pull requests 读写权限和 Contents 读取权限。权限最小化这个原则一定不要省不要图省事开 repo 全权限万一 token 泄露影响面可以控制住。拿 PR diff 有两个接口可以用。一个是GET /repos/{owner}/{repo}/pulls/{pull_number}带Accept: application/vnd.github.diff头直接拿完整 diff 文本另一个是列出 PR 的文件GET /repos/{owner}/{repo}/pulls/{pull_number}/files能拿到每个文件的具体 patch以及文件名、状态added/modified/removed。两个我都会用到files 接口用于过滤和切分diff 接口用于最终组装成审查任务。注意 files 接口分页默认 30 条PR 改动文件特别多时需要用per_page100或者翻页这个细节容易漏。提交评论我用的是POST /repos/{owner}/{repo}/pulls/{pull_number}/comments这是 PR 的常规评论接口。如果你想做行内评论inline review comment要用POST /repos/{owner}/{repo}/pulls/{pull_number}/comments的带 position/line 参数版本或者在 GraphQL 里用 addPullRequestReviewThread mutation。行内评论效果好但实现复杂度高一些对行号的映射要求准确diff 更新后行号可能失效。我第一版用的行内评论后来发现这个问题太频繁干脆改成了统一汇总评论稳定性优先。4. 实操全流程从零搭建Hermes PR审查机器人4.1 环境准备与配置清单我假设你已经有一个 GitHub 仓库并且本机或者服务器上有 Python 3.10 以上环境。需要的依赖不多核心是requests和hermes-agent如果 Hermes 是作为 Python 库调用的。另外需要准备两样东西一个 Hermes 运行环境本地模型或云端接口取决于你用的部署方式一个 GitHub token。我的环境清单如下可以照抄项目推荐配置说明操作系统Ubuntu 22.04 / macOS开发机也凑合但长期跑建议服务器Python3.10太老版本有些库装不上HermesDocker 或本地进程建议单独跑别和业务进程混在一起GitHub TokenFine-grained PAT只勾选目标仓库的 PR 权限消息通道WebhookGitHub 仓库 Settings 里配置4.2 创建审查服务主程序我写的服务核心逻辑其实很薄大致分这几步接收 Webhook → 解析 event → 判断是否需要处理 → 拉取 PR 信息并准备 diff → 调用 Hermes 审查 → 提交评论。下面是一个简化版的代码骨架用的 Flask 做 Webhook 接收端import os import json import requests from flask import Flask, request, jsonify app Flask(__name__) GITHUB_TOKEN os.environ[GITHUB_TOKEN] GITHUB_API https://api.github.com REPO_OWNER your_org REPO_NAME your_repo HEADERS { Authorization: fBearer {GITHUB_TOKEN}, Accept: application/vnd.githubjson, } app.route(/webhook, methods[POST]) def webhook(): payload request.json if payload.get(action) not in (opened, synchronize): return jsonify({ok: True}) pr_number payload[pull_request][number] # 放到后台线程处理避免Webhook响应超时 import threading threading.Thread(targetreview_pr, args(pr_number,)).start() return jsonify({ok: True}) def review_pr(pr_number): # 1. 获取PR信息 pr_url f{GITHUB_API}/repos/{REPO_OWNER}/{REPO_NAME}/pulls/{pr_number} pr_info requests.get(pr_url, headersHEADERS).json() title pr_info[title] description pr_info.get(body) or # 2. 获取文件级diff files_url f{pr_url}/files?per_page100 files requests.get(files_url, headersHEADERS).json() # 3. 按文件逐个送Hermes审查 review_results [] for f in files: if f[status] removed: continue # 删除的文件不用审查 patch f.get(patch) if not patch: continue # 文件名带路径审查意见里要能定位 result hermes_review_file(f[filename], patch, title, description) review_results.append(result) # 4. 汇总提交评论 if review_results: combined_md \n\n.join(review_results) comment_url f{pr_url}/comments requests.post( comment_url, headersHEADERS, json{body: combined_md}, )这段代码只是为了展示流程实际生产环境至少还要加Webhook 签名校验、异常重试、任务去重、日志记录。4.3 封装Hermes审查函数hermes_review_file这个函数是把 diff 文件和提示词模板拼接然后调用 Hermes 拿到结果。以我用的方式为例Hermes 支持 Python SDK 调用也可以起一个 HTTP 服务然后用 requests 调用。我提供两种形态的示意。def hermes_review_file(filename, patch, pr_title, pr_desc): prompt build_review_prompt( project_typeSpring Boot 后端服务, tech_stackJava 17, Spring Boot 3.x, MySQL, Redis, team_rules所有对外接口必须有参数校验; 禁止硬编码密钥, diff_contentf文件: {filename}\ndiff\n{patch}\n, ) # 方式一如果用Python SDK from hermes_agent import HermesAgent agent HermesAgent(...) response agent.chat(prompt) # 方式二如果用HTTP服务 # response requests.post(http://127.0.0.1:8080/chat, json{prompt: prompt}).text return response这里有一个很重要的点Hermes 的输出不能完全信任。有时候它会输出一段带错误 Markdown 格式的内容甚至中途断掉。我会在拿到输出后做一个简单的后处理检查是否包含“LGTM”或“未发现明显问题”如果没有反而更好直接往上拼如果格式太乱就加一层规则修正。更稳妥的做法是让 Hermes 输出 JSON然后用json.loads解析失败就重试一次。重试仍然失败的话就把这条任务标记为 skipped并在汇总评论里告知“有 N 个文件审查超时/失败”不阻塞 PR 流程。4.4 配置GitHub Webhook并联通测试Webhook 的配置路径是GitHub 仓库 → Settings → Webhooks → Add webhook。Payload URL 填你的服务地址 /webhook路径Content type 选application/jsonEvents 选择 “Let me select individual events”只勾选 Pull requests。配置完之后我习惯做一个联通测试随便改一点代码提一个临时 PR看 Webhook 是否被触发。如果服务没收到请求优先检查三件事Payload URL 拼写、服务端口是否开放、GitHub Secrets 里填的 token 是否有权限。GitHub 的 Webhook 管理页面会记录最近几次投递结果投递失败能看到响应错误码这个排查看板非常有用比对着日志猜高效得多。4.5 部署运行用Docker把服务固定下来最后部署阶段我写了一个非常简单的 Dockerfile 和 docker-compose 配置方便团队其他人复用。Dockerfile 核心就三行基础指令不展开docker-compose 里需要注意的是把 GitHub token 用环境变量传进去不要写死在镜像或代码里。另外给服务加个健康检查接口比如/healthz返回 ok方便监控。还有一个容易被忽略的点Webhook 对响应时间有要求GitHub 会在投递后等你的服务响应如果超过 10 秒没有返回GitHub 会标记投递失败。所以 Webhook 接收接口里一定不要同步做完整审查先把事件接收下来、立即返回 200再把审查任务扔到后台线程或者消息队列里去处理。我第一版就是直接在 Webhook 里同步调 Hermes结果 GitHub 那边显示投递超时服务端还在跑两边都很难受。改成异步处理之后这个坑彻底消失了。5. 实战效果复盘真实PR审查案例解析5.1 一个典型PR的完整审查过程我用自己的一个实验仓库跑了一段时间积累了一些真实的审查案例。拿其中一个比较典型的 PR 来说改动是一个订单模块的重构大概 200 行 diff涉及 6 个文件。Hermes 对这个 PR 的处理结果分成了三部分安全与正确性问题 2 个、代码风格问题 3 个、整体评价 1 段。其中有一个问题我觉得特别有价值。改动里的一个函数从数据库查了一批订单数据然后循环对每个订单做状态判断如果状态异常就抛异常。Hermes 指出如果这条 SQL 查询结果是按时间排序的而业务上希望最先进入处理的订单优先被检查那么查询条件缺少一个显式的ORDER BY依赖数据库默认排序会导致行为不确定。这个问题人工 review 很容易忽略因为看起来“程序逻辑是对的”但 Hermes 结合 diff 上下文嗅到了排序依赖的味道。这类问题就是 AI 审查真正值钱的地方。还有一个安全类问题代码里把用户传入的条件直接拼进了 SQL 的LIKE语句Hermes 立刻标记为 SQL 注入风险并给出了改用参数化查询的具体建议。这种问题其实很基础但人确实会漏尤其是赶进度的需求里。5.2 审查质量评估误报率和有效率的平衡当然Hermes 也不是每次都准。我统计了自己仓库里大约 40 次审查记录人工复核后发现明确有价值、能帮助发现真实问题的评论占大概 60%属于常识性建议但不够关键、可忽略的占 25%明显误报的占 15% 左右。这个数据在可接受范围内但必须设置一道“严重级别校准”机制。校准的办法是在提示词里强调“只有确定是问题才算 BLOCKER / WARNING不确定的一律算 SUGGESTION”。这条约束能显著把误报率往下拉因为模型倾向于宁多勿漏你需要在提示词里强制它“克制”。我改了一版提示词之后BLOCKER 级别的误报率从大概 30% 降到了 10% 以内效果非常明显。5.3 与人工审查的配合节奏自动化审查上线之后团队里最需要磨合的是“人怎么看待 bot 的评论”。我定了两条规矩第一bot 评论里的 BLOCKER 级别问题作者必须在合并前给出回应要么修复、要么在评论里说明理由第二WARNING 和 SUGGESTION 级别的评论人工 reviewer 可以自行决定是否采纳bot 也只是一票不代表最终结论。实际运行下来团队接受度比预期高因为大家发现 bot 确实能挡住一些低级错误而且 bot 不会催人、不会情绪化这对提升体验帮助很大。6. 常见问题与排查技巧实录6.1 问题速查表现象可能原因解决方案Webhook 显示投递失败服务不在线 / 响应超时 / URL 不对检查服务日志确认能公网访问改异步处理先返回 200收到事件但无审查评论Token 权限不足 / 评论接口报错检查 token 是否有 PR 权限查看服务日志的 API 响应状态码Hermes 输出中断上下文太长 / 模型超时按文件拆分 diff限制单个请求的 diff 行数加超时重试评论定位不准确diff 行号漂移改用统一汇总评论评论里写清楚文件路径和符号名重复审查同一 PRWebhook 重复触发 / 重试机制冗余在服务端用 pull_number commit_sha 做幂等缓存6.2 关于响应超时和异常重试稳定运行的核心是“宁可跳过也不要卡死”。我给不同环节都设了超时GitHub API 请求设 15 秒超时Hermes 单次审查设 90 秒超时超过直接标记失败。失败之后重试一次还是失败的就把这次审查任务单独放进一个 failure 列表日志里打印出来。这里分享一个我踩过的真实坑最开始我做的是“审查失败就重新投递 Webhook”结果和 GitHub 的重试机制叠加一个失败的请求会触发好几次重复处理直接把服务打挂了。后来我把触发链路和服务处理逻辑彻底分开——Webhook 只负责写入一个任务队列真正干活的是消费者。这样就算 GitHub 重发事件队列里重复的任务也能通过 commit_sha 去重不会造成重复审查。6.3 成本控制与资源占用心得如果你用的是本地跑模型的方式务必给 Hermes 进程设内存上限。我第一次是在一台 8G 内存的云主机上部署模型加载后直接把内存吃满服务 OOM 了好几次。后来给 Docker 容器设了mem_limit: 4g又调整了模型参数量才稳定下来。如果用的是云端接口主要控制 prompt 长度diff 太大就截断只保留关键上下文比硬喂全部内容更省钱也更稳定。另外强烈建议加一个“单 PR 审查文件数上限”的开关比如超过 15 个文件的 PR 直接跳过不给bot 也不给 API 造成压力。这个策略看起来有点粗暴但它能强迫开发者保持小步提交的好习惯是真有实效的。6.4 团队落地时的一些建议最后给准备在团队里引入这套系统的朋友几个基于实际经验的建议。上线初期不要直接让 bot 在正式仓库里评论先在个人仓库跑一周人工评估输出质量。上线之后每两周复盘一次误报率并且把团队确认过的误报案例整理成负样本改进提示词或规则。审查规则一定要让一线开发参与制定他们最清楚团队的死穴在哪里也让后面执行时阻力小很多。我个人实际使用下来的体会是Hermes 做 PR 审查最大的价值不在于替代人而是把人的注意力从“读完每一行”中解放出来让人更专注在“架构、边界、演进”这些真正需要经验的判断上。这套系统我从搭好到现在迭代了三个版本每次踩坑都让流程再顺一点。如果你也打算搞一个类似的自动化评审工具照着上面的链路一步步搭跑通第一版并不难真正花时间的反而是后面的规则校准和排错机制打磨但这一步做好了后面的收益会越来越明显。