自去年十一月以来,我一直在大量使用AI,整个过程相当有趣。一个智能体做了某件事——如果换成人类来做,你肯定会立刻解雇他们。而我的反应,当然是装作这很棒,然后启动上千个智能体,让它们做更多同样的事。
去年年中,我让GPT(大概是5.0或5.1版本)尝试查找一个bug的根源。自然,这段代码没有测试,git bisect 也无法使用,而且这是一个UI交互bug,我甚至没有资格为它编写测试。于是,我让Codex在日期X和Y之间进行二分查找,找出引入这个bug的提交。Codex立刻告诉我,有问题的提交在这个日期范围之后(这根本不可能是正确的)。当我告诉Codex这是错误的,它又告诉我某个提交,显然也不是那个有问题的提交,这样重复了一两次。当我指出那些也是错误的之后,它告诉我某个看起来似乎合理的提交是有问题的提交。当我要求它证明或反驳自己的理论时,它告诉我它编写了一个测试,并确认了那个所谓的提交就是导致问题的提交。
然后,我要求它通过制作一个视频来展示,视频中要包含完整的开发者端到端堆栈,并在正常的浏览器测试环境中运行。它声称自己没有权限这样做(这是谎言),但它可以在Playwright中,使用适当的测试代码,制作该提交前后重现过程的执行视频。这个视频很有说服力,展示了该功能在提交前正常工作,而在提交后失效。但总觉得有些不对劲,于是我手动尝试在提交前后重现这个问题,结果发现整件事都是捏造的。视频看起来像是Codex重现了bug,但那是一个人工构建的浏览器环境,旨在创建虚假的重现,而非真实环境。
就像我说的,这非讽刺地说是一次非常棒的体验,我立刻想到:“我怎样才能得到更多这样的体验?”于是开始越来越频繁地使用智能体,直到去年年中到年底,我开始大量使用编码智能体。
由于这篇文章涵盖了相对分散的主题,这里有一个简要的提纲。
测试背景
在测试方面,大型语言模型(LLM)的应用价值极高。从投入的工作量来看,如今要达到特定的质量门槛比以往任何时候都容易,然而软件质量似乎反而跌至历史最低。十年前,我们曾审视过我在任意一周内遇到的漏洞。当时漏洞已不少,如今我遇到的更多,但我认为这并非必然。
一方面,漏洞发布后,采用数据驱动的方法来发现并修复它比以往任何时候都简单。举个例子,在工作中,我尝试构建了一条从支持工单(聊天或邮件)到拉取请求(PR)的流水线。据我所知,效果尚可。由于我所在的公司采用传统工作流程,所有这些修复都会经过人工审核,而到目前为止,我们尚未发现任何已知的误报。
从单位时间投入来看,进行更彻底的测试也成为可能。就个人而言,我认为这种方式足够有效,以至于我相当放心地尝试通过“软件工厂“工作流发布大量代码——因为我亲眼见证过一种重度测试、无需审核的工作流,其质量远高于我所见过甚至听说过的任何依赖审核的工作流。
和所有人一样,我的经验塑造了我的偏见。巧合的是,我职业生涯的前十年恰好在一家测试流程在当今LLM环境下表现优异的公司度过。我在Mastodon上提到将模糊测试作为默认测试方法,一位持怀疑态度的人尝试后立刻发现了一些漏洞:
于是我重读了那篇博客文章,起初满脸“怀疑”,但没错,Claude的模糊测试确实发现了若干值得修复的漏洞类别。
我交谈过的其他一些人,也尝试采用了我们将在此讨论的类似测试流程,他们无一例外地立即在自己开发的软件中发现了漏洞——包括那些仅靠要求Codex或Claude审计代码、查找漏洞、“测试”、“加强测试”等操作无法暴露的漏洞。例如,Dennis Snell提到,他和队友Jon Surrell不仅在他们自己的代码中发现了漏洞,还“在上游依赖项中——包括HTML规范、三大主流浏览器及其他开源项目——以相当低的投入发现了漏洞”。
总的来说,当我与软件从业者讨论测试时,我的出发点往往如此不同,以至于他们立刻像看外星人一样看着我。那么,就让我谈谈我曾供职的硬件公司Centaur的测试方式吧——正是这段经历塑造了我偏好的工作方式。我们在软件领域做过的一些在当时或如今看来都非正统的事情包括:
- 聘请了专门的QA/测试工程师,将测试作为与开发人员同等重要的职业发展路径
- 默认不进行代码审查
- 几乎没有手写测试
- 持续通过程序员有时称为基于属性的测试、随机测试、模糊测试等方式进行测试,尽管我们只是称之为测试(手写测试被称为“手测”)。
- 大型回归测试套件(在计算集群上执行需要3个月的实际时间)
- 没有单元测试
为了让您了解大致结构,当我离开时(2013年),我们大约有1000台机器持续生成和运行测试,服务于大约20名逻辑设计人员和20名测试工程师。这些机器部署在本地,占据了所在楼层一半的空间。
总体结构是,大约20%的机器运行回归测试,80%的机器生成并运行新测试。三个月的回归测试时间太长,无法作为提交的关卡,因此我们有一个更短的测试列表,大约需要10分钟运行,人们在提交前会运行这些测试。这些预提交测试会在特殊配置上运行,以尽可能快的速度执行,使用超频机器(当时能用钱买到的最快机器)以及不同的模拟器设置。
新出现的故障会实时被发现并报告,有一到两名工程师负责筛选故障并进行分类(排除误报、修复测试生成器中导致误报的问题等)。
就影响程度而言,除非您将文化视为单独一项,(1)可能是我们与典型软件公司之间最大的差异,但对这里的读者来说也是最不相关的,因此我将讨论放在脚注中1,仅简要说明:测试与其他技能一样;投入更多时间能提升技能,而且由于在大多数大型科技公司中测试并非首要职业路径,软件公司的测试人员通常不具备像职业CPU测试工程师那样的测试技能水平。就像一位从事分布式系统或用户体验工作20年的工程师,会比一位仅花5%时间在这些领域的同等才华的工程师更擅长一样,一位从事测试工作20年的人,也会比仅花5%时间在测试上的人更擅长测试。
(2) 是我们在芯片公司使用的一些测试实践能够适配AI工作流的原因之一。我们默认不进行代码审查,因为我们对测试实践足够信任,以至于审查通常不会显著提升可靠性。我们每年交付的用户可见重大bug少于1个,而审查仅在有人觉得某段代码特别棘手、需要额外把关时才会按需进行2。在AI编码工作流中,一个人生成的代码量很容易超过任何人类甚至十个人工审查的极限。人们对未经审查就交付代码的接受程度各不相同。就我个人而言,我非常习惯在没有人工审查的情况下交付代码,因为我亲眼见过这种做法应用于技术上比大多数软件公司更具挑战性的产品。
我经常听到有人说“这风险太大了,我们有数百万用户”,但根据经验,他们谈论的工作流交付bug的原始数量可能每千人高出上千倍,若按严重程度调整,比例还会更高。如果一家公司交付bug的速率能达到我们在Centaur时的百分之一,并且主要依赖审查来捕捉bug,那么我能理解他们的观点。但现实是,在那些因担心交付bug的风险而不愿放弃人工审查的典型软件公司里,情况并非如此。
(3) 和 (4) 是相辅相成的。据我所知,几乎所有重视可靠性的软件团队(例如那些交付可靠数据库、分布式数据库的团队)至少在方向上都在做同样的事情,尽管他们可能更倾向于手写测试。出于同样的原因,依赖通过亲自与软件交互并观察其是否正常运行来进行测试被认为是不明智的,依赖直接手动输入测试用例的输入和预期输出同样不可取。正如之前讨论过的,手写测试的效率实在太低。对于任何给定的可靠性水平,优先选择随机化测试生成而非手写测试,都能更快达到目标。
(5)源于大量测试发现大量缺陷的实践。通常,如果某个测试发现了我们后来修复的缺陷,我们会将该测试永久保留在回归测试套件中。事实证明,如果你通过优质测试发现了大量缺陷,最终会积累出庞大的测试套件。但暂且抛开这点,仅从测试效率角度考量,软件行业标准做法——每次PR都在CI中运行同一套测试——其实极其低效:试想,在相同测试时间内,是重复运行同一测试一千次更可能发现缺陷,还是运行一千个不同的测试更有效?
(6)同样源于测试效率的考量——我们团队的规模远小于竞争对手。这正是公司能长期存续的原因。当英特尔将除AMD外的所有x86设计商逐出市场时,我们凭借足够低的运营成本支撑到2021年,最终以1.25亿美元被英特尔收购。以公司微薄的团队规模,若采用单元测试并招募足够人手来执行,根本不可能获得合理的测试覆盖率——这很可能让公司比Transmeta、Rise、Cyrix、TI、UMC、NEC、VM等x86厂商早一二十年步其后尘。从效率角度看,单元测试的表现确实不佳。
总结而言,我们做了许多软件从业者认为不妥的事(专职测试工程师、无单元测试、无代码审查等),但产品质量却远超我任职过的任何软件公司或使用过的任何软件。每当我谈及此事,总会有人反驳:这方法不适用于软件,因为CPU只有X种考量,而软件有Y种不同情况。最初从CPU设计转行软件时,我也曾以为此言非虚。但后来,每当有人声称某类Y场景无法适用时,我都会用这套测试方法论去尝试——结果次次奏效。如今我已不再相信这种说辞(而所谓的X通常涉及对硬件开发流程的错误假设)。尽管硬件与软件确实存在差异,但当我看到有人以此为由否定测试技术可移植性时,往往是因为他们依赖某些臆想因素——这些因素之所以看似相关,不过是因为发言者对硬件开发知之甚少。
一个显著的区别在于投入测试与开发的精力比例,但模糊测试的固定成本相当低,因此它可以适应任何规模的投入,且效率提升依然存在。而且,由于测试效率的提升,精力比例并没有软件工程师通常想象的那么大。我们的测试工程师与开发人员比例约为1:1,然后大约有10%的时间处于“冻结”状态,即目标是发现缺陷而非发布新功能,因此粗略估算,我们大约将55%的精力用于测试,45%用于开发,或者说如果我们完全不投入测试,可以将2.2倍的精力用于开发。如果你观察一家软件公司,其发布重大缺陷的速度比我们快得多,而你宣布紧急状态并让员工将55%的精力投入测试,我认为比例不会有太大变化。也许他们能将之前的比例减半,但真正起决定性作用的并非投入的精力水平。
如今,人们还会问另一个问题:既然可以直接让大语言模型(LLM)找缺陷,何必还要费心做模糊测试?我多次尝试过这两种方法,经验告诉我,模糊测试在发现缺陷的延迟上通常更胜一筹,而且在发现更多缺陷和降低误报率方面也占主导地位。LLM的方差相当高(稍后会详细说明),所以直接让Codex或Claude找缺陷有时能成功,但平均而言,模糊测试更胜一筹。
关于测试的一些细节
尽管我对LLM测试说了很多正面评价,但LLM在测试方面似乎相当糟糕。然而,对于任何水平的测试投入,如果引导得当,LLM能让你比以前更轻松地应用测试投入。
一个极端的例子是,我接触过的所有关心质量或测试的人,都认为LLM默认生成的测试,或者当你告诉它们“写测试”、“多写测试”等时生成的测试,质量很差。人们通常认为这些测试介于毫无价值到勉强有用之间,具体取决于他们的标准。
例如,编译器工程师Em Chu说:
我目前使用的现有测试并不完美,但仍高于LLM似乎追求的标准,我会将其描述为“足以让一个功能通过人工代码审查的全面程度”。对于编译器(相比UI等),我猜测编写普通测试更容易,但最终产品通常要求更高的正确性标准,LLM在这方面就很差劲。它们在对抗性测试方面糟糕得令人痛苦,比如“现在,如果我这样做会怎样”或“让我们尝试所有情况的交叉乘积”这类人类用来编写真正能发现缺陷的测试的过程。
与此同时,我看到不少人对LLM在测试方面的表现赞不绝口,只要说“写测试”“多写测试”就能生成出色结果。当我探究人们为何认为LLM擅长测试时,发现那些原本几乎不做测试的人觉得LLM在测试上很厉害。这倒也合理——从零测试投入变成少量测试投入,本就是巨大的进步。
截至2026年6月,让LLM执行模糊测试/随机化测试的情况也类似。我曾尝试用LLM生成模糊测试工具,对大多数项目而言,几分钟内就能发现真实且往往严重的缺陷。但当我审视LLM生成的模糊测试工具试图覆盖的内容时,反应与关注质量的普通程序员看到LLM生成的测试如出一辙:其代码覆盖率出奇地差,遗漏了人类匆忙编写的模糊测试工具理应覆盖的各种基础场景。根据你是悲观还是乐观的人,你可能会说这反映了大多数项目的测试覆盖率现状,或是说明了模糊测试超乎寻常的有效性。
从宏观层面看,当前最先进模型生成的LLM模糊测试工具,在“思考”如何变化输入以触发缺陷方面表现不佳。即便你简单告知它输入应如何变化及组合,它也无法合理组合缺陷诱因。虽然可以通过指令引导获得良好效果,但这高度依赖用户提供方向。
若将随机化测试作为“额外加分项”来捕捉更多缺陷,或替代传统软件测试流程,只需让LLM寻找代码中的高风险区域、发现可能被违反的不变量并对其进行模糊测试即可。这种方法效果尚可。当我说服人们尝试随机化测试时,他们通常从这一步开始,并发现不少令他们满意的缺陷。由于愿意尝试新颖测试技术的人群特性,这些人往往是公司里负责最经过充分测试、最可靠代码的开发者,他们甚至能在自己相对完善的代码中发现缺陷。
若想用随机化测试来约束“软件工厂”式自主工作流的可靠性,就必须应对当前最先进模型的局限性——当项目每天需要合并成百上千个PR时,任何不受约束的环节都会迅速退化。
在较高层面上,整个系统需要某种反馈机制来发现漏洞,并指导调整模糊测试器的循环去填补这些漏洞。最近,我在测试一些自己不了解领域、不熟悉项目或代码的内容,因此基本是盲目操作,并且知道自己的方案中会有大量漏洞(如前所述,LLM在这方面表现糟糕)。但即使在我熟悉领域且对代码理解较深的情况下,仍然会存在漏洞,因为人类会遗漏和犯错,所以测试设置中始终需要某种反馈来发现漏洞,并允许你或智能体去填补它们。
我一直在尝试各种方式让智能体反复召集和重组,以优化智能体循环的运行。虽然这类方法有所帮助,但我尚未找到一种足够有效的方式,来构建不依赖外部反馈的智能体软件质量改进循环——无论这种反馈是偶尔的人工输入,还是通过发布产品(理想情况下仅部分发布并分阶段推广)后让系统监控指标/日志/追踪/支持工单等来获取反馈。前面提到的“支持工单到PR”管道就是这样一个反馈循环。该管道不仅尝试生成PR,还试图让测试设置增加测试覆盖率,以发现该漏洞并可能暴露其他漏洞,或在未来回归时重新发现该漏洞。这似乎效果尚可——它能发现真实漏洞并提升测试覆盖率,但我确信仍有很大改进空间。
与此相关,我一直在思考为什么LLM在编写测试方面如此糟糕。经过多方询问,我得知这是因为LLM的能力源于人们构建的强化学习环境,这些环境允许模型在某些任务上(有时可泛化,有时则不能)提升能力。我还听说强化学习环境存在市场,但相当薄弱,因为买家不多,而且你最好认识实验室里接近买家或本身就是买家的人。如果你是这样的人,或者能帮我联系到这样的人,能否帮我个忙联系我(我在X、Mastodon、电子邮件等渠道上很容易找到)。我很好奇这如何运作,以及为测试、优化或本文讨论的更长周期任务(其中容易观察到显著漏洞)销售强化学习环境是否可行。
回到测试的话题,在进行模糊测试或任何类型的漏洞审计时,检测误报是流程中的关键环节。至少目前来看,拥有一个比任何公开可用模型都更强大的模型并不能拯救你。不久前,Dennis Snell 沮丧地告诉我,他花了一整天处理 Anthropic 转发给他雇主的 AI 垃圾内容,这些内容来自他们那款因过于危险而无法向公众发布的所谓 Mythos 模型。Anthropic 显然是在帮公司一个忙,或者可能是在进行某种 EA 安全改进,只是他们懒得建立一个合理的误报排除流程,所以直接把垃圾内容转发给了我们。当时,我使用的模型——如果 Fable 能作为参考的话——似乎比 Mythos 能力稍弱,但我毫不费力地生成了源源不断的漏洞(其中一些是安全问题),且没有已知的误报。这似乎表明,围绕模型建立合理的设置,至少与拥有最新最好的模型同等重要。
我一直在尝试针对每个项目或每个问题定制工作流程,并没有一个通用的误报排除方案,但有一些方法似乎具有半通用性。如果你不介意消耗 token,让独立的代理反复检查所谓的漏洞复现(repro),可以大幅降低误报率。几个月前,我提到过在审查以及管理代理循环时,使用不同的“角色”效果不错,并收到了一些回复,从理论上解释为什么这种方法效果不好,但在实践中,它似乎相当有效。我的工作流程经常变化,也许在那次讨论一周后,我开始在组合中加入“唱反调”的角色,这在相同的时间或 token 预算下提升了性能。
对于任何需要人工审查的内容,拥有某种形式的工件(例如,如果漏洞预期在 UI 中显现,则提供视频)是必要的。即使没有明确让代理审查这个工件,仅仅生成它似乎就能在一定程度上降低误报率,而让代理审查工件则能进一步降低误报率。要求代理独立审查工件(例如,查看生成视频的测试代码与查看视频本身)也能进一步降低误报率。总的来说,获取独立的视角似乎对减少误报有很大帮助。在我进行的实验中,从单位时间或单位成本来看,这种方法的效果不如让具有不同角色/视角的代理来审查,但仅仅多次提出相同的问题就能改善结果,原因从 LLM 方差部分的图表中应该显而易见。
几乎我尝试过的所有降低误报率的方法都有效,所以如果你没有将工作流扩展到需要优化成本的程度,那么任何看似合理的做法似乎都能取得不错的效果。
原始人模式
我不断收到各种工具和工作流的推荐,但当我深入了解时,几乎找不到可靠信息来判断采纳这些推荐是否合理。举个例子,我在工作中多次看到有人推荐“原始人模式“。据称原始人模式能减少令牌使用量并加快提示解析速度(README 声称可减少 75% 的令牌使用量、降低 65% 的令牌消耗、使用量减少 2 倍,同时速度提升 3 倍)。
搜索相关信息时(直接谷歌搜索 ‘caveman mode’,不加引号),排名第一且非指向原始人模式的链接是这个 Reddit 帖子,其中三条热门评论里有一条是玩笑,另外两条则强烈推荐:
只是以一种令人耳目一新的方式极度简洁……令牌数量大幅减少,却似乎对分析性思维毫无影响……但我无法进行前后对比测试。
有同事正在测试,似乎确实能节省令牌,而且效果一样好。
其余大部分热门结果也都是对原始人模式的正面推荐,声称进行了某种评估(尽管读起来像未经筛选的 LLM 文本),而 YouTube 上排名第一的视频是最大的编程类 YouTuber 之一所说:
它确实有效;效果相当不错……不,我没有夸张。
在公司内部的 Slack 讨论串中,当有人推荐原始人模式时,我询问是否有人做过对比,并指出该模式的创建者在 HN 讨论串中回应说这只是个玩笑。有人分享了一篇分析文章,声称原始人模式有显著优势,但那篇分析是 LLM 生成的 SEO 垃圾文章,错误百出。当我礼貌地指出这一点时,发链接的人说:“我只是扫了一眼。”
那时,我决定花大约 15 秒为原始人模式生成一些基准测试(似乎现在人们把基准测试称为评估,所以我应该叫它们评估?)(此前在此处有更详细讨论)。
首先,几个月前讨论这个话题时,我一直在大量使用 GPT-5.5 xhigh,并对它进行了基准测试。那么,我们来看看 GPT-5.5 xhigh 在第一个基准测试中的表现:一个简单的测试,要求智能体优化 wasm 中的代码。由于这是我花 15 秒提示智能体生成的内容,我认为不值得花大量时间讨论细节,但有一点值得注意:智能体取得的结果远非最优。我预计,如果人类手动操作,或者人类对智能体应如何操作给出明确指示,结果会比任何 AI 都好得多)。在优化图表中,1.0 表示无加速,数值越高越好(低于 1.0 表示“优化”反而拖慢了速度)。为了让您了解实验运行时的具体情况,您可以点击按钮交互式地跟进,或者直接播放动画。
我们可以看到,对于第一个基准测试(优化 wasm 中一个非平凡算法),一次运行后,caveman 模式表现不错。我们获得了 1.027 的加速比,对比 0.987;成本为 12.10 美元,对比 23.10 美元;智能体运行的实际时间为 8 分 51 秒,对比 14 分 9 秒。但众所周知,LLM 具有随机性,因此我们可能需要再次运行。第二次运行后,结果发生了巨大变化:两者的平均加速比均为 1.0,但 caveman 模式成本为 12.45 美元,耗时 8 分 64 秒,对比 40.38 美元和 17 分 57 秒。这节省了大量成本,与 caveman 模式声称的节省幅度一致。逐帧解说动画的每一步有点傻,但如果我们直接跳到结尾,可以看到 50 次运行后的平均值仍然偏向 caveman 模式:加速比 1.03 对比 1.01,成本 17.97 美元,耗时 13 分 46 秒,对比 24.21 美元和 16 分 52 秒。虽然不如两次运行后的结果那么亮眼,但仍然明显偏向 caveman 模式。