跳到正文
Tessl Blog· Simon Maple·· 8 天前AI 评分36

智能体代码审查的解剖:Tessl Code Review 如何重构 PR 审查流程

Anatomy of an Agentic Code Review

AI 导读

Tessl 提出智能体代码审查的六部分结构:标准、视角、发现、裁决、逐条回应与记忆,其中标准以 skills、docs 和 rules 形式存放在仓库中作为上下文。

正文 · AI 翻译

多年来,从提交工单到合并 PR 的流程对大多数团队来说几乎一成不变。一旦工单进入 Linear 或 Jira,开发者就会坐到他们钟爱的机械键盘前,编写代码并提交一个 pull request。一位同事会进行审查(花费的时间与代码行数成反比),经过一两轮之后,它就会被合并。审查从来都不快,但它能跟上节奏,因为人类以人类的速度编写代码。

编码智能体突然改变了这一现实。它们能在几分钟内将工单变成 pull request,因此团队最终会收到数量更多、速度更快的 PR。审查并没有随之加速,而且众所周知,它成了瓶颈。

这篇文章探讨了为 PR 提交速率不断加快的世界而构建的代码审查新世界。以下是我们将要逐步讲解的完整智能体工作流。在本文的剩余部分,我们将从它所取代的传统审查开始,一部分一部分地构建这张图。

anatomy-overview.png

TL;DR

  • 典型的代码审查往往是自动化测试加上人工审查,但当我们引入智能体时,我们可以增加多个新的角度,例如验证编码标准、审查角色视角、反馈并记住过去的错误等等。
  • 除了测试之外,大多数人工审查通常只会添加一些表述松散的发现,而且因人而异。在智能体产生的 PR 数量下,审查会变慢、因审查者而异,并且会忘记它们学到的东西。
  • 如果你将知识和实践存储在上下文中,代码审查就是迈向上下文驱动的软件工厂的绝佳垫脚石。

开始使用:有兴趣自己动手做吗?你可以做到,而且入门是免费的,只需在你的代码仓库上安装 Tessl Code Review,就能在下一个 pull request 上看到第一个循环运行起来。设置 Tessl Code Review。

你正在取代的审查

让我们先回顾一下传统代码审查流程。开发者提交一个 PR,由人工查看、提供反馈和指导,然后开始一个手动循环,直到 PR 的更改被批准并合并。

Screenshot 2026-10-07 at 11.18.29.png

对这个 pull request 的典型人工审查可能如下:

“看了一下,整体看起来不错。你有没有考虑过如果用户 ID 缺失会发生什么?另外 utils 里已经有一个重试辅助函数,可能值得复用。等这些问题解决后我很乐意批准。”

这条评论本身没有问题。它很友好,观点也合理。但它没有说明哪个问题会阻碍合并,哪个只是建议。它没有指向任何代码行。它把正确性问题与代码复用的提醒混在一起。而且它漏掉了这次更改中最严重的问题。把这一点乘以智能体产生的数量,就没有任何人工审查者能跟得上,更不用说保持一致了。当然,审查的质量会因团队中哪位人工审查者有空接手而有所不同。

智能体代码审查的剖析

Screenshot 2026-10-07 at 14.47.39.png
  1. 一个标准(预先定义):编码标准、最佳实践、代码库中良好代码应该是什么样,以及设计选择。
  2. 视角(预先定义):专家意见的角色或类型应该多样化,并且从多个角度审视变更审查,每个角度只问一个具体问题。
  3. 发现(过程中发现):每个被定位、加权并解释的问题。
  4. 一个裁决(过程中决定):一个更详细的、由发现得出的决定。
  5. 对每一条发现的回应(事后构建):一个可执行的下一步:修复、反驳或拒绝,下一轮会尊重这个答复。
  6. 一份记忆(事后更新):本次审查学到了什么,并反馈到标准中,使下一次变更审查尊重过去的决定和学到的经验。

我们之前分享的那条人工评论只有几条表述松散的意见,别的就没什么了。让我们拿同一个 pull request,但这次我们依次过一遍这几个部分。

1. 一个标准

anatomy-1-standard.png

每次审查都是拿变更去对照某个东西。在上面那条评论里,那个东西就是审查者当天恰好知道并在意的内容。换一个队友就会对照另一套标准,而作者事先无从得知其中任何一套。

好的审查始于一套在 pull request 打开之前就已写下且可见的标准。作者可以对照它检查自己的工作,审查者一致地应用它,团队可以争论标准本身,而不是争论一条条评论。在 Tessl Code Review 中,这套标准作为上下文存放在你的仓库里:你的团队拥有并像对待其他代码一样修改的技能、文档和规则。

2. 专门的视角

anatomy-2-perspectives.png

一个审查者从头到尾读一遍 diff,要同时兼顾正确性、安全性、性能和可读性。什么吸引了他的注意,什么就得到关注。在这里,那是一个缺失的用户 ID 和一个重复的辅助函数,而最严重的问题却没被注意到。

好的审查把工作拆分开来,给每个视角一个要回答的窄问题:

  • 正确性与数据完整性:这个变更做了它该做的事吗,它触及的数据在变更后是否完好无损?
  • 安全与隐私:攻击者能利用这个变更做什么,它暴露了人们的哪些信息?
  • 规模与韧性:随着负载增长,它的成本是多少,当它依赖的某个东西失败时会发生什么?
  • 可维护性与代码质量:下一个人或智能体能否理解它并安全地修改它?

在 Tessl Code Review 中,每个视角都是一个镜头:一个聚焦的审查视角,打包成一项技能,把你的标准应用到某一个领域。这四个是内置镜头,它们并行运行。当只被问及攻击者能做什么时,安全视角发现了通才视角漏掉的问题:该端点向任何已登录用户返回电子邮件地址。当然,如果你愿意,你可以在此基础上添加,并定义适合你的组织和项目的自己的镜头。

3. 发现

anatomy-3-findings.png

这是同一个 pull request,按视角逐一审查:

需要修改

  • 必需 · 安全与隐私:向任何已登录用户返回电子邮件地址。src/api/users.ts:48
  • 建议 · 可维护性与代码质量:重建了现有的 retry() 辅助函数。
  • 建议 · 规模与韧性:支付 API 调用没有超时设置。

每条发现都有相同的结构:

  • 它来自哪里。提出它的视角,这样你在读之前就知道这是哪类问题。
  • 它有多重要。必需还是建议,由审查者决定,而不是留给作者去猜。
  • 它在哪里。文件和行号,这样没人需要去琢磨审查者的意思。
  • 为什么这是个问题。直白地说明后果。“向任何已登录用户返回电子邮件地址”告诉你的是风险,而不只是某些地方看起来不对劲。

把它和“大体上看起来不错”对比一下。这里的每一条发现都可以直接采取行动,无需再跟进沟通。

4. 一个结论

anatomy-4-verdict.png

一份发现清单并不等于一个决定。好的评审以一个决定收尾:批准,或请求修改。有些发现可能是必须处理的,有些可能是建议性的,而在考虑所提出的安全问题时,往往更容易理解这一点。该结论应基于分诊结果得出。这两条建议性发现值得修复,但仅凭它们本身并不会阻止合并。

什么会阻止合并,其标准也应当明确。在 Tessl Code Review 中,团队设定了可以请求修改的最低严重级别,因此“阻止合并”在每个拉取请求上含义一致。人工评审者仍然是流程的一部分,但他们从一个结构化的结论出发,而不是面对一片空白的 diff,把时间花在需要判断的地方,而不是发现显而易见的问题。

5. 对每条发现的回应

anatomy-5-response.png

评审是一场对话,而不是单向广播。好的评审期望对每条发现都有回应:修复它、反驳它,或说明理由后拒绝它。拒绝和修复一样有用。“这是有意为之,调用方已经应用了超时”是下一次评审应当掌握的知识。

下一轮评审应当尊重这些回应。当 Tessl Code Review 重新评审一个拉取请求时,已修复的发现会被标记为已解决,仍然存在的发现不会被再次发布,而修复后又重新出现的发现会被重新打开。作者永远不必再费力地翻看同一条评论两次。

超越代码评审:Tessl 验证器与安全发现

在我们进入最后一部分——记忆——之前,值得看看好的评审根本不应该去捕捉什么。有两类问题最好在评审的两侧处理。

Tessl 验证器,在拉取请求之前

anatomy-7-verifiers.png

验证器可以根据所做变更的类型被确定性地触发。例如,如果你更新了一些前端代码,你可能有一个前端验证器,它随后使用 LLM 来检查并对已提交的代码强制执行特定规则。验证器与你的其余上下文一起存放在你的仓库中,既可以在拉取请求打开之前运行,也可以在 CI 中的常规评审期间运行。如果验证器在代码编写过程中失败,智能体会在 CI 的评审阶段之前修复问题。验证器以二元方式通过或失败,如下所示:

✓ imports-use-path-aliases
✓ no-secrets-in-source
✓ migrations-are-reversible
✗ api-routes-require-auth    src/api/users.ts:48

它们之所以必要,是因为判断代价高昂。每个透镜提出的每条发现都要耗费一次评审、一次返回作者的往返以及又一次评审。验证器在几秒内运行,几乎不花什么成本,并且每次都给出相同的答案。凡是验证器能捕捉到的,评审就不必去捕捉,这让各个透镜可以自由地把注意力花在真正需要推理的问题上。

验证器不是写一次就放着不管的,而评审反馈正是它们演进的方式。当一个透镜不断提出同一条发现,而它背后的规则被证明是二元的时,那就是一个等待被编写的新验证器。上面的失败正是如此:安全透镜在 src/api/users.ts:48 处标记的认证缺口,现在在拉取请求存在之前就被捕捉到了。反馈也可以反向起作用。如果某个验证器在你的团队认为没问题的代码上触发,那么这次拒绝就是一个信号,提示收紧或停用该检查。无论哪种方式,验证器的集合都会随着每一次评审而增长和锐化,这就是我们接下来要谈到的记忆。

安全发现,与审查发现并列

anatomy-8-security.png

你的安全扫描器,无论是 Snyk、Semgrep 还是 Dependabot,都会检查变更中是否存在已知漏洞。其发现会以与审查发现相同的方式呈现,因此可以直接发送回编码代理进行修复,然后再次扫描,而不是留在仪表板中等待有人分类处理。

  • 高 · 依赖项: lodash 4.17.15 存在已知的原型污染问题。升级到 4.17.21。package.json
  • 中 · 代码: 搜索查询通过字符串拼接构建。请使用参数化查询。src/db/search.ts:22

你仓库中的修复策略决定哪些内容会被自动修复。验证器和安全发现共同将一整类问题从审查的负担中移除,从而让审查可以自由地将注意力集中在真正需要判断的问题上。

6. 记忆

anatomy-6-memory-v2.png

这可能是长期流程中最重要的部分。我们不希望代理一遍又一遍地犯同样的错误,这极其令人沮丧,并让我们对其能力失去信任。大多数审查在合并时结束,它们学到的一切也随之而去。下一个拉取请求犯同样的错误,同样的审查评论再次出现。

好的审查会留下一些东西。看看我们 PR 中的三个发现,以及每一个应该变成什么:

  • 认证缺口。 如果审查不断发现未认证的路由,那就不再是判断问题了。它变成一个验证器,因此问题在拉取请求甚至还不存在之前就被捕获。
  • 重建的重试辅助函数。 如果代理不断重建它,说明它们不知道它存在。这应该属于代理在编写代码之前读取的上下文。
  • 缺失的超时。 如果这是一项标准,那么规模和弹性视角应该每次都明确检查它。

每个发现不再是一条需要有人重复的评论,而是变成源头处的修复。

这在 Tessl 对我们来说已经变得非常强大。我们构建了一个维护代理,它会定期读取历史拉取请求、发现和日志,然后以拉取请求的形式提出变更,由我们的团队审查。

一句话概括其结构

虽然审查速度是代理式代码审查改善我们工作流程的主要方式,但还有更多重要方面能为我们带来更可靠、更受信任的流程。使用反映我们最佳实践和编码标准的上下文,供审查代理使用,再加上验证器和视角,能为我们带来最想要的审查结果。此外,学习阶段让我们能够借助人工指导和评论,随着每个 PR 改进上下文,避免在每个 PR 中一次又一次地犯同样的错误。

这就是为什么代码审查是开始构建上下文的正确起点。当标准存在于你的仓库中,并且每个发现都反馈到其中时,审查就变成了通往上下文驱动软件工厂的垫脚石,在那里,每一次变更都从上一次变更教给你的东西开始。

开始使用: 安装 Tessl Code Review,然后从你现有的代码和过去的拉取请求生成你的第一个视角。设置 Tessl Code Review

来源:Tessl Blog · tessl.io