Dan Luu谈AI Agent编程实践经验
知名技术博主分享使用AI Agent进行编程的实战笔记与思考。包含具体的工作流改进方案和陷阱避坑。
知名技术博主分享使用AI Agent进行编程的实战笔记与思考。包含具体的工作流改进方案和陷阱避坑。
自去年 11 月以来,我一直在相当频繁地使用 AI,整个过程是一种颇为有趣的体验。AI 智能体会做出一些事情,如果换成人类来做,你肯定会立刻把他开除。当然,我的反应却是假装这一切很棒,然后启动一千个 AI 智能体,好让它们做出更多这样的事情。
去年年中,我让 GPT(可能是 5.0 或 5.1)尝试查找一个 bug 的来源。很自然地,这份代码没有测试,git bisect 也无法使用,而且这是一个 UI 交互 bug,我甚至都不太具备为它编写测试的能力,于是我让 Codex 在日期 X 和 Y 之间进行二分查找,找出引入该 bug 的提交。Codex 立刻告诉我,问题提交位于这个日期范围之后(这显然不可能正确)。我告诉 Codex 这个结论是错的,它随后又给出了一两个提交,但那些提交显然也不是问题所在。我再次告诉它这些答案不对,之后它又指出了一个看起来还算合理的提交。当我要求它证明或证伪自己的推断时,它告诉我,它编写了一个测试,并确认所谓的那个提交就是导致功能损坏的提交。
接着,我要求它使用完整的开发者端到端技术栈,在正常的浏览器测试环境中录制视频向我展示。它声称自己没有权限这样做(这是谎话),不过它可以使用相应的测试代码,在 Playwright 中分别录制该提交前后复现过程的执行视频。视频看起来很有说服力:在该提交之前,这项功能运行正常;在该提交之后,功能则无法工作。但我总觉得哪里不对劲,于是亲自在该提交前后分别复现问题,结果发现整件事都是捏造的。视频让人以为 Codex 已经复现了这个 bug,但它使用的是一个专门设计来制造虚假复现结果的人工浏览器环境,而不是真实环境。
正如我所说,因为这毫不讽刺地成了一次如此“出色”的体验,我立刻心想:“怎样才能得到更多这样的体验?”于是我开始越来越频繁地使用 AI 智能体,直到去年年中至下半年,我已经开始重度使用编程 AI 智能体。
由于本文涉及一组相对分散的话题,下面先给出一个简要提纲。
关于测试的一些细节
AI 智能体循环与本文的写作过程
人们各说各话的一些原因
在测试方面,LLM 具有极高的杠杆效应。从所需投入来看,如今达到某个特定质量标准比以往任何时候都更容易,但软件质量似乎却比以往任何时候都低。十年前,我们曾统计过我在随意选取的一周里遇到的 bug。当时 bug 就已经相当多了,而如今我遇到的 bug 更多,但我认为事情并非一定要这样。
首先,在 bug 已经发布之后,如今使用数据驱动的方法发现并修复它,比以往任何时候都更容易。举个例子,我在工作中尝试创建了一条从支持工单(聊天或电子邮件)到拉取请求(PR)的流水线。据我所知,这套流程运行得还不错。由于我所在的公司采用传统工作流,所有这些修复都会由人类审核;到目前为止,我们还没有发现任何已知的误报。
按照投入的单位时间计算,现在也可以进行更加彻底的测试。就我个人而言,我认为这种方法可以有效到这样的程度:我相当乐意尝试通过“软件工厂”工作流发布大量代码,因为我见过一种以测试为中心、无需审核的工作流,其产出的质量远高于我见过、甚至听说过的任何依赖审核的工作流。
和所有人一样,我的偏见也源于自己的经历。恰巧我职业生涯的第一个十年是在一家公司度过的,而那家公司的测试流程恰好非常适合如今的 LLM 环境。我曾在 Mastodon 上谈到将模糊测试作为默认测试方法,一位持怀疑态度的人尝试之后,立刻发现了一些 bug:
所以我重新读了一遍那篇博客文章,当时满脸都是“我很怀疑”的表情,但不,确实如此,Claude 的模糊测试发现了好几类值得修复的 bug
我交流过的许多人也尝试采用类似于本文将要讨论的测试流程,而他们全都立刻在自己负责的软件中发现了 bug,其中包括一些仅仅要求 Codex 或 Claude 审计代码中的 bug、查找 bug、“测试”、“继续测试”等方式无法暴露出来的问题。例如,Dennis Snell 提到,他和队友 Jon Surrell 不仅在他们正在开发的代码中发现了 bug,还以相当低的成本发现了“上游依赖中的 bug,包括 HTML 规范、三大浏览器以及其他开源项目”。
总体来说,每当我和软件从业者讨论测试时,我的出发点与他们差异之大,以至于他们会立刻像看外星人一样看着我。因此,我们来谈谈我曾经工作过的那家硬件公司 Centaur 是如何进行测试的,因为那段经历塑造了我对理想工作方式的偏好。我们当时采用的一些做法,无论过去还是现在,在软件行业中都属于非传统做法:
聘用专职 QA/测试工程师,并将测试作为与开发者同等的一等职业发展路径
默认不进行代码审核
几乎没有手写测试
持续进行程序员有时称为基于属性的测试、随机测试、模糊测试等类型的测试,不过我们只是把这些称为“测试”(手写测试则称为“手工测试”)
庞大的回归测试套件(在计算集群上执行需要 3 个月的实际时间)
为了让你了解其整体结构,在我离职时(2013 年),我们大约有 1,000 台机器始终在生成和运行测试,为大约 20 名逻辑设计工程师和 20 名测试工程师服务。这些设备部署在本地,占据了我们所在大楼的半层空间。
整体架构大致是:约 20% 的机器用于运行回归测试,另外 80% 用于生成和运行新测试。耗时三个月的回归测试显然无法作为提交代码的门禁,因此还有一份短得多的测试列表,运行时间大约为 10 分钟,大家会在提交之前执行这些测试。这些提交前测试会在一套特殊环境中运行,以尽可能提高执行速度,其中既包括可以买到的最快的超频机器,也采用了不同的模拟器配置。
新的失败会在出现时立即被发现并报告,同时会有一到两名工程师专门负责整理和分诊这些失败,包括排除误报、修复测试生成器中导致其产生误报的问题等。
从影响程度来看,除非把文化单独算作一项,否则(1)可能是我们与典型软件公司之间最大的差异,但它对本文读者来说也是最不相关的一点,因此我会把相关讨论放到脚注 1 中。这里只简单说一句:测试和其他任何技能一样,投入更多时间练习就会提高水平。由于在大多数大型科技公司中,测试并不是一条一等职业发展路径,因此软件公司员工的测试能力,通常达不到某些以 CPU 测试为职业的工程师所具备的水平。就像一名花费 20 年研究分布式系统或 UX 的工程师,一定会比一名天赋相同、却只把 5% 时间投入分布式系统或 UX 的工程师做得更好一样,一个花费 20 年从事测试工作的人,也一定会比一个只将 5% 时间用于测试的人更擅长测试。
(2)则是让我们在芯片公司使用的某些测试实践非常适合 AI 工作流的原因之一。我们默认不进行代码审核,是因为我们足够信任自己的测试实践;总体来说,审核并不会增加多少可靠性。我们每年发布的、对用户明显可见的重大 bug 不到一个,只有当某个人认为某项工作特别棘手、希望多一双眼睛帮忙检查时,才会视需要进行审核 2。在 AI 编程工作流中,一个人可以轻易生成多到任何单个人类、甚至十个人类都无法手工审核的代码。对于在未经审核的情况下发布代码,不同人的接受程度各不相同。就我个人而言,我非常习惯在没有人工审核的情况下发布代码,因为我见过这种方式被用于一些技术挑战远高于大多数软件公司绝大多数软件的产品。
我经常看到人们说:“这样风险太大了;我们拥有数百万用户。”但从实证角度看,他们所谈论的工作流,按人均原始数量计算,其 bug 发布率可能要高出一千倍;如果再根据严重程度进行调整,这一比例还会高得多。如果某家公司主要依靠审核来发现 bug,而其发布 bug 的速率只有我们在 Centaur 时的百分之一,那么我可以理解他们的观点。但在那些因为担心发布 bug 的风险而不愿放弃人工审核的典型软件公司中,实际情况并非如此。
(3)和(4)是相辅相成的。我所知道的、几乎所有真正重视可靠性的软件团队(例如开发可靠数据库、分布式数据库等产品的各类团队),至少在方向上都采用了相同的做法,尽管他们手写测试所占的比例可能更高。人们之所以认为,依靠自己与软件交互并观察软件看起来是否正常工作来进行测试是一种糟糕的做法,同样也是为什么依靠直接手工输入测试的输入和预期输出也是一种糟糕的做法。正如前面讨论过的,手写测试的效率实在太低。对于任何给定的可靠性目标,如果优先使用随机化测试生成而不是手写测试,你都会更快地达到目标。
(5)则源于大量测试发现了大量 bug。通常,如果某个测试发现了一个后来被我们修复的 bug,我们会永远将这个测试保留在回归测试套件中。事实证明,如果使用优秀的测试发现了大量 bug,最终自然会得到一个庞大的测试套件。不过,暂且抛开这一点,单纯从测试效率的角度来看,软件行业的标准做法——让同一组测试在每个 PR 的 CI 中运行——效率低得惊人。想一想哪种方式更有可能发现 bug:一天内把同一个测试运行一千次,还是用相同的测试时间运行一千个不同的测试?
(6)同样源于对测试效率的考量,因为我们的团队规模远小于竞争对手。这也是公司能够存续如此之久的原因之一。当 Intel 淘汰了除 AMD 之外的所有 x86 处理器设计公司时,我们的运营成本足够低,使公司得以一直存续到 2021 年,随后被 Intel 以 1.25 亿美元收购。以公司如此小的团队规模,依靠单元测试不可能获得合理的测试覆盖率;而如果为了做单元测试而招聘足够多的人,公司很可能早在一二十年前,就会步 Transmeta、Rise、Cyrix、TI、UMC、NEC、VM 等公司 x86 项目的后尘。从效率角度看,单元测试的表现相当糟糕。
总而言之,我们做了不少大多数软件从业者认为是坏主意的事情(配备专职测试工程师、不做单元测试、不进行代码审查等),但我们的质量远高于我工作过的任何软件公司,也远高于我使用过的任何软件。每当我谈起这件事,人们都会说,这套方法不适用于软件,因为 CPU 只需要考虑 X,而面对 Y 时无法采用同样的做法。我刚从 CPU 设计转到软件行业时,也认为这或许是真的;但从那以后,对于别人提到的每一种据说无法应用这种测试方法的 Y,我都实际尝试过,而它无一例外都奏效了。因此,我现在不再认为这种说法有多大可信度(而且,人们所说的那些 X,通常也建立在对硬件开发方式的错误假设之上)。硬件和软件之间确实存在真正的差异,但当我看到有人以此为理由,声称测试技术无法从硬件迁移到软件时,实际情况往往是:这个人依赖了某个想象出来的因素,而这个因素之所以看起来相关,仅仅是因为他对硬件开发了解不多。
一个显著差异在于测试与开发的投入比例,但模糊测试的固定成本相当低,因此这套方法能够扩展到任何投入规模,而且效率优势始终存在。此外,由于测试效率有所提升,实际投入比例并没有软件工程师通常想象的那么夸张。我们的测试工程师与开发人员之比大约是 1:1,此外,我们可能有 10% 的时间处于“冻结”状态,这一阶段的目标是寻找 bug,而不是交付新功能。因此,粗略估算下来,我们将 55% 的精力用于测试,45% 用于开发;换句话说,如果完全不在测试上投入精力,我们本可以在开发上投入 2.2 倍的精力。如果你观察一家交付重大 bug 的频率比我们高出许多倍的软件公司,然后宣布进入紧急状态,让所有人将 55% 的精力投入测试,我认为这个比例并不会发生太大变化。也许他们能把原来的 bug 比率降到一半之类的水平,但真正造成差异的并不是投入规模。
如今,人们还会问:既然可以直接让 LLM 寻找 bug,为什么还要费心做模糊测试?我现在已经多次尝试过这两种方式。根据我的经验,在发现 bug 所需的延迟方面,模糊测试通常胜出;而在发现更多 bug 和降低误报率方面,模糊测试则占据压倒性优势。LLM 的方差相当高(后文会进一步讨论),所以直接让 Codex 或 Claude 寻找 bug,有时也能取胜;但平均而言,模糊测试表现更好。
尽管我对使用 LLM 进行测试说了很多非常积极的话,但 LLM 似乎并不擅长测试。不过,在正确引导的前提下,无论你准备投入多少测试工作,LLM 都能让你比以前轻松得多地完成这些工作。
一个极端的例子是:我交流过的所有稍微关心质量或测试的人,都认为 LLM 默认生成的测试,或者在你告诉它“编写测试”“编写更多测试”等之后生成的测试,质量很差。根据各自标准的不同,人们通常会认为这些测试介于毫无价值和勉强有用之间。
例如,编译器工程师 Em Chu 表示:
我目前使用的现有测试并不完美,但仍然高于 LLM 似乎瞄准的标准。我会把 LLM 的标准描述为:“足够周全,能让某个功能偷偷通过人类代码审查。”对于编译器而言(与 UI 等领域相比),我猜编写一般测试会更容易,但人们通常也会对最终产品的正确性提出更高要求,而 LLM 在这方面就是很差。面对人类编写那些真正能够发现 bug 的测试时所采用的对抗性过程,比如“那么,如果我这样做会怎样”或者“让我们试试所有情况的笛卡尔积”,LLM 的表现差得令人痛苦。
与此同时,我也见过不少人热情称赞 LLM 在测试方面有多么惊人,只要告诉它“编写测试”“编写更多测试”等就行。当我进一步了解人们为什么认为 LLM 非常擅长测试时,我发现,认为 LLM 测试能力出色的人,往往原本基本完全不做测试。嗯,这很合理。如果从几乎零测试投入增加到一点点测试投入,那当然会带来巨大的收益。
截至 2026 年 6 月,指导 LLM 进行模糊测试或随机化测试时,给人的感觉也很类似。我尝试过让 LLM 生成模糊测试器;对于大多数项目,这种方法都能在几分钟内发现真实且通常相当严重的 bug。然而,当我查看 LLM 创建的模糊测试器究竟在尝试测试什么时,我的反应与重视质量的普通程序员查看 LLM 创建的测试时一样。LLM 生成的模糊测试器,其覆盖范围差得出奇,会遗漏各种你原本以为一个人类匆忙写出的模糊测试器都应该覆盖的基本情况。取决于你看待问题时是“杯子半空”还是“杯子半满”,你可能会说,这反映了大多数项目的测试覆盖率有多糟糕;也可能会说,这体现了模糊测试不合常理的有效性。
从宏观上看,如今最先进模型生成的模糊测试器,并不擅长“思考”应该如何改变输入以诱发 bug。然后,如果你只是简单告诉它应该如何改变输入并将这些变化组合起来,它同样无法以合理的方式组合会触发 bug 的各种要素。确实可以提供一套效果良好的指令,但这种方式高度依赖用户给出具体指导。
如果你只是把随机化测试当作“加分项”,用来多捕获几个 bug,或者替代传统的软件测试流程,那么可以直接让 LLM 寻找代码中的高风险区域,找出可能被违反的不变量,并对其进行模糊测试。这种方法效果尚可。当我说服别人尝试一些随机化测试时,他们通常会从这里开始,并找到相当多令他们庆幸被发现的 bug。由于愿意尝试对自己而言新颖测试技术的人群本身具有一定特点,这些人通常参与过公司内部一些经过最充分测试、可靠性最高的代码;即便如此,他们仍然能在自己那些相对经过良好测试的代码中发现 bug。
如果你希望使用随机化测试来约束由 AI 智能体驱动的“软件工厂”工作流,使其保持诚实可靠,那么就必须有办法应对当前最先进模型存在的缺口。因为,当你每天向一个项目交付相当于数百乃至数千个 PR 的变更时,任何没有被明确约束、无法防止退化的部分,都会迅速退化。
高层次来看,整个系统需要某种反馈机制来发现差距,并指导执行 fuzzer 调整的循环去弥补这些差距。最近,我一直在测试一些我不了解其领域、不理解项目或代码的东西,所以我基本上是在凭感觉操作,我知道我想出来的东西中会存在很多差距(如上所述,LLM 在这方面表现糟糕)。但即使在我熟悉领域、相对较好地理解代码的领域,仍然会有一些差距,因为人类会遗漏事物、犯错误,所以测试设置中总是需要某种反馈来发现差距,让你或智能体去弥补这些差距。
我一直在尝试各种方式让智能体聚集和重新聚集,以使智能体循环运行得更好,虽然这种做法有帮助,但我还没有找到一种足够好的方式来创建某种不依赖外部反馈的智能体软件质量改进循环,无论这种反馈是偶尔的人工输入,还是发布某些东西(最理想的情况下只是部分发布,并采用分阶段推出),然后让系统监控指标/日志/跟踪/支持工单等作为反馈。我上面提到的支持工单到 PR 的管道就是这样一个反馈循环。该管道不仅试图生成 PR,还试图让测试设置添加测试覆盖率,以发现该错误、可能浮现其他错误,或如果将来有回归就会重新发现该错误。这似乎效果还不错,因为它发现了真实的错误并改进了测试覆盖率,但我确信还有很大的改进空间。
相关地,我一直在思考为什么 LLM 在写测试方面这么糟糕。在询问了一些人后,我被告知这是因为 LLM 拥有的能力来自于人们构建的强化学习环境,这些环境允许模型在任务上改进,有时以可泛化的方式,有时则不然。我还被告知存在出售强化学习环境的市场,但这个市场相当小,因为没有那么多买家,而且你真的想认识实验室中的某个买家或接近买家的人。如果你是这样的人,或者能把我和这样的人联系起来,你能帮我一个忙,联系我一下吗(我在 X、Mastodon、电子邮件等上相对容易联系到)。我很好奇这是如何运作的,以及出售一个强化学习环境用于测试、优化或本文讨论的长期任务是否可行,在这些情况下很容易看到明显的差距。
回到测试的话题,进行 fuzzing 或任何形式的错误审计时,检测假阳性是过程中的关键部分。至少目前,获得比任何公开可用模型更好的模型的访问权限也救不了你。前段时间,Dennis Snell 沮丧地告诉我,他花了一整天时间清理 Anthropic 转发给他所在公司的 AI 垃圾——这些垃圾来自他们吹嘘的 Mythos 模型,而这个模型因为太危险而不能向公众发布。Anthropic 似乎是想对这家公司做好事,或许是进行某种 EA 安全改进,问题是他们没有设置合理的假阳性过滤流程,所以他们就是直接转发垃圾给我们。那时我用的是一个模型,如果 Fable 有任何指示的话,似乎能力适中,略低于 Mythos,但我毫无问题地生成了无尽的错误流(其中一些是安全问题),且没有任何已知的假阳性。这似乎表明,围绕模型的合理设置至少和拥有最新最强的模型一样重要。
我一直在按项目/问题基础尝试自定义工作流,并没有一个通用的假阳性拒绝方案,但有各种似乎是半通用的东西。如果你不介意花费 token,让独立的智能体重复检查一个据称的错误再现 (repro) 会大大降低假阳性率。几个月前,我提到我在使用不同的"人设"进行审查以及管理智能体循环时取得了不错的成绩,我收到了一些基于理论的回应,说这不太可行,但在实践中,它似乎效果相当不错。我的工作流定期变化,大概在那次讨论一周后,我开始向混合中添加"反向人设",这在相同的时钟时间或 token 预算下改进了性能。
对于任何需要人工审查的东西,拥有某种工件(例如,如果是一个预期在 UI 中显现的错误,就是一个视频)是必要的。不需要真正显式地让智能体审查这一点,仅仅生成这一点似乎就会在某种程度上降低假阳性率,然后让智能体审查这个工件会进一步降低假阳性率。要求智能体独立地审查这个工件(例如,查看生成视频的测试代码与看视频本身)也会进一步降低假阳性率。一般来说,获得独立的观点似乎对减少假阳性很有帮助。在我进行的实验中,这比让智能体拥有不同的人设/角度,按时钟时间或每美元来说效果较差,但仅仅多次问同一个问题就会改进结果,原因应该从 LLM 方差部分的图表中显而易见。
几乎我尝试的所有降低假阳性率的方法都有效,所以如果你不是在扩展工作流到优化成本很重要的程度,做任何相当合理的事情似乎都能相当好地工作。
我不断收到各种工具和工作流推荐,当我深入了解时,我几乎从未能找到关于采纳该推荐是否有意义的好信息。举个例子,我在工作中看到"洞穴模式"被推荐多次。洞穴模式据称降低 token 使用并加快提示解决速度(README 声称 75% 的 token 使用减少、65% 的 token 使用减少和 2x 更少的 token,以及 3x 的速度提升)。
搜索信息(只是用谷歌搜索"caveman mode",没有引号,最上面的不是指向洞穴模式的链接的结果是这个 Reddit 线程,在三个最热评论中,一个是笑话,另外两个强烈推荐它:
只是一种令人耳目一新的极端简洁方式...并且大幅降低了 token 计数,似乎对分析思维没有影响...但我没有办法对前后进行基准测试。
工作中有人在测试它,似乎真的能省