尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

代码评审自动化实践:Hermes智能体如何重塑GitHub PR审查流程

发布时间:2026/9/9 6:25:52

资讯中心
01
ARTICLE

代码评审自动化实践:Hermes智能体如何重塑GitHub PR审查流程

代码评审自动化实践:Hermes智能体如何重塑GitHub PR审查流程
1. 代码评审的隐形瓶颈PR越积越多问题却还是那几类接手维护一个中等规模的团队仓库之后我第一个直观感受是PR不是在“排队”是在“堆尸”。开发高峰期一个下午能有十几条PR涌进来人工评审平均要等半天到一天小的逻辑问题反复在不同PR里出现——键名写错、异常被吞、依赖没锁版本、死代码没删。不是评审人不认真而是人脑在短时间内处理几十个跨模块的diff时天然只能抓大放小。小问题漏掉往往要到code review结束、合并之后再由线上告警来“提醒”。那时候我开始认真考虑一件事能不能让一个自动化的审查Agent先进场把所有机械的、重复的、规则明确的检查全部吃掉把人力的注意力聚焦到真正的架构设计和业务逻辑上。于是就有了这篇博文的主角——Hermes一个跑在GitHub工作流里的自动化代码评审智能体专门做PR审查这一件事。先说清楚它能干什么每当有人打开或更新一个PRHermes自动拉取变更内容结合仓库内的既有规则、依赖关系、静态扫描结果和上下文理解输出一份评审意见。它不只是跑一下lint也不只是看有没有密钥它会把“这段代码改了之后可能影响谁”“这个并发场景有没有竞态”“这个错误处理路径会不会吞异常”这类需要一定推理的问题一并点在PR里。这篇文章不是官方文档的复读机而是我在把Hermes接入真实项目、跑通完整流程、处理了上千条PR审查之后沉淀下来的实践记录。适合两类人看一类是受困于PR积压、想引入自动化评审又不知道从哪下手的团队维护者另一类是已经搭了个雏形、但发现规则写得不好、误报太多、大家开始不看的同学。我会把架构、接入步骤、规则设计、成本控制、以及我踩过的坑都摊开讲。2. Hermes审查流水线的整体架构一次PR事件到评论回写的完整链路2.1 从Webhook到评论中间究竟发生了什么很多人以为自动化评审就是把LLM接进来丢一段diff进去然后让它输出评论。真要这么简单项目早就满地跑了。实际落地时最难的部分不在“让模型看懂代码”而在“让整个链路稳定、可控、不会把仓库搞乱”。我的落地架构大概分为五层触发层监听GitHub Webhook事件主要是pull_request下的opened、synchronize、reopened、ready_for_review以及pull_request_review_comment用于响应后续追问。数据层根据PR编号拉取元信息标题、描述、提交列表、Changed Files、每个文件的diff、基础分支与当前分支的对比快照。分析层跑静态检查脚本、读取仓库规则配置再把这些结构化结果连同diff一起打包拼接成交给大模型的分段上下文。决策层模型输出分文件、分严重级别的审查意见每个意见附带行号和类别标签。回写层通过GitHub REST API创建Review把评论挂到对应代码行上顺带在Check Run里给一个“审查通过/需要修改”的状态标记。这五层之间不是串行就算了。我实际做的时候每一层都加了缓存、重试和熔断。后面讲踩坑的时候会细说但记住一句话自动化工具一旦挂在了CI或者合并流程上它本身就是一套分布式系统稳定性优先级高于一切炫技功能。2.2 为什么数据层是整个系统的命门说个容易让新人翻车的地方diff数据不是拉下来一份就完事。GitHub的application/vnd.github.diff返回的是基础分支和PR分支的完整对比结果大仓库一次几万行diff很常见。如果直接把这几万行塞给模型不说token成本单是上下文窗口就顶不满。我的做法是分三档处理# 简化示例按文件大小和改动行数分档 PRICING_BUCKET { small: {max_changed_lines: 50, max_file_size: 100_000}, medium: {max_changed_lines: 300, max_file_size: 500_000}, large: {max_changed_lines: 2000, max_file_size: 2_000_000}, } def classify_file(diff_entry): changed_lines diff_entry[additions] diff_entry[deletions] file_size diff_entry[file_size] if changed_lines 50 and file_size 100_000: return small if changed_lines 300 and file_size 500_000: return medium return large小文件直接完整送入分析中等文件做基类上下文剪裁只保留引用到的符号定义大文件默认只审diff涉及的函数和类不展开整段历史逻辑。这样做还有一个好处当某个文件被修改了2000行以上你其实不太需要Agent逐行看——人也不会这么看。正确地退化为“只做变更影响评估不做行级评论”反而比硬塞结果更可用。2.3 分析层为什么要“多管齐下”而不是只信大模型纯靠大模型做代码审查短期看着惊艳长期用会有两个问题一是幻觉会编造不存在的风险点二是对仓库里已经存在的、别人定死的规矩一无所知。所以我的分析层里大模型只是其中一个组件前面还挂了老实的确定性检查gitleaks扫密钥只输出新增泄露不翻历史旧账eslint/checkstyle直接读仓库现有配置把报错项作为硬性输入自定义脚本来查“允许出现在PR中的调试标记”比如debugger、console.log、print()依赖锁定文件的diff对比确认有没有在非预期情况下改lock文件。这批输出会以类似“工具结果引用”的方式拼进给模型的上下文里。实现时我在Prompt里写明“以下工具输出是真实扫描结果如果报告中标注了ERROR你必须将其视为优先级最高的阻塞项。”这一步是为了绑住模型的手脚避免它自由发挥把误报当成真问题也避免它漏掉机器已经查出来的硬伤。3. 审查规则的分层设计哪些该拦死、哪些该提示、哪些该闭嘴3.1 规则三层模型硬阻塞、软反馈、静默建议我见过最失败的自动化评审配置是把所有规则全部设成error级别。结果就是PR里飘着五十条评论真正有用的三条被淹没开发直接把机器人拉黑。所以从我第一天接入Hermes就确立了一个原则自动化Agent的输出必须分层而且要允许仓库owner通过配置调整每一层的强度。我常用的三层模型长这样层级用途典型例子对应操作L1 硬阻塞合并前必须解决密钥泄露、高危依赖、明显的空指针、死锁、破坏性API变更未做兼容review状态置为REQUEST_CHANGESL2 软反馈应该讨论缺少错误处理、并发风险、资源未关闭、测试覆盖不足行级comment不阻塞合并L3 静默建议可选优化命名统一、函数拆分、注释补充、代码重复汇总到一个总评里不逐行刷屏L1规则数量我刻意控制在20条以内全部是确定性问题规则越少越容易维护误报率越低。L2是主力大部分时候都是“你这里的错误处理吞掉了异常建议至少log一下”这类。L3有一个关键约束不在文件diff里散落评论而是合并成一条“优化建议汇总”挂在PR总览里。3.2 仓库级配置用YAML让每条规则都可被覆盖Hermes的规则配置文件我放在仓库根目录下的.hermes/rules.yml里。设计上先给一套默认值再允许仓库自行覆盖阈值、开关和汇报级别。rules: # 硬阻塞L1 secret_detection: enabled: true level: block tools: [gitleaks] invalid_migration: enabled: true level: block pattern: db/migrate/.* hint: 数据库迁移文件不允许在普通功能PR中出现 # 软反馈L2 unhandled_error: enabled: true level: warn confidence_threshold: 0.7 # 置信度低于70%时降级为静默 test_coverage_for_new_code: enabled: true level: warn required_files: [**/*_test.*, **/*.spec.*] ignore_paths: [docs/**, examples/**] # 静默建议L3 repeated_code: enabled: true level: silent max_occurrences: 3 keywords: - TODO - FIXME - HACK exclude_paths: - dist/** - build/** - vendor/** - *.lock - package-lock.json - yarn.lock有几个细节值得注意。第一confidence_threshold是对模型输出置信度的“降级开关”我要求模型在输出每条评论时同时给0到1的置信度低于阈值的自动降到L3而不是直接丢弃。这样既保留信息又不会污染主要评论流。第二exclude_paths里的锁文件是必加的否则每次依赖升级都会触发一堆没营养的文件diff审查。第三规则的hint字段是对模型的额外引导比如“数据库迁移文件不允许出现在普通PR中”这条如果不给hint模型很可能看不出来文件名和目录的含义。3.3 默认规则会漏掉的那两类“人的直觉”规则文件写得再细也覆盖不了所有情况。我用了一段时间后发现模型有几类判断是规则脚本永远给不了的而恰恰是人工评审最希望它盯住的。第一类是“变更影响范围”。比如你改了一个底层的SessionManager类它被几十个Service引用正常的规则检查只会看你这一处改动有没有语法错误但Hermes会去读引用关系然后提示“这个改动会影响以下模块的初始化流程建议补一下冒烟测试”。这是我在Prompt里加了一个任务角色定义才做到的我让它先读目录结构和引用关系再对改动做影响面分析最后才落到具体的行级评论。第二类是“历史上下文关联”。很多bug不是这次PR引入的而是这次PR把原本的一个假设打破了。比如说某个函数一直没做空值校验是因为顶层已经保证不为空而你这次新增的调用路径绕过了顶层保护。这种问题本质上是“跨文件、跨历史”的推理大模型相比纯规则工具有天然优势也确实是它在整个体系里最值钱的部分。4. 从零接入注册GitHub App、配置Webhook与首次审查4.1 选GitHub App而不是个人Token如果你只是想在本地试一下用个人访问Token当然快。但要在团队仓库里正式用我强烈建议以GitHub App的方式接入。原因有三个权限可以收敛到只能读代码、写PR评论不会碰到仓库设置、分支保护这些高危权限App安装到仓库后与个人账号解耦谁维护这套系统的密钥都不会影响个人账号安全GitHub App的Token是短期动态生成的比一个长期有效的Token泄露了要好处理得多。注册路径是在GitHub账号的Settings - Developer settings - GitHub Apps里新建一个App。我当时设置的权限大致是权限项权限值用途Pull requestsRead write创建Review、发表评论ChecksRead write写Check Run状态ContentsRead-only拉取代码和diffMetadataRead-only获取仓库基础信息IssuesRead-only读取PR关联的issue上下文4.2 Webhook事件的取舍与配置Webhook不是把所有事件都勾上就行。勾太多你的服务会收到大量无关请求勾太少又会有一些PR场景漏触发。我跑了两个月后最终固定在下面四个事件上pull_request.opened新PR进来首轮审查。pull_request.synchronizePR更新了提交做增量复审。pull_request.reopened关闭的PR重新打开重新审查。pull_request.ready_for_review从Draft转成正式PR启动审查。注意不要勾pull_request.edited。只改标题和描述不会影响代码质量反而会触发大量无效审查浪费token也容易让团队觉得这个机器人在乱刷存在感。Webhook的Payload URL指向你部署的服务地址比如https://hermes.example.com/github/webhook。GitHub会验签你在App设置里生成一个Webhook Secret服务端校验证书签名后才会继续处理。# 用Flask实现的极简入口重点是验签 from flask import Flask, request, abort import hmac, hashlib, os app Flask(__name__) SECRET os.environ[GITHUB_WEBHOOK_SECRET] app.post(/github/webhook) def handle_webhook(): signature request.headers.get(X-Hub-Signature-256, ) body request.get_data() expected sha256 hmac.new( SECRET.encode(), body, hashlib.sha256 ).hexdigest() if not hmac.compare_digest(signature, expected): abort(401) event request.headers.get(X-GitHub-Event) payload request.get_json() # 只处理我们需要的事件 if event pull_request: action payload.get(action) if action in (opened, synchronize, reopened, ready_for_review): enqueue_review_task(payload) return {ok: True}其实这一层不需要做得太重关键是不要阻塞Webhook请求。GitHub的Webhook有超时阈值如果服务在10秒内不返回它会认为投递失败并进入重试队列。所以我这里只做验签和入队真正的审查逻辑丢给后台worker异步执行这样体验最稳。4.3 首次审查的完整流程假设你已经把App装到了测试仓库接下来第一次真实运行的流程是开发者在仓库里创建一个PR事件触发服务端收到Webhook。服务端用App的私钥动态生成一个Installation Token用这个Token去调GitHub API。它先拉取PR的files接口拿到所有变更文件的列表和diff。读取仓库根目录下的.hermes/rules.yml合并默认规则。对文件做分档处理小文件直接审大文件做上下文剪裁。拼接成Prompt后发给模型模型返回结构化JSON。服务把JSON里的每一条评论映射回具体的文件、行号调用create review接口。如果存在L1规则命中Review状态置为REQUEST_CHANGES否则置为COMMENT。最后写一个Check Run名字叫hermes/code-review结论为success或neutral。第一次跑通这个流程的时候我盯着PR页面上冒出来的那几条评论看了半天老实说有些震撼。不是因为它写得有多惊艳——实际上里面有一条误报后来我调了规则——而是整个闭环完全自动从开发push代码到评论出现在PR上只需要一两分钟。4.4 自托管服务的部署要点服务本身我用Docker部署在一台2C4G的机器上。这个配置对单仓库、日审查量100次以内的场景绰绰有余瓶颈从来不在算力而在API调用频率。如果你有多个仓库要接建议队列和worker分离同时给GitHub API限流留好余量。# docker-compose.yml关键片段 services: hermes: image: hermes-agent:latest env_file: .env ports: - 8080:8080 volumes: - ./data:/data # 存放审查历史记录 restart: unless-stopped.env里至少要配这几个变量GITHUB_APP_ID123456 GITHUB_APP_PRIVATE_KEY_PATH/run/secrets/hermes.private-key.pem GITHUB_WEBHOOK_SECRETyour_webhook_secret MODEL_API_BASEhttps://api.example.com/v1 MODEL_API_KEYsk-xxxx MODEL_NAMEhermes-1.5 REVIEW_MAX_FILES_ONCE30 REVIEW_WORKER_CONCURRENCY4这里REVIEW_MAX_FILES_ONCE是一个我自己加的保险。有的PR一次性改了七八十个文件全部塞进一次审查会非常慢。我设置的30个文件上限一旦超过就拆成多批审查或者只审其中改动最大的一半文件剩余的在总评里提示“由于文件数量较多本次仅完成前30个文件的逐行审查”。5. 评论风格工程把自动回复写得像人话且不惹人烦5.1 自动化评论的“刷屏灾难”是怎么发生的接入Hermes两周后我第一次收到团队成员的抱怨不是“审查有误报”而是“评论太多了PR页面像弹幕”。点开每条评论看内容单独拎出来都算合理但几十条堆在一起体验非常差。人看代码评审的时候注意力是有限的评论密度一旦超过某个阈值反而会漏掉真正重要的问题。那次之后我做了三处调整可以说直接影响了这个工具在团队里能不能活下来同一类型的问题只报一次不逐行刷屏。比如一个文件里有5处console.log只挂一条评论列出行号清单。行级评论只留给L1和L2L3绝不逐行出现。每次PR审查的总评论数设了上限默认20条超过之后其余的自动合并进总评。5.2 评论模板的标准化固定评论格式能显著提升阅读效率。早期让模型自由发挥风格千奇百怪有些评论长到像一篇小作文有些又短到看不出问题在哪。后来我要求输出严格走模板每条评论必须包含四个部分问题摘要、影响分析、修改建议、严重级别。**Issue**: 这里的 session 刷新失败后错误被吞掉了 **Impact**: 用户会在登录状态过期后看到空白页而不是被引导到登录页 **Suggestion**: - 捕获异常后至少调用 log_warn() 记录一条日志 - 返回上游一个可识别的错误码而不是静默继续 **Level**: L2 (warn)这个模板的妙处在于它把“批评”变成了“问题描
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。