从可运行代码到可发布应用:面向全栈 Web 应用生成的测试驱动开发框架 From Runnable to Shippable: Multi-Agent Test-Driven Development for Generating Full-Stack Web Applications from Requirements
TDDev 用三阶段闭环把 TDD 自动化迁移到浏览器层,使 Web 应用生成的功能正确率比无 TDD 基线提升 34–48 个百分点。
前置知识
Test-Driven Development (TDD)
TDD 是一种软件开发实践,核心是'先写测试,再写实现'。开发者针对每个特性先编写自动化测试用例(描述期望行为),然后编写最少代码让测试通过,最后重构。TDD 通过把需求转化为可执行规约,让进度可量化、缺陷可定位。在 LLM 编码代理场景中,TDD 的反馈来自编译器/终端输出,可被代理直接读取。
本文的方法基础就是 TDD,但需要先理解传统 TDD 才能体会论文指出的核心痛点:Web 应用的正确性不能从源代码或终端输出推断,必须部署到真实浏览器并通过用户交互仿真才能判断,这正是 TDDev 要解决的根本矛盾。
LLM Coding Agent
基于大语言模型的自主编码代理,能够循环调用工具(读文件、写文件、执行命令)完成多步骤编程任务。典型例子包括 Claude Agent SDK、OpenCode、OpenHands 等。代理接收任务描述和可用工具列表,LLM 反复决定下一步动作(自然语言完成或工具调用),工具结果回灌给 LLM,直到 LLM 认为任务完成。
TDDev 评估的正是这类代理。论文特意选用'故意做小'的 ClaudeSDK(无规划模块、无记忆、无多文件上下文选择),让协议差异而非代理复杂度成为自变量。理解代理的工具调用循环机制,是理解论文中 Agentic-TDD 与 Whole-Project/Incremental 协议差异的前提。
Acceptance Test / 验收测试
从用户视角描述系统行为的测试,关注'做什么'而非'怎么做'。每条验收测试通常包含:被测功能、交互步骤序列(如'输入邮箱、点击提交')、可在 UI 中观察的预期结果(如'主页出现新商品')。与单元测试不同,验收测试面向业务功能而非代码单元,常作为开发完成度的判定标准。
TDDev 第一阶段的核心产出就是验收测试套件 $C(T_0)$,它由 LLM 通过'soap opera 测试'思路(先想象用户角色和目标,再细化为可执行步骤)生成,直接决定了后续验证阶段判分和修复阶段的方向。没有这一层抽象,自动 TDD 闭环无从启动。
Model Context Protocol (MCP) 服务器
MCP 是一个由 Anthropic 提出的标准化协议,让 LLM 代理通过统一接口(通常是 stdio)调用外部工具。MCP 服务器把工具能力(start_app、run_tests 等)注册为可发现资源,任何兼容 MCP 的代理都能直接挂载,无需修改代理内部实现。
论文把 TDDev 的部署/测试/停止三个工具打包成 MCP 服务器,塞入 OpenCode 的会话配置,这样在 Agentic-TDD 条件下 OpenCode 就能像 ClaudeSDK 一样调用这些工具。这是论文实现'跨代理、跨模型、跨基准一致工具接口'的关键工程手段,也是研究方法可复现性的基础。
研究动机
现有编码代理生成的 Web 应用在功能正确性上严重不足:WebGen-Bench 基准显示,即使是 state-of-the-art 系统,在超过 70% 的案例中无法满足功能性需求。核心矛盾在于 Web 应用正确性的判定与传统代码截然不同——传统 TDD-for-agent 假设验证信号来自编译器输出、终端日志或单元测试结果,这些信号对 LLM 直接可见;但 Web 应用的正确性必须经过部署、浏览器渲染、模拟用户点击/输入/导航交互才能判断,失败也是用户体验层面的(按钮不响应、状态不更新、页面无法跳转)而非显式错误日志。这些'上下文性、用户面对性'的失败既难捕获,也难以直接喂给代理去修复。当前的工程实践把这三步(部署、交互观察、失败翻译)全部交给人类开发者手动完成,既繁琐又使得 TDD 策略的系统性受控研究根本无法进行。
本文的目标是本文的目标是构建一个名为 TDDev 的模块化框架,把 Web 应用生成的 TDD 闭环完全自动化,消除人类中介,使受控的 TDD 协议对比研究变得可行。具体而言,框架要解决三大子问题:(1) 把模糊的自然语言需求(如'一个购物网站')转化为可由浏览器代理执行、可由系统判分的验收测试;(2) 自动部署代理生成的代码并在浏览器中通过 Playwright 模拟真实用户交互进行验证;(3) 把浏览器观察到的失败(往往是上下文相关的体验问题)翻译成结构化、可操作的修复报告,供编码代理直接消费。论文的最终目标不仅是提出框架,还要在 2 个编码代理 × 2 个骨干模型 × 2 个基准的实验矩阵下,首次给出 TDD 协议对 Web 应用生成质量影响的实证分析。
与已有工作不同的是,已有 TDD-for-agent 工作(TiCoder、ConTested、AutoCodeRover、TestGen-LLM 等)全都默认验证反馈来自终端或编译器,这一假设在 Web 应用领域被打破。UI 代码生成相关工作(从 pix2code 到 WebpageUICopilot、Design2Code)则聚焦于从截图/设计图生成前端外观,对全栈功能性几乎不评估。GUI 测试领域虽有 Monkey、Playwright、基于 RL/MLLM 的测试方法,但它们主要用于发现缺陷或探索式覆盖,既不面向具体功能验收规约,也未嵌入到开发闭环中产生修复信号。TDDev 正好填补这个空白:它是第一个把'验收测试生成 + 浏览器交互验证 + 失败翻译'三件事系统化地串成闭环、且首次允许在受控条件下比较不同 TDD 协议的工作。切入角度的核心新颖性体现在两个维度——一是把 TDD 从'代码可观察层'扩展到'浏览器可观察层',二是把'测试信号可访问性'作为研究对象,而非测试技巧本身。
核心方法
TDDev 围绕一个三阶段闭环展开,让编码代理在没有人类介入的情况下反复部署、测试、修复生成的 Web 应用。整体直觉是:Web 应用正确性的判断必须放到真实运行环境中,而浏览器是唯一能提供端到端功能信号的'黑箱'。技术路线上,框架先用 LLM 把自然语言需求拆解成结构化验收测试(场景化、面向用户角色),然后让一个 LLM 驱动的浏览器测试代理基于页面可访问性树和测试上下文动态生成 Playwright 动作并执行,最后把测试代理积累的交互轨迹与自然语言解释压缩成结构化失败报告,作为编码代理下一轮修复的直接输入。在这套基础设施之上,论文定义了四种开发协议(沿'强制程度'轴从高到低:Incremental、Whole-Project、Agentic-TDD、Non-TDD),并把这四种协议作为实验自变量,在 2 个编码代理 × 2 个骨干模型 × 2 个基准的矩阵下做对比。
TDDev 与已有 TDD-for-agent 工作的本质区别,在于它把验证反馈从'代码可读层'搬到'浏览器可观察层',并把这条反馈链路上原本由人完成的三件事(测试生成、交互验证、失败翻译)一次性自动化。其核心创新点有三:第一,验收测试生成采用'soap opera testing'思路,先让 LLM 想象具体用户角色(如发布食物的协调员、查询附近列表的接收者)及其目标,再把目标细化为(feature, ordered steps, expected outcome)三元组,这同时解决了'多样性'和'可判分性'两个难题。第二,测试代理不是预先写好的脚本,而是基于实时可访问性树动态生成 Playwright 代码,既能适应代理生成应用结构的不确定性,又比通用 GUI agent 更可靠——这是一个'脚本可靠性'与'通用性'的折中设计。第三,失败翻译阶段把交互轨迹、自然语言解释、测试上下文一起构造成结构化报告(包含 attempted steps、failure location、observation 三个字段),让编码代理拿到的不再是'页面哪里不对'这种模糊描述,而是'register form 缺少 submit 按钮'这样的精确修复起点。
方法步骤详情
TDDev 的执行流程严格按三阶段闭环组织。阶段一(Acceptance Test Generation):输入自然语言需求 $T_0$,首先让 LLM 想象多个具体用户角色及其目标,产生候选测试需求集合;然后对每个候选,调用 LLM 把它细化为三元组 $\langle \text{feature}, \text{ordered steps}, \text{expected outcome} \rangle$,其中 expected outcome 必须在浏览器中可观察;所有生成的测试 $C(T_0)$ 在编码开始前就作为显式产物暴露,允许用户在开发前审查调整。阶段二(Interactive Validation, 算法 1):编码代理输出应用 $S$ 后,TDDev 自动检测框架、部署到本地 URL,用 Playwright + Chromium 打开;测试代理在每个时间步读取当前页面的可访问性树 $o_t$ 与测试上下文 $c$,查询 LLM $M$ 决定下一步动作(生成 Playwright 代码)或返回 verdict;每执行一步就把 (动作, 结果) 追加到轨迹 $\tau$,最终输出 verdict $\in \{\text{pass, fail, partial}\}$ 与轨迹。阶段三(Failure Translation):当 verdict 非 pass 时,BuildFailureReport 把轨迹、自然语言解释、测试上下文压缩成结构化报告 $f$,包含 attempted steps、failure location、observed behavior 三段,作为修复输入。协议层面,四种协议把这套工具用不同强度接入开发流程:Non-TDD 完全不暴露工具、单次评估;Agentic-TDD 通过 MCP 服务器把工具注入代理,代理自主决定何时调用(算法不外加循环);Whole-Project(算法 2)先一次性实现全应用,再进入最多 $K=5$ 轮的 deploy-test-repair 循环,所有测试一起跑;Incremental(算法 3)逐个特性处理,每个特性部署后只跑当前测试 + 已通过的回归套件,直到通过才进入下一个。
技术新颖性
技术新颖性主要体现在三个层面。第一是'测试来源'的革新——传统 LLM agent 的测试反馈直接来自编译器或单元测试,而 TDDev 通过浏览器交互代理+可访问性树+Playwright 三件套,创造了一种全新的'浏览器级反馈信号',既不是文本也不是图像,而是结构化的交互轨迹。第二是反馈信号的'翻译机制',这是论文相对 AutoCodeRover、SWE-agent 等工作的最大区别——后者直接消费编译/测试日志,前者必须把'按钮点击没反应'这种体验级问题逆向映射为'某个 handler 没绑定'这种代码级修复指令,BuildFailureReport 就是承担这一翻译职责的核心组件。第三是协议维度的研究设计,论文首次把'基础设施是否提供'与'是否强制执行'两个变量解耦,形成 4 协议对比,并首次发现模型生成风格(holistic vs read-then-extend)与协议结构之间的可预测交互作用,这是一个有理论贡献的实证观察。
实验结果
核心发现可以归纳为四点。第一,TDD 基础设施能稳定提升 Web 应用生成质量:RQ2 显示在 WebGen-Bench 三个 agent-model 组合上,TDD-equipped 协议比 Non-TDD 基线提升 34–48 个百分点(ClaudeSDK+Sonnet:65.8% vs 31.3%,ClaudeSDK+Qwen:71.4% vs 23.3%,OpenCode+Qwen:50.7% vs 11.7%);ArtifactsBench 也有 +7.6pp 的提升但幅度收窄,反映 TDD 收益与任务复杂度正相关——简单任务单次即过,迭代修复空间小。第二,最优协议取决于模型生成风格:RQ3 显示 Sonnet(holistic,从头重写型)在 Agentic-TDD 下达到 65.8%,但若强加 Incremental(假设 read-then-extend 的代码组织),反而掉到 31.3%,与基线持平;反之 Qwen(conservative,read-then-extend 型)在 Incremental 下达到 71.4%,若走 Agentic-TDD 则只有 41.0%——这是 protocol-model 风格错配的典型表现。第三,反馈轮次的效果同样依赖风格:RQ4 表明 Whole-Project 在三组合下大致把准确率从 $k=1$ 到 $k=5$ 翻倍(Sonnet:21.3%→49.1%,Qwen:29.0%→51.4%,OpenCode:24.0%→50.7%),前两轮收益最大、$k=4$ 后饱和;Incremental 在 Sonnet 上 $k=2$ 后停滞,在 Qwen 上则持续增长(59.7%→71.4%)。第四,协议错配同时伤害准确率和成本:RQ4+6.2 显示 Sonnet 在 Incremental 下花 9.7M tokens 换来 0pp 收益,Qwen 在 Incremental 下花 108.7M tokens 才换来 +46.7pp(每个 pp 2327K tokens,效率比 Sonnet 最佳低 25 倍)。用户研究(RQ5)进一步证实 TDDev 把开发者的人工干预降到 0 次/0 词,而 Bolt.diy 平均需要 3 次干预/74 词额外提示/4.7 分钟人工时间。
查看结构化数据
| 任务 | 指标 | 本文 | 基线 | 提升 |
|---|---|---|---|---|
| WebGen-Bench (ClaudeSDK + Claude Sonnet 4.6) | acc@5 (Pass + 0.5×Partial)/Total × 100% | Agentic-TDD: 65.8%; Whole-Project: 49.1%; Incremental: 31.5% | Non-TDD: 31.3% | +34.5 pp (Agentic-TDD),但 Incremental 与基线持平,显示协议错配 |
| WebGen-Bench (ClaudeSDK + Qwen-3.5) | acc@5 | Incremental: 71.4%; Whole-Project: 51.4%; Agentic-TDD: 41.0% | Non-TDD: 23.3% | +48.0 pp (Incremental 最佳),与 Sonnet 形成镜像——风格决定协议选择 |
| WebGen-Bench (OpenCode + Qwen-3.5) | acc@5 | Whole-Project: 50.7%; Incremental: 45.7%; Agentic-TDD: 27.3% | Non-TDD: 11.7% | +39.0 pp (Whole-Project 最佳),展示协议效益跨代理也成立 |
| ArtifactsBench (ClaudeSDK + Claude Sonnet 4.6) | acc@5 | Whole-Project: 86.2%; Agentic-TDD: 82.9%; Incremental: 81.4% | Non-TDD: 78.6% | +7.6 pp,差距收窄源于基准偏自包含游戏/动画任务,功能需求简单 |
| 测试生成覆盖率 (WebGen-Bench 10 应用) | matched GT features / total GT features | 覆盖 7/10 完美、3/10 部分、整体 91.9% (57/62 GT 测试) | 生成测试数 12.4 vs GT 6.2 平均,粒度更细 | 覆盖率足够支撑闭环,漏掉的仅是站点导航等结构性元素 |
| 测试代理准确率 (20 correct + 20 broken fixture) | verdict 准确率 | broken 100% (20/20)、correct 75% (15/20)、整体 87.5% | 无对照(模块自检) | 零假阳性(没把 broken 误判为 pass),所有错误均为保守型假阴性 |
局限与改进
作者承认的局限有三:一是测试代理存在 12.5% 的误判率,虽然全部是假阴性(把正常应用判错),但会触发不必要修复轮;二是测试生成在 3/10 应用上未能覆盖站点导航、面包屑等结构性需求,这些需求需要详细的 UI 走查描述才能推断;三是 ArtifactBench 任务分布偏窄(自包含游戏/动画),留不出 TDD 迭代空间。从读者角度我额外观察到几点:第一,论文所有结果都在一台 MacBook Pro M-series + 32GB RAM 上得出,未涉及多机分布式或大集群复现,实际工业部署的稳定性未经验证;第二,evaluation 完全依赖 LLM 驱动的测试代理判分,虽然假阳性率为 0 令人安心,但 87.5% 的总体准确率仍是上限天花板,且 Sonnet 和 Qwen 在判分时可能共享同一 LLM 的偏差;第三,用户研究样本仅 3 人,且没有跨资历/跨项目的扩展,定性结论的统计强度有限;第四,WebGen-Bench 50 个样本的 seed=42 固定,虽然可复现但对任务分布的代表性可能不足。
独立分析的弱点
独立分析可识别几个改进方向。第一,测试代理对'正确应用'的判分存在 25% 假阴性,根因有二:一是 selector 生成错误(LLM 生成的 Playwright 选择器与渲染元素不匹配,如 `text=Add` vs 渲染符号 `+`),可通过接入元素属性 fuzzy match 或允许测试代理在 selector 失败时自动 fallback 修复;二是过度保守判分(对轻微措辞差异直接 reject),可通过引入语义相似度阈值(如对 expected outcome 做 embedding 比对)放宽通过条件。第二,Incremental 协议对 holistic 模型无效的核心是它强迫 Sonnet 反复重写整应用,导致回归套件每轮暴露新缺陷。改进方向:让协议检测代理每次实现的 diff,若 diff 是 full-rewrite 而非增量则自动切换到 Whole-Project 行为,实现'自适应协议'。第三,Qwen 在 Incremental 下 token 消耗是 Sonnet 同条件的 18 倍(108.7M vs 5.9M),主要源于每特性最多 5 轮 × 6 特性的笛卡尔展开与累积上下文,可考虑增量上下文压缩或只对失败的测试保留完整描述。第四,失败报告目前是纯文本结构化字段,缺乏指向具体文件/行/元素的精确锚点,可考虑让 BuildFailureReport 附带元素 DOM 路径和最可能的源码位置。第五,MCP 服务器在 Agentic-TDD 下把工具塞给代理,但工具描述质量决定了代理自主决策的成败,论文未给出工具描述细节,复现时容易踩坑。
未来方向
作者在结论中提出三个明确方向:一是把浏览器验证扩展到认证多用户工作流,目前 TDDev 假设应用是无状态的匿名交互,对需要登录/会话保持的应用尚未支持;二是验证 protocol-风格 交互是否在更大模型家族(不只是 Sonnet 和 Qwen)上仍然成立,这关系到结论的可外推性;三是研究自适应执行策略,即在运行时根据模型行为(如检测到 full-rewrite 模式)动态选择协议。基于论文结果,我认为可延伸的方向还有:(1) 把验收测试生成从 LLM 想象用户角色扩展为可对接产品规格文档、用例库或用户访谈记录,减少对 LLM 创造性的依赖;(2) 引入 execution trace 重放机制,让失败报告携带'上一次能通过的代码版本'作为参考,降低修复时的全局回滚风险;(3) 把 TDDev 接入 CI 流水线,实现人类只参与需求审查和最终验收,中间全闭环;(4) 探索更细粒度的协议,如'feature-grouped incremental'(按功能耦合度分批)或'criticality-first'(按安全/数据完整性优先级排序);(5) 引入 human-in-the-loop 的最小化触发机制,在修复轮连续 2 轮无改进时主动请求人工干预,而不是继续空转 token。
复现评估
复现评估整体偏正面。作者明确在 Data Availability 部分给出代码与数据存档地址(Zenodo DOI:10.5281/zenodo.19251377),框架、实验数据、评估 fixture 全部开源。代码层面,TDDev 的三大模块(测试生成、浏览器验证、失败翻译)以模块化设计呈现,算法 1-3 是可以直接照搬的伪代码,MCP 服务器接口也是标准化的。实验配置层面,论文给出了完整的 2 代理 × 2 模型 × 2 基准矩阵(表 2),所有 LLM 设置为 temperature=0 和最大上下文,WebGen-Bench 固定 seed=42 抽 50 例、ArtifactsBench 同样方式抽 100 例,可直接复用。算力门槛较低:MacBook Pro M-series + 32GB RAM 即可运行,无需 GPU 集群,主要消耗是 API 调用 token。复现难度中等:需要 API key(Anthropic + OpenRouter)、能跑 Playwright 的 Node/Python 环境、能调 Claude Agent SDK 和 OpenCode,以及对 LLM 工具调用链的基本调试能力。潜在风险是不同 API 版本下 Sonnet 4.6 的行为可能漂移、OpenCode 版本迭代可能影响工具接口、Playwright 与 Chromium 的版本组合会影响 selector 解析。建议复现时优先验证 RQ1 模块可靠性(只需 fixture 应用,不需要跑完整实验),确认测试代理准确率和测试生成覆盖率与论文一致后再展开 RQ2-4。
论文图表
交互验证算法的伪代码:输入测试用例 $c=\langle \text{feature, steps, expected} \rangle$、应用 URL $u$、LLM $M$、最大迭代次数 $T$;循环读取渲染页面 $o_t$、查询 LLM 决定 Playwright 动作或 verdict;每步把 (动作, 结果) 追加到轨迹 $\tau$,返回 (verdict $v$, trajectory $\tau$, failure report $f$)。
阶段二的核心算法,定义了测试代理的完整决策循环,是理解 Interactive Validation 模块的直接入口。
Whole-Project 协议伪代码:先 ImplementAll 一次性生成应用,再进入最多 $K$ 轮的 deploy-test-repair 循环(每轮跑全部测试),若全通过则提前返回,否则 ClassifyFailures 后让代理修复整个应用。
中度强制协议的算法定义,与 Algorithm 3 的 Incremental 形成对照。
Incremental 协议伪代码:维护一个回归套件 $P$ 和当前应用 $S$,逐个处理测试用例 $c_i$;每轮 ImplementFeature、部署、跑 $P \cup \{c_i\}$、若全过则把 $c_i$ 加入 $P$ 进入下一个,否则修复后重试,最多 $K$ 轮。
高度强制协议的算法定义,是论文揭示'protocol-model 风格错配'现象的核心场景。