
在凌晨两点的那个时候, 我依然在进行代码PR的审查工作。在上个月的这段时间里, 我们团队的成员接纳到了一个由外部委托的项目任务, 当时有三名实习阶段的年轻人一同向代码仓库提交了他们编写的程序版本。其中最为令人感到惊愕的状况发生在某一天, 那一天往代码库里堆放了多达12个合并请求, 每一个合并请求里面平均包含的关于300行代码内容的修改量。我自己从晚上时钟指向10点这个时刻开始着手进行审核工作, 一直埋头苦干直到凌晨两点多的时候方才将上述任务全部完成完毕——导致我在随后第二天的例行团队站会期间, 因为极度疲劳而险些在站立状态下进入睡眠状态。更让人感到崩溃的事情, 其实并不是那个数量有多少而是那些质量非常底下。具体来讲, 有一个实习生竟然把API key这种非常重要的东西直接硬编码到代码里面并且提了交, 还有另一个人更是把 .log 日志文件弄得满地都是, 剩下的那一个提交的 SQL 查询语句连最基本的索引都没有加, 结果就是面对一个拥有两百万行数据的大表时, 系统只能傻傻地进行全表扫描。而且特别让人无语的是, 这些问题的类型每次都是一模一样的, 简直是同样的低级错误反反复复地出现, 你刚认认真真地完成了一次代码审查, 以为这下没问题了, 结果下一次这些错误依然原封不动地再来一趟。讲实话, 人工写代码这个东西属于那种重要但是不紧急的任务, 要是拖延上两天肯定没有人去催促, 可如果时间拖长了代码的质量就会呈现直线下降的趋势, 更加现实的一个情况是小团队里根本不存在专职的人员负责对代码进行审查, 基本上都是谁有空谁会稍微看两眼, 如果只是随便看一眼能看出什么东西来呢, 顶多看个名字规不规范而已, 连 SQL 注入这样的问题恐怕都发现不了。当时我内心就产生了一个想法, 不知道是否有可能性, 可以安排某一位对象来代劳完成此项工作任务。答案是可以的。整个方案的费用是零。具体来说, 每个月有2000分钟的免费时长, 而且软件也是开源免费的。用户只需要一个 API key。在注册的时候就会赠送500万 token。这些额度足够来审核几百个 PR。那些是无需花钱就能够拿来用的计算资源, 它们的任务主要是负责把脚本给触发起来, 并且让脚本能够得以顺利运行。一共有两条触发路径, 你需要根据具体的使用场景来做出选择。推荐采用代码一提交就立即进行审计的方式, 这样开发者就不用等待了, 而且也不需要自己去维护服务器。实操四步搭起来第一步, 就是要把那个创建的东西给弄出来。在仓库里建.//-.ymlname: Hermes Code Reviewon:pull_request:types: [opened, synchronize]jobs:review:runs-on: ubuntu-latestpermissions:pull-requests: writesteps:- uses: actions/checkoutv4with:fetch-depth: 0- name: Get PR diffrun: |git diff origin/${{ github.base_ref }}...HEAD /tmp/pr.diffecho DIFF_SIZE$(wc -l /tmp/pr.diff) $GITHUB_ENV- name: Hermes Reviewif: env.DIFF_SIZE 0run: |# 安装 Hermescurl -fsSL | bash# 用 Hermes 审查代码hermes chat -q 审查以下 PR 代码变更检查1) 安全漏洞 2) 代码风格 3) 潜在 bug 4) 性能问题。给出具体建议用中文回复。PR 变更如下\\\$(cat /tmp/pr.diff)\\\ /tmp/review.md# 把审查结果贴到 PR 评论gh pr review ${{ github.event.pull_request.number }} \--comment --body $(cat /tmp/review.md)env:DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}GH_TOKEN: ${{ github.token }}这里需要对关键配置的参数意义做出解释说明。把这段 YAML 代码复制到你的项目仓库里面去, 只需要修改一行内容就可以了, 具体的做法是把原来的部分替换成你正在使用的模型所对应的环境变量名字, 如果你使用的是某种特定的模型, 那就填写那个型号对应的环境变量名, 如果采用的是另一种情况, 就写另一种情况对应的那个环境变量名。在进行到第二个步骤的时候, 需要去对API Key这个项目进行相关的设置处理。# 去 platform.deepseek.com 注册进「API Keys」页面创建 key# 复制 key 后在 GitHub 仓库设置# Settings → Secrets and variables → Actions → New repository secret# Name: DEEPSEEK_API_KEY# Value: «redacted:sk-…»新注册用户在进行注册操作之后可以直接领取到数量为五百万个的令牌, 如果对某次拉取请求进行审阅操作, 并且假设该拉取请求的差异代码行数量是一千行, 那么这一次审阅大约会消耗三千到五千个令牌, 基于上述数据计算, 这五百万个令牌总量足够完成对上千个拉取请求的审阅任务。第三步: 验证的过程, 就是提交一个 PR 来进行尝试。你去建一个专门用来做测试用的分支, 在里面胡乱写一段带有缺陷的代码。下面的那一串内容你不用动脑子去想它的意思, 直接把它复制到你常用的终端工具里面去运行就完事了。git checkout -b test/hermes-review# 在代码里塞一个硬编码的 API keyecho API_KEY «redacted:sk-…» config.pygit add config.py git commit -m add configgit push origin test/hermes-review你去把请求提上去, 然后等待一分钟到两分钟的时间, 之后审查意见就会出现在这个请求的下面了。它会自动检测到那个写死在代码里的 API 密钥, 并且给出警告提示。进行到第四步, 这一步是可选择性的阶段, 目的是为了把审查的质量给进一步提升起来, 具体做法是去使用自定义设置的方法。上面采用的是通用模式, 即对所有代码都使用同一套标准来进行审查处理。然而针对处于不同阶段的项目而言, 你必然期望能够存在具有差异化的审查侧重点情况发生。例如说针对前端类型项目则需要更加关注 XSS 漏洞防范以及可访问性指标提升问题方面的事宜。而另一方面则是针对后端类服务项目需将注意力集中于 SQL 注入风险预防与并发安全性保障等环节内容上。请把这个东西保存一下, 然后把它存到一个专门的地方成为文件。./--.txt你是本项目的代码审查员。审查时重点关注1. 安全SQL 注入、XSS、硬编码密钥、路径遍历2. 性能N1 查询、未使用索引、不必要的大循环3. 代码风格命名规范本项目用 snake_case、函数长度不超过 50 行4. 错误处理是否遗漏 try-catch、是否吞掉了异常信息5. 测试新增逻辑是否有对应测试给出具体行号和修改建议。用中文回复语气友好但直接。随后, 在相应位置对其进行引用。hermes chat -q $(cat .github/hermes-review-prompt.txt)PR 变更如下\\\$(cat /tmp/pr.diff)\\\ /tmp/review.md这样一来, 每一个单独的项目都可以拥有属于它自己的审查标准内容了, 所以就不需要每次都要进行修改操作了。第五步骤, 这个步骤您可以选择不进行执行, 使用定时功能来对数据进行批量检查和审核。如果你不想进行使用, 或者你想要定时扫描所有开放状态下的PR, 这个内容同样能够做到这一点:# 每 15 分钟检查一次仓库的开放 PRhermes cron create 15m \--prompt 检查 ~/projects/my-repo 仓库的所有开放 PR。对每个 PR用 git diff 获取变更审查代码质量、安全、性能。用 gh pr review 发布审查结果。两种方式的对比维度触发时机当进行PR提交这个动作的时候, 就会立即产生触发的效果。定时轮询部署位置云端你自己的服务器延迟秒级分钟级成本每个月的时长是免费提供的, 总共有两千分钟。那就是属于你自己的, 那台机器。在生产实践里真的遇到过让人头疼的坑, 具体到第一个坑呢, 就是在使用git diff这个功能的时候发现拿不来base分支的数据。在执行操作的过程当中, 出现的异常情况为: 当你把代码仓库的状态进行切换之后, 尝试使用git diff命令将主干分支也就是主分支和当前所在的历史节点位置进行对比的时候, 系统返回了一个错误提示, 并且这个错误提示的具体内容是致命性的失败。导致这个问题的一个原因是, 配置里面提到了/v4 这个版本, 它的默认设置是 fetch-depth: 1。这样的设置意味着代码仓库只拉取了最新的一次提交记录。因此, 本地是没有 base 分支的历史记录的。因为 PR 的 base 分支没有拉到本地来, 所以在执行 git diff 命令进行对比的时候, 程序自然就会找不到相关的内容了。解法在 里加 fetch-depth: 0上面 YAML 里已经加了。这一行省了我两个小时 debug。踩到了第二个坑, 就是审查的结果因为篇幅过长, 导致被系统进行了截断处理。症状表现为, 在一项对于包含两千行代码的拉取请求进行的审查工作彻底完结之后, 令人感到意外的情形出现了, 那就是在相关的评论区域中, 仅仅能够看到这一拉取请求内容的前半部部分信息, 至于后面的所有剩余部分则是处于一种无端消失不见的状态, 没有任何踪迹可寻。造成这种情况的原因是, 当使用gh pr命令并结合--body参数进行数据操作时, 该命令对字符长度设置了一定的限制。通常情况下, 这一限制大约为65536个字符。而在处理大规模拉取请求即大PR的审查内容的过程中, 如果同时包含差异对比原文等补充材料, 整体内容的大小很有可能突破上述设定的上限数值, 从而引发错误提示或者其他非预期行为表现。解法hermes chat -q ... /tmp/review.mdif [ $(wc -c /tmp/review.md) -gt 60000 ]; thenhead -c 60000 /tmp/review.md | gh pr review $PR_NUM --comment --body-file -echo 由于审查内容较长已分段展示 | gh pr comment $PR_NUM --body-file -elsegh pr review $PR_NUM --comment --body $(cat /tmp/review.md)fi我们得到的教训是, 在进行 PR 审查之前, 必须先用“wc -l /tmp/pr.diff”这个命令来判断一下差异文件的大小。如果代码行数超过了 1500 行, 那就建议把它拆分成多个小一些的 PR 来分别进行审查, 这种方式也是符合最佳实践要求的。在环境构建这个环节, 大家遭遇了第三个坑, 也就是出现了安装操作超时的问题。当时的具体表现情况是这样的, 也就是去执行那个使用管道命令将远程代码直接传递给bash进行安装的步骤, 然后在运行过程中, 大约过去了十分钟之后, 程序因为等待时间过长而发生了超时错误, 最终导致整个安装流程失败。原因是, 在那个镜像里面, 安装脚本会去安装那个依赖, 第一次运行的时候需要下载差不多 200MB 的包, 如果仓库那边的那个操作触发得特别频繁, 比方说每次推送的时候都会触发, 那么每一次都得重新进行下载, 这样耗时的情况就会叠加起来。关于这个问题的具体解决方式为, 采取使用缓存机制的方式来进行。- name: Cache Hermesuses: actions/cachev4with:path: ~/.hermeskey: hermes-${{ runner.os }}-${{ hashFiles(.github/workflows/hermes-review.yml) }}- name: Install Hermes (if not cached)run: |if [ !-f ~/.hermes/hermes-agent/venv/bin/python3 ]; thencurl -fsSL | bashfi在第一次完成下载动作之后, 如果后续再触发相关环节, 系统就会直接从缓存进行数据恢复。这样的话, 原来需要耗费 5 分钟的安装时间就被大幅缩短至只需 5 秒。总结这一套组合式的措施, 把我写代码的情况从过去每晚必须忍受长达两小时的折磨, 转变成了现在只要收到通知就去简单看一眼的程度, 其中的核心关键点主要就是三个事项:是触发器, PR 一提自动拉起审查, 是大脑去分析代码发现问题的能力以及给出建议, gh CLI 则是嘴巴用来把结果自动贴到 PR 评论区的。这套方案里, 你不需要花一分钱成本, 唯一需要付出的投入时间, 仅仅就是写好那个文件所花费的十分钟左右。因为个人的项目使用免费额度, 那是完全足够并且非常充足的。如果是团队来进行使用的情况下, 可以在上面悬挂一个价格相对较为低廉的模型, 比如Flash 8B这个模型, 它每百万个token的价格仅为0.0375美元这样的话, 每个月总体的花费成本还远远不到买一杯咖啡的钱。可能会有人提出这个问题, 说人工智能审查这种东西可不可靠呢?根据我个人的实际经验来看, 它的定位并不是要去完全代替人工进行审查这种工作, 相反, 它的作用是把人工审查的这种方式从一个需要逐行去阅读和理解代码的辛苦过程, 改变成为去审核由人工智能提供的这些建议的过程, 让人类的工作精力能够更多地集中放在判断以及做出决策这样一些更具创造性的事情上面, 而那些重复性的检查工作的负担则是交给了相关的工具或者系统来完成, 这也就是我们所说的合理的分工方式。过去那段时间, 我们常常需要花费长达两个钟头的功夫来寻找那些比较低级的错误内容, 而当今这个阶段, 我们只需要用短短两分钟的时间, 便可以把人工智能所提供的审查建议全部浏览完毕一遍, 从而能够把更多宝贵的时间精力留给那些真正值得我们深入思考的问题之上。这不是所谓的AI取代工程师的情况, 而是工程师们终于不用再把自己宝贵的时间消耗在一些重复性的工作上了。