在人类历史的大部分时间里,我们都是通过代码审查来评估代码质量的:由某个人阅读你写的内容,确认它是否简洁、周全、高效、易懂,并且测试充分。对于智能体来说,这种方式并不具备可扩展性;代码量实在太多,没人能逐行读完。因此,我们越来越多的质量检查,必须在智能体周围的 harness、环境和操作系统层面完成。
如今,软件质量取决于你为智能体设定的约束。

说到质量,智能体正在为你写代码。Sonar 为你提供可交付上线的质量门禁。我曾让一个编码智能体做出一个很炫的应用,然后又让另一个智能体去审查它——审了两次。两次审查结果还不一致,而重新跑一遍又给出第三种答案。你不能靠抛硬币来决定是否合并。Sonar 会对每次提交执行同一套完整检查:深入的跨文件分析、风险分布图,以及让每个人类和智能体都遵守同一标准的质量门禁。试试 Sonar。由 Sonar 赞助。
约束通过对智能体的提议施加测试和确定性规则,来定义系统被允许做什么。正是通过设定并维护这些约束,我们才能构建出可靠交付高质量生产软件的闭环,即使智能体每天会生成成百上千、甚至数百万次变更也是如此。

我们把这些约束称为质量门禁(quality gates),它们有很多种形式。包括传统的单元测试、属性测试和验收测试。也包括变异测试(mutation testing),即我们生成代码的变体,用同一套测试去跑,并确保没有人偷偷塞进我们漏掉的 bug。还包括围绕代码质量的各类指标,例如圈复杂度(cyclomatic complexity)和行长度,它们有助于保持可读性。约束在系统接受并应用哪些提案、将哪些内容作为代码变更落地时,也扮演着重要角色。当某个变更提案从运行智能体的解释器,流转到智能体控制器,再进入生产环境时,我们已经对它做了足够多的检查,因此有信心它是安全可发布的,而且其影响范围也完全在智能体可控范围之内。
智能体可以提出任何想法。你的约束决定这个提案是否足够安全、正确、范围明确且有价值,足以让你和团队交付上线。
这个模型提供了很多能力,但也遗漏了许多部分,而这些遗漏值得我们今天思考。一个问题是自主性;智能体也许能很好地执行意图,但在信息缺失或尝试做的事情存在歧义时,可能会失败。这既适用于任务本身,也适用于任务如何被 harness、环境和其他组件参数化。许多导致人类无法成功交付优秀代码的原因,也同样会出现在智能体身上:脆弱的环境经不起脚本化压力测试、非确定性的构建、缺失的权限,以及薄弱的测试。這促使我们构建更好的环境,让智能体获得可信反馈,允许低损失的失败模式,并更容易逐步走向成功。

我们追求的环境,应当让智能体能真正做事、获得可信反馈,并且即使失败也不会造成太大损失。
另一个重要问题是信任。我们不能不加甄别地把意图交给某个即便像现代智能体一样聪明而稳健的系统,而不去检查其正确性。我们可以从信任开始,但这种信任必须靠努力赢得。

有些约束在工作开始前就塑造了工作方式。另一些约束在智能体工作过程中提供反馈。还有一些则决定其输出是否能够跨越生产边界。
关于如何围绕一个系统建立验证结构,有很多种建模方式。
依我经验,最好为约束设置一套更广泛、但经过刻意选择的检查,而不是只依赖单元测试。其核心思想是:每种检查都有不同职责,范围可以从类型安全和性能,一直到后期的安全扫描。团队也可以自行定义约束,包括通过 ESLint 之类的 lint 工具执行的架构规则。很多工具内置了钩子,在出问题时可以拉入智能体,或者人类,来处理。
就目前而言,智能体输出是否有用,和其是否只是垃圾,很多时候仍然取决于运营这个闭环的团队能力。
AI 给我们带来了高产出的代码生成和速度提升,但这也意味着人类要审查每一次变更会越来越困难。你必须有意识地决定把他们的注意力放在哪里。如果你把一个人类检查放进一个本来以机器速度运转的系统里,就别惊讶它会影响生产力。人类注意力稀缺且宝贵,所以我们应该主动把它导向那些最需要判断力、最细致微妙的问题。只有当自动化约束的防护栏失效时,才应该拉入下游的人类参与。
未来的人类“代码审查”会非常不同
正确性是一个重要维度,但你可能还会关心其他方面,比如可维护性、性能、安全性、效率和可理解性。正如正确性可以拆解为多种信号,质量的其他部分也一样。我们设立了多少约束固然重要,但更重要的是这些约束是否足够严格,能否达到我们对质量和可投入生产的标准。
软件质量不是单一指标。可以把它看作一组信号,而这些信号对你和团队的重要程度各不相同。
背压(back-pressure)可以通过很多工具来实现:编译器拒绝无效代码、测试失败、安全策略阻止不良实践、CI 拒绝部署。理想情况下,它应当贯穿整个闭环,而不是仅仅在所有工作结束时做一次最终审查。
约束和背压让智能体在坏工作演变成问题之前就把它拦下来
如果因为变更量超过了工具的处理能力,导致我们无法应用约束,会发生什么?我们最终会建立一个队列,并依赖一个以人类速度运转的验证系统。为了扩展规模,我们希望尽可能把验证推到整个流程中,而不是等到最后一刻。如果我们能在自动化检查范围内完成扩展,就能提升整个交付系统的速度和吞吐量。如果验证闭环的容量不够,我们就需要做几件事中的一件。第一,可以扩展验证系统,创造更多容量来约束并回压新来的变更。第二,可以降低智能体生成新变更的速率,让验证赶上工作量。第三,可以降低质量门槛,让验证不至于像原来那样强力回压。从扩展性角度看,我们需要准备好做这些事。与此同时,我们也不应止步于意识到:在某些方向上,放松约束其实能让我们做得更多。也许我们可以通过提供成群的智能体开发者,或者自动化软件工厂,在无需等待我们逐个审查的情况下创建变更,从而提升智能体生成变更的速度。
在某些地方,我们也许希望给它们更多自由,同时在另一些地方保持更严格的约束。通过在我们最关心的地方提供更紧的约束,我们可以在不牺牲质量的前提下最大化吞吐量。围绕这些决策,有很多取舍。最显而易见的是,我们必须在不同质量维度之间做权衡。正如我们强调的,安全性非常重要,但我们也不得不在交付安全性和按时交付产品之间做权衡。在创新导向和质量导向之间,存在一个连续谱。某种程度上,我们必须决定自己想站在这个谱系的哪个位置。
我们希望把来自环境和系统的清晰反馈传回给智能体或团队,这样人们就能把精力集中在更主观的部分:品味、意图和架构。如果我们能帮助人类始终待在安全的约束范围内,就可以避免他们费劲去搞清楚到底哪里出了错。
软件质量不止是正确性。软件质量还意味着可维护性、良好的性能、安全性、效率,以及易于理解。所有帮助我们达到这些标准并保持生产流畅的约束,都会在交付流水线中形成背压。
我们需要有意识地决定在哪里施加强约束,哪里可以移除或放松约束。在这两个目标都能发挥作用的地方,施加强约束。如果它们对其中一个或两个目标都没帮助,就不要支持它们。要随时准备根据具体情况提高或降低标准。并且要记住,软件系统中不同位置的这些约束,正是让软件质量具备可执行性的原因。
我们应该在最能同时服务这两个目标的地方施加强约束,并考虑移除或放松那些对任何一个目标都帮不上忙的约束。我们也应当随时准备按需上调或下调质量门槛。实际上,正是软件系统中各个环节的这些约束,让质量真正“有牙齿”。在很多情况下,我们可以通过部署新工具,或者强化已有工具,来创造更多背压和更多约束。所有这些都可以用于对大多数变更请求施加回压。我们希望把这些能力贯穿整个流水线构建起来。我们不想等到流水线末端,等 CI 系统只是告诉我们:不修复问题就不能部署。我们想尽可能早地、通过每一条可能的路径使用这些信号。这个系统中的终极约束,是我们施加在自己身上的约束——我们要为构建和运行这个系统时所做出的决定与行动负责。但和其他所有约束一样,我们也需要审慎权衡:究竟要让多少自己的判断去约束、去形成背压、并充当最终检查。
质量,就在于我们施加在智能体周围的约束。所以,当你为自己的应用思考质量时,不妨拿这个问题定义出发,制定你自己的约束驱动方案。
Pangram 4 将本文评为 100% human written。提醒一下,如果你正在寻找搭建质量门禁的好起点,Sonar 提供了一个相当不错的解决方案。
