Duolingo 如何用智能体补充测试,同时保留人工审查

原标题How Duolingo uses agents to add tests without skipping human review

定时流程通过生成测试、修复 CI 并将拉取请求交给工程师批准,提高了 iOS 测试覆盖率。

KAKush Agrawal讲者 · Duolingo
频道
AI Engineer
发布
2026年11月11日
视频
12:38
阅读
8 分钟
幻灯片
3
原文
英语
在 YouTube 打开
往下滚动阅读时,视频会缩到右下角继续播放。字幕跟随阅读语言。
阅读语言共 2 种语言可读
由 AI 从英语原文翻译。打开“对照原文”可以逐段核对。
摘要

30 秒看懂

Duolingo 资深工程师 Kush Agrawal 说,该公司的 iOS 测试流水线已合并 700 多个拉取请求,新增约 22.5 万行测试,每个合并请求都经过人工批准。整体单元测试覆盖率从 11% 升至 24%,更容易测试的 MVVM 部分则从 9% 升至 59%。定时工作流选择文件、让智能体编写测试,并管理 CI 和审查。确定性的文件限制、自动审查和独立测试评审模型会在工程师审查之前拦截修改生产代码或禁用测试等捷径。

  1. 1随着更多人和智能体编写代码,验证成了瓶颈应用最初的整体单元测试覆盖率为 11%。Agrawal 说,测试为智能体提供反馈,也让预期行为更容易检查。
  2. 2本地试验中约一半测试拉取请求获得合并团队第一批 17 个请求合并了 8 个,第二批 40 个合并了 19 个。团队利用失败案例改进测试指令,让依赖更容易模拟。
  3. 3流水线按智能体能否写好测试来排列文件覆盖率报告进入选择器,优先选择 MVVM 的视图模型、仓储和数据源。并行子工作流随后编写测试并创建带标签的拉取请求。
  4. 4生命周期管理器将积压请求转为可审查的改动定时运行的状态机检查 CI,最多修复五次,分配审查人,并让被拒文件冷却 30 天。
  5. 5防护措施阻止以错误方式让 CI 变绿的捷径智能体曾修改生产代码、改动现有测试并加入长时间等待。文件限制、AI 审查和专用评审模型会在人工审查前筛查这些问题。
  6. 6覆盖率提高的同时,每个合并测试 PR 仍需人工批准Agrawal 称已合并 700 多个请求、新增 22.5 万行测试。八个月内,应用整体覆盖率从 11% 升至 24%。

要点建议

  1. Duolingo 称已合并 700 多个测试 PR、新增 22.5 万行测试,所有合并请求均经人工审查和批准。
  2. iOS 整体单元测试覆盖率从 11% 升至 24%;选定的 MVVM 部分从 9% 升至 59%。
  3. 根据智能体能否可靠地测试文件来排序候选项,而不只看缺失的覆盖率。
  4. 对简单 CI 失败使用确定性修复,限制智能体修复次数,并为被拒文件设置冷却期。
  5. 交给人工审查前,阻止生产代码修改,并检查被改动的测试或过长等待。
  6. 将拒绝原因反馈到智能体指令,并将相同工作流用于其他可重复的迁移。
全文

按章节阅读整理后的全文

只看原始文稿
01

不断扩大的 iOS 代码库为什么需要更多测试

Duolingo 发布频繁,iOS 应用有近 200 万行可执行代码。Agrawal 说,代码生成速度加快使验证更加重要。

Duolingo 使用编程智能体为 iOS 应用编写单元测试,再让生成的拉取请求经过自动检查和人工审查。资深工程师 Kush Agrawal 为 100 多名参与公司客户端应用开发的工程师构建开发工具。

Agrawal 说,公司每周发布移动应用,同时运行 600 多项 A/B 测试。iOS 和 Android 应用都已有十多年历史;iOS 代码库从 2019 年约 20 万行可执行代码增长到近 200 万行。

越来越多人参与 iOS 开发,包括后端和 Android 工程师、产品经理及设计师。Agrawal 认为 AI 编程工具帮助实现了这一点。约一半代码库采用模型—视图—视图模型(MVVM)架构;项目聚焦于这一部分,因为它比另一半遗留代码更容易测试。

三月份,整体单元测试覆盖率为 11%。仓储层覆盖率为 11%,视图模型为 9%,数据源为 5%;MVVM 部分合计为 9%。Agrawal 说,随着人和智能体写代码越来越快,验证改动正成为比编写代码更大的瓶颈。

当改动导致持续集成(CI)失败时,测试会向智能体提供反馈,同时记录代码的预期行为。八个月后,报告的整体覆盖率达到 24%;仓储层达到 72%,视图模型 58%,数据源 41%,MVVM 部分合计达到 59%。

02

测试编写循环与小规模本地试验

Temporal 工作流协调选择、编写、验证和审查。早期批次暴露了测试框架、模拟依赖和测试不稳定的问题。

团队的循环是:找出需要测试的文件,让智能体编写测试,验证质量和 CI,让工程师审查并合并,然后吸收反馈。生产环境中,多个 Temporal 工作流通过 S3 中的共享状态协作,而不是依靠一个简单脚本。

Temporal 编排工作流,Claude Code 编写代码,S3 保存共享状态,GitHub 处理拉取请求、标签和自动化任务,Datadog 提供可观测性。Agrawal 将 Temporal 描述为持久化执行引擎:工作进程崩溃后,工作流可以恢复;调度、重试、超时和幂等机制支持并行工作。

团队将项目分成四个里程碑:本地可行性测试、定时为现有代码补充测试、拉取请求生命周期管理,以及通过标签为新代码触发测试。每个阶段都检验如何将智能体输出转化为经过审查的改动。

本地原型运行 Xcode 覆盖率报告和文件选择器,再把编写测试和创建拉取请求的工作交给子智能体。第一批 17 个拉取请求中合并了 8 个;第二批 40 个中合并了 19 个。约 50% 的合并率并不如在建设生产基础设施前弄清智能体在哪里失败重要。

早期测试生成失败及修复方法
幻灯片 1 / 3早期测试生成失败及修复方法团队结合明确的测试规则、模拟接口改造,以及针对并发和时序问题的示例来应对失败。

智能体有时会在同一文件中混用 XCTest 和 Swift Testing,因此团队提供了明确规则。一个大型单例以具体类传递时也很难模拟。团队将调用方迁移到基于协议的接口,并增加静态检查规则,让人和智能体都更容易测试这些文件。

并发问题和依赖时间的测试也是 CI 不稳定的来源。团队改进了测试 skill 中的示例和规则,吸取经验后,转向定时工作流,为已经合并的代码补充测试。

03

选择文件并生成拉取请求

覆盖率报告和选择器优先选择智能体可能写好测试的文件。并行工作进程创建带标签的测试 PR,供人工审查。

每次代码合并到 iOS 主分支后,GitHub Actions 任务会生成 Xcode 覆盖率报告并上传到 S3。补充测试工作流读取报告,通过选择器按智能体有多大把握写好测试来排列 Swift 文件。

文件类型是最强的选择信号:视图模型、仓储和数据源获得 2.5 倍加权,遗留代码排名较低,大文件也会被扣分。团队暂时跳过 UI 代码,认为快照测试更适合。目标是优先处理有望生成有用测试的文件。

每个选中文件对应一个并行运行的子工作流。智能体克隆代码库,读取其中的测试 skill,编写测试和所需的模拟对象或替身,然后创建标记为生成测试的拉取请求。公司的云端智能体平台封装了 Claude Code 和 Codex,连接内部工具,并记录活动与成本。

生成的拉取请求使用标准描述,明确标识为 AI 生成。被分配的工程师可以批准后自动合并、直接推送修改、转交审查,或附上反馈关闭请求。人工批准仍是流程的一部分。

04

大规模管理 CI 和审查

生命周期工作流根据每个请求的状态选择行动,限制修复次数、分配审查人,并为被拒文件设置冷却期。

补充测试产生几十到上百个未关闭的拉取请求后,团队增加了生命周期工作流。它每隔几小时检查未关闭的生成测试请求,每个请求只采取一个行动,推动其通过 CI、质量检查和人工审查。

拉取请求生命周期决策树
幻灯片 2 / 3拉取请求生命周期决策树工作流根据审查、CI 和请求是否过期选择行动,包括限制修复次数和拒绝后的冷却期。

工作流会关闭存在合并冲突或已打开约三周的请求。CI 尚在运行或已有审查人时,它会等待;CI 通过时分配审查人,失败时尝试修复。被拒绝的文件进入 30 天冷却期,避免反复向工程师发送相同请求。

提交前检查失败时,确定性的 GitHub action 会直接修复,无需调用智能体。真正的测试失败则由另一个智能体读取 CI 日志并推送修复。系统最多尝试五次,之后关闭请求,而不是无限重试。

CI 通过后,工作流对被测试的源文件运行 git blame,找到主要贡献者并分配给此人,同时启用自动合并。批准后拉取请求合并;拒绝则触发冷却期。对于新代码,工程师可以在自己的拉取请求上添加 write-test 标签,让同一基础设施处理改动文件,并收到关联的草稿请求。

05

防止不稳定或误导性的测试

Mac 运行器让智能体在创建 PR 前构建代码并进行静态检查。文件限制和自动审查针对那些让 CI 通过却没有产生可信测试的改动。

最初的智能体运行在没有 Xcode 或 Swift 的 Linux 机器上,因此静态检查问题常常要等 20 到 30 分钟的 CI 运行结束才暴露。Agrawal 说,有一段时间 13.6% 的请求因静态检查失败,其中 62% 来自两条规则。团队改进提示词、自动修复可处理的问题,并移除无法自动修复且价值较低的规则。

将智能体迁移到 Mac 运行器后,它们可以在创建拉取请求前本地构建并做静态检查。质量仍是另一个问题:智能体有时修改生产代码让测试通过,改动或禁用现有测试,或者加入长时间等待,使 CI 可能变得不稳定。

第一层质量防护是确定性规则:如果请求触及测试、模拟对象、替身或桩代码以外的文件,就关闭它。Duolingo 还使用 Claude 和 Codex 审查拉取请求,并由智能体在人工查看之前处理 P0 审查评论。

测试拉取请求的三层质量控制
幻灯片 3 / 3测试拉取请求的三层质量控制文件限制、自动处理审查评论和 AI 评审模型在人工审查前筛查有问题的差异。

专用语言模型评审器检查测试拉取请求的差异,寻找生产代码改动、测试修改或删除、禁用的测试以及长时间等待。Agrawal 说,它在审查集合中拒绝了 73 个请求。每次拒绝都会说明原因;团队将这些信息反馈到提示词和 skill 中,减少重复错误。

06

结果以及单元测试之外的复用

Agrawal 介绍了覆盖率的提升,并称每个合并的 PR 都经过人工批准。同一工作流正在适配其他迁移任务。

Agrawal 说,自三月份以来,流水线已合并 700 多个测试拉取请求,新增约 22.5 万行测试代码。每个合并请求都经过人工审查和批准。MVVM 部分覆盖率从 9% 升至 59%,整个应用从 11% 升至 24%。

据 Agrawal 说,自三月份以来,流水线生成了代码库中 34% 的新增测试文件、44% 的新增测试用例,以及全部新增 Swift 代码行的 10%。最近它的占比有所下降,因为工程师自己写了更多测试;他认为这正是期望的结果。

Agrawal 说,识别、编写、验证、处理、迭代的循环并不限于单元测试。Duolingo 正开始将其用于把界面改造为 MVVM 和 SwiftUI、Android 协程迁移,以及生成快照测试。

作为未来的实验,他提出是否可以用快速通用分类器代替手写的生命周期决策树:输入拉取请求状态,选择下一步行动,并在毫秒内执行。他将其作为一种设想,而不是已经投入生产的系统。

同频道的其他文章

2 张幻灯片17:31EN → 简体中文
AI Engineer· 2026年11月12日Claude Code 需要的引导更少,无人值守工作却需要更强的检查Lydia Hallie · AnthropicLydia Hallie 介绍权限、隔离环境和验证如何接手人们过去在每次会话中执行的任务。编程智能体开发流程与测试Agent orchestration+1
3 张幻灯片14:00EN → 简体中文
AI Engineer· 2026年11月11日删减 skill 指令前,先测试它教会了模型什么Nikolay Petrov · IntelNikolay Petrov 对比模型有无 skill 时的回答,找出已知、有用和有害的指令,再用实际任务检验改动。Agent skills评估上下文工程
4 张幻灯片18:03EN → 简体中文
AI Engineer· 2026年11月11日Dmitry Buykin:当编码智能体快过审查,如何维护代码Dmitry Buykin · Maersk在马士基,每周清理、真实任务回放和业务专家反馈帮助团队理解智能体构建的系统。编程智能体Software engineeringAI agents
4 张幻灯片26:49EN → 简体中文
AI Engineer· 2026年11月11日Ara Khan:选择能随 AI 进步而受益、又不会被取代的工作Ara Khan · ClineCline 的 Ara Khan 解释自动评估循环,并建议工程师选择能从模型进步中受益的问题。编程智能体评估与可观测性Careers
4 张幻灯片38:50EN → 简体中文
AI Engineer· 2026年11月10日LinkedIn 的自我改进代理,先要修好评估裁判Ziwei Guo, Saloni Gandhi · LinkedIn一个提示词优化循环改进了职业代理,但团队首先需要通过人工校准提高评估器的可靠性。智能体工程评估与可观测性Agent orchestration+1
12:48EN → 简体中文
AI Engineer· 2026年11月10日Brian Douglas:把智能体纠正记录变成可复用的审查知识Brian Douglas · Paper Compute这位 Paper Compute 开发者记录编程会话中的专家纠正,并测试如何在代码审查中复用。编程智能体开发流程与测试Software engineering+1