作者 Prithvi Rajasekaran 是我们 Labs 团队的成员。

过去几个月里,我一直在研究两个相互关联的问题:让 Claude 产出高质量的前端设计,以及让它在没有人工干预的情况下构建完整应用。这项工作源于我们早期在前端设计 skill长时间运行的 coding agent harness 上的努力。在这些工作中,我和同事们通过 prompt engineering 和 harness design,让 Claude 的表现显著超过 baseline,但两者最终都遇到了天花板。

为了突破这些限制,我寻找能够横跨两个相当不同领域的新型 AI engineering 方法:一个领域由主观品味定义,另一个领域由可验证的正确性和可用性定义。受到 Generative Adversarial Networks(GANs)的启发,我设计了一个包含 generator agent 和 evaluator agent 的多 agent 结构。构建一个能够可靠地、并且带有品味地给输出打分的 evaluator,意味着首先要开发一组标准,把“这个设计好吗?”这类主观判断转化为具体、可评分的术语。

随后,我把这些技术应用到长时间运行的自主编码中,并沿用了我们早期 harness 工作中的两个经验:把构建过程分解成可处理的块,以及使用结构化 artifact 在 session 之间交接上下文。最终结果是一个三 agent 架构:planner、generator 和 evaluator,它能在持续数小时的自主编码 session 中产出丰富的全栈应用。

我们此前已经展示过,harness design 会对长时间运行的 agentic coding 效果产生重大影响。在早期的一个实验中,我们使用 initializer agent 将产品规格拆解成任务列表,再使用 coding agent 逐个 feature 实现任务,然后交接 artifact 以便在 session 之间携带上下文。更广泛的开发者社区也收敛到了类似洞察,例如 “Ralph Wiggum” 方法使用 hooks 或 scripts 让 agent 保持连续迭代循环。

但一些问题仍然持续存在。对于更复杂的任务,agent 仍然会随着时间推移逐渐偏离轨道。在分解这个问题时,我们观察到 agent 执行这类任务时有两种常见失败模式。

第一,随着上下文窗口填满,模型在长任务上往往会失去连贯性(见我们关于上下文工程的文章)。一些模型还会表现出“context anxiety”,即当它们接近自己认为的上下文限制时,会开始过早收尾工作。Context resets,也就是完全清空上下文窗口并启动一个新的 agent,再结合结构化 handoff 把前一个 agent 的状态和下一步传过去,可以解决这两个问题。

这不同于 compaction。Compaction 会就地总结对话的早期部分,让同一个 agent 在缩短的历史上继续工作。虽然 compaction 保留了连续性,但它不会给 agent 一个干净的起点,这意味着 context anxiety 仍然可能持续。Reset 提供了一个干净的起点,代价是 handoff artifact 必须有足够状态,让下一个 agent 能干净地接上工作。在我们早期测试中,我们发现 Claude Sonnet 4.5 表现出足够强的 context anxiety,单靠 compaction 不足以带来强大的长任务表现,因此 context resets 成了 harness design 的必要部分。这解决了核心问题,但也给每次 harness 运行增加了编排复杂度、token 开销和延迟。

第二个我们此前没有处理的问题,是自我评估。当被要求评估自己产出的工作时,agent 往往会自信地夸赞这些工作,即使在人类观察者看来,质量显然平庸。这个问题在设计这类主观任务上尤其明显,因为它没有等价于可验证软件测试的二元检查。一个布局感觉精致还是普通,是一种判断,而 agent 在给自己工作打分时会可靠地偏向积极。

然而,即使在那些拥有可验证结果的任务中,agent 有时仍然会表现出糟糕判断,从而妨碍它完成任务。把执行工作的 agent 和评判工作的 agent 分开,被证明是解决这个问题的强力杠杆。这种分离本身不会立刻消除宽松倾向;evaluator 仍然是一个 LLM,也倾向于对 LLM 生成的输出慷慨。但调优一个独立 evaluator 让它保持怀疑,事实证明比让 generator 批判自己的工作容易得多。一旦外部反馈存在,generator 就有了具体可迭代的对象。

我从前端设计开始实验,因为自我评估问题在那里最明显。如果没有任何干预,Claude 通常会倾向于安全、可预测的布局,这些布局技术上可用,但视觉上并不突出。

两个洞察塑造了我为前端设计构建的 harness。第一,尽管美学无法完全还原为一个分数,并且个人品味总会有所不同,但可以通过编码设计原则和偏好的评分标准来改善它。“这个设计漂亮吗?”很难被一致回答,但“这是否符合我们的优秀设计原则?”给了 Claude 一个具体的评分对象。第二,通过把前端生成和前端评分分开,我们可以创建一个反馈循环,推动 generator 走向更强的输出。

基于这个想法,我写了四条评分标准,并在 prompt 中同时提供给 generator 和 evaluator agents:

  • 设计质量: 这个设计感觉像一个连贯整体,而不是零件集合吗?这里的强工作意味着颜色、排版、布局、图像和其他细节共同创造出独特的情绪和身份。
  • 原创性: 是否有自定义决策的证据,还是模板布局、库默认值和 AI 生成模式?人类设计师应该能看出有意为之的创意选择。未经修改的 stock components,或白色卡片上紫色渐变这类 AI 生成的明显痕迹,在这里会失败。
  • 工艺: 技术执行:排版层级、间距一致性、色彩和谐、对比度比例。这是能力检查,而不是创造力检查。大多数合理实现默认都能做得不错;失败意味着基础破损。
  • 功能性: 独立于美学的可用性。用户能理解界面做什么、找到主要操作,并在不猜测的情况下完成任务吗?

我强调设计质量和原创性,超过工艺和功能性。Claude 默认已经在工艺和功能性上表现良好,因为所需的技术能力往往是模型自然具备的。但在设计和原创性上,Claude 经常产出至多只能算平淡的结果。这些标准明确惩罚高度通用的“AI slop”模式,并通过更高权重的设计和原创性推动模型承担更多审美风险。

我使用带有详细分数拆解的 few-shot examples 来校准 evaluator。这确保 evaluator 的判断与我的偏好一致,并减少迭代之间的分数漂移。

我把循环构建在 Claude Agent SDK 上,这让编排保持简单。Generator agent 首先基于用户 prompt 创建 HTML/CSS/JS 前端。我给 evaluator 配备了 Playwright MCP,让它能够在给每个标准打分并写出详细 critique 之前,直接与实时页面交互。实践中,evaluator 会自行导航页面,截图并仔细研究实现,然后产出评估。这个反馈会作为下一次迭代的输入流回 generator。每次生成我会运行 5 到 15 轮迭代,每轮通常都会推动 generator 朝更有辨识度的方向前进,因为它会回应 evaluator 的 critique。由于 evaluator 会主动导航页面,而不是给静态截图打分,每个循环都会花费真实的 wall-clock time。完整运行最长会延伸到四小时。我还指示 generator 在每次评估后做出战略决策:如果分数趋势良好,就 refine 当前方向;如果方法不起作用,就 pivot 到完全不同的美学方向。

在多次运行中,evaluator 的评估分数会在迭代中提高,随后趋于平台期,但仍然留有提升空间。一些生成会逐步 refinement。另一些则会在迭代之间发生剧烈的审美转向。

标准的措辞会以我没有完全预料到的方式引导 generator。包含“最好的设计是 museum quality”这样的短语,会把设计推向某种特定的视觉收敛,说明与标准相关的 prompting 直接塑造了输出的性格。

虽然分数通常会随迭代提高,但模式并不总是干净地线性上升。后期实现整体上往往更好,但我经常看到自己更喜欢中间某轮而不是最后一轮。实现复杂度也倾向于随着轮次增加而提高,generator 会为了回应 evaluator 的反馈去追求更有野心的方案。即使在第一轮,输出也明显好于完全没有 prompting 的 baseline,这说明这些标准及其相关语言本身,在任何 evaluator feedback 进一步 refinement 之前,就已经把模型从通用默认值中推开。

一个值得注意的例子是,我 prompt 模型为一家荷兰艺术博物馆创建网站。到第九轮时,它产出了一个干净的、深色主题的虚构博物馆 landing page。页面在视觉上很精致,但大体符合我的预期。然后,在第十轮,它完全废弃了这个方向,把网站重新想象成一种空间体验:一个用 CSS perspective 渲染、有棋盘格地板的 3D 房间,艺术品以自由形式挂在墙上,并使用基于门廊的导航在展厅之间移动,而不是滚动或点击。这是我以前没有从单次生成中看到过的那种创意跃迁。

带着这些发现,我把这种受 GAN 启发的模式应用到全栈开发。Generator-evaluator 循环自然映射到软件开发生命周期,其中 code review 和 QA 扮演着与设计 evaluator 相同的结构性角色。

在我们早期的长时间运行 harness 中,我们已经通过 initializer agent、一次处理一个 feature 的 coding agent,以及 session 之间的 context resets,解决了多 session 编码的连贯性问题。Context resets 是一个关键 unlock:harness 使用 Sonnet 4.5,而它表现出了前面提到的“context anxiety”倾向。创建一个能在 context resets 之间良好运作的 harness,是让模型保持任务状态的关键。Opus 4.5 基本上自行移除了这种行为,所以我能够从这个 harness 中完全删掉 context resets。Agents 在整个构建过程中作为一个连续 session 运行,并由 Claude Agent SDK 的自动 compaction 沿途处理上下文增长。

在这项工作中,我基于原始 harness 的基础构建了一个三 agent 系统,每个 agent 都针对我在此前运行中观察到的一个特定缺口。系统包含以下 agent personas:

Planner: 我们之前的长时间运行 harness 要求用户预先提供详细 spec。我想自动化这一步,所以创建了一个 planner agent,它接受一个简单的 1 到 4 句 prompt,并把它扩展成完整产品 spec。我 prompt 它在 scope 上保持有野心,并专注于产品上下文和高层技术设计,而不是详细技术实现。这样强调的原因是,我担心如果 planner 试图预先指定细粒度技术细节并出错,spec 中的错误会级联到下游实现中。更聪明的做法似乎是约束 agents 要产出的 deliverables,并让它们在工作中自己找路径。我还要求 planner 寻找机会把 AI features 编织进产品 spec。(见底部 Appendix 中的例子。)

Generator: 早期 harness 中一次处理一个 feature 的方法,在 scope 管理上表现良好。我在这里应用了类似模型,指示 generator 以 sprints 工作,每次从 spec 中接一个 feature。每个 sprint 使用 React、Vite、FastAPI 和 SQLite(后来是 PostgreSQL)stack 实现 app,并且 generator 被要求在每个 sprint 结束时自评工作,然后再交给 QA。它也有 git 用于版本控制。

Evaluator: 早期 harness 产出的应用通常看起来令人印象深刻,但你实际尝试使用时仍然有真实 bug。为了捕捉这些问题,evaluator 使用 Playwright MCP 像用户一样点击运行中的应用,测试 UI features、API endpoints 和 database states。然后,它根据发现的 bugs 和一组仿照前端实验的标准为每个 sprint 打分。这里的标准被改造为覆盖产品深度、功能性、视觉设计和代码质量。每个标准都有硬阈值,如果任何一个低于阈值,sprint 就失败,generator 会得到关于问题所在的详细反馈。

在每个 sprint 之前,generator 和 evaluator 会协商一份 sprint contract:在写任何代码之前,先就这块工作怎样算“done”达成一致。这存在的原因是产品 spec 有意保持高层,我希望有一步能弥合 user stories 和可测试实现之间的差距。Generator 提出它要构建什么,以及如何验证成功;evaluator 审查该 proposal,确保 generator 正在构建正确的东西。两者迭代,直到达成一致。

沟通通过文件处理:一个 agent 会写文件,另一个 agent 会读取它,并在该文件内或在一个新文件中回应,前一个 agent 再依次读取。随后 generator 会根据达成一致的 contract 构建,再把工作交给 QA。这让工作忠于 spec,同时避免过早过度指定实现。

对于这个 harness 的第一版,我使用 Claude Opus 4.5,同时把用户 prompts 跑在完整 harness 和单 agent 系统上做对比。我使用 Opus 4.5,是因为这是我开始这些实验时我们最好的 coding model。

我写了以下 prompt 来生成一个 retro video game maker:

Create a 2D retro game maker with features including a level editor, sprite editor, entity behaviors, and a playable test mode.

下表展示了 harness 类型、运行时长和总成本。

Harness Duration Cost
Solo 20 min $9
Full harness 6 hr $200

Harness 贵了 20 倍以上,但输出质量差异立刻显现。

我期待的是一个界面,在里面我可以构建一个关卡及其组成部分(sprites、entities、tile layout),然后点击 play 真正玩这个关卡。我先打开 solo run 的输出,初始应用看起来符合这些预期。

然而,当我点击探索时,问题开始出现。布局浪费空间,固定高度面板让 viewport 大部分留空。工作流很僵硬。尝试填充关卡时,它提示我先创建 sprites 和 entities,但 UI 中没有任何东西引导我走向这个顺序。更关键的是,实际游戏坏了。我的 entities 出现在屏幕上,但没有任何东西响应输入。深入代码后发现,entity definitions 和 game runtime 之间的 wiring 是坏的,而界面上没有任何地方提示问题在哪里。

Solo harness 创建的应用初始界面

打开 solo harness 创建的应用时看到的初始屏幕。

评估完 solo run 后,我把注意力转向 harness run。这次运行从同一个一句话 prompt 开始,但 planner 步骤把这个 prompt 扩展成了分布在十个 sprints 中的 16-feature spec。它远远超出了 solo run 的尝试。除了核心 editors 和 play mode,spec 还要求 sprite animation system、behavior templates、sound effects and music、AI-assisted sprite generator and level designer,以及带 shareable links 的 game export。我给 planner 访问我们的前端设计 skill 的权限,它读取并使用该 skill 为 app 创建视觉设计语言,作为 spec 的一部分。对于每个 sprint,generator 和 evaluator 会协商一份 contract,定义该 sprint 的具体实现细节,以及用于验证完成情况的可测试行为。

这个 app 立即展现出比 solo run 更高的 polish 和 smoothness。Canvas 使用了完整 viewport,面板尺寸合理,界面具有一致的视觉身份,并且跟随了 spec 中的设计方向。Solo run 中看到的一些笨拙之处仍然存在:工作流仍然没有清楚说明你应该先构建 sprites 和 entities,再尝试填充关卡,我不得不自己摸索出来。这看起来像基础模型产品直觉上的缺口,而不是 harness 被设计来解决的问题,不过它确实暗示了 harness 内部有一个可以通过 targeted iteration 进一步提升输出质量的位置。

使用各个 editors 时,新 run 相比 solo 的优势变得更明显。Sprite editor 更丰富、功能更完整,tool palettes 更干净,color picker 更好,zoom controls 也更可用。

因为我要求 planner 把 AI features 编织进 spec,app 还内置了 Claude integration,让我可以通过 prompting 生成游戏的不同部分。这显著加快了工作流。

完整 harness 构建应用中的新游戏创建界面

初始屏幕:在完整 harness 构建的 app 中创建新游戏。

最大的差异在 play mode。我真的能够移动 entity 并玩这个游戏。物理效果还有一些粗糙边缘:我的角色跳到平台上,但最终和平台重叠,这在直觉上感觉不对;但核心东西是可用的,而 solo run 没做到。移动一会儿后,我确实遇到了 AI 游戏关卡构造的一些限制。有一堵很大的墙我跳不过去,所以被卡住了。这说明 harness 可以处理一些常识改进和边界情况,进一步 refine 这个 app。

阅读 logs 时可以清楚看到,evaluator 让实现保持在 spec 轨道上。每个 sprint,它都会逐项走过 sprint contract 的测试标准,并通过 Playwright 操作运行中的应用,对任何偏离预期行为的东西 filing bugs。Contracts 很细粒度,仅 Sprint 3 就有 27 条覆盖 level editor 的标准,而且 evaluator 的发现足够具体,无需额外调查就可以采取行动。下表展示了 evaluator 识别出的一些问题示例:

Contract criterion Evaluator finding
Rectangle fill tool allows click-drag to fill a rectangular area with selected tile FAIL — Tool only places tiles at drag start/end points instead of filling the region. fillRectangle function exists but isn’t triggered properly on mouseUp.
User can select and delete placed entity spawn points FAIL — Delete key handler at LevelEditor.tsx:892 requires both selection and selectedEntityId to be set, but clicking an entity only sets selectedEntityId. Condition should be `selection
User can reorder animation frames via API FAILPUT /frames/reorder route defined after /{frame_id} routes. FastAPI matches ‘reorder’ as a frame_id integer and returns 422: “unable to parse string as an integer.”

让 evaluator 表现到这个水平需要不少工作。开箱即用时,Claude 是一个糟糕的 QA agent。在早期运行中,我看到它识别出真实问题,然后说服自己这些问题不算大事,并照样批准工作。它也倾向于做表面测试,而不是探查边界情况,所以更微妙的 bug 经常溜过去。调优循环是阅读 evaluator 的 logs,找到它的判断与我的判断不一致的例子,然后更新 QA prompt 来解决这些问题。在 evaluator 的评分方式变得让我觉得合理之前,这个开发循环经过了好几轮。即便如此,harness 输出也显示了模型 QA 能力的限制:小的布局问题、某些地方感觉不直观的交互,以及 evaluator 没有彻底测试到的更深层嵌套 features 中尚未发现的 bug。进一步调优显然还能捕捉更多验证空间。但与 solo run 相比,核心 feature 根本不可用,而这里的提升非常明显。

第一组 harness 结果令人鼓舞,但它也臃肿、缓慢且昂贵。合乎逻辑的下一步,是找到简化 harness 而不降低性能的方法。这部分是常识,也部分源于一个更一般的原则:harness 中的每个组件都编码了一个关于模型不能独立完成什么的假设,而这些假设值得压力测试,因为它们可能不正确,也因为它们会随着模型进步迅速过时。我们的博客文章 Building Effective Agents 把底层思想表述为“找到尽可能简单的解决方案,只在需要时增加复杂度”,这是任何维护 agent harness 的人都会持续遇到的模式。

在我第一次尝试简化时,我大幅削减了 harness,并尝试了一些有创意的新想法,但没能复制原始版本的表现。同时,也变得很难判断 harness design 中哪些部分真正 load-bearing,以及它们以什么方式 load-bearing。基于这次经验,我转向更有方法的做法:一次移除一个组件,并审查它对最终结果的影响。

当我进行这些迭代循环时,我们也发布了 Opus 4.6,这进一步激励我降低 harness 复杂度。有充分理由预期 4.6 需要的 scaffolding 会少于 4.5。来自我们的发布博客:“[Opus 4.6] plans more carefully, sustains agentic tasks for longer, can operate more reliably in larger codebases, and has better code review and debugging skills to catch its own mistakes.” 它在 long-context retrieval 上也有显著提升。这些都是 harness 原本被构建来补充的能力。

我首先完全移除了 sprint construct。Sprint structure 曾帮助把工作分解成块,让模型能连贯地工作。考虑到 Opus 4.6 的改进,有充分理由相信模型可以原生处理这项工作,而不需要这种分解。

我保留了 planner 和 evaluator,因为二者仍然继续增加明显价值。没有 planner,generator 会低估 scope:给它原始 prompt 时,它会不先 speccing 自己的工作就开始构建,并最终创建出比 planner 版本更少功能的应用。

移除 sprint construct 后,我把 evaluator 改为在运行结束时单次执行,而不是逐 sprint 打分。由于模型能力强了很多,这改变了 evaluator 在某些运行中有多 load-bearing,它的有用性取决于任务相对于模型本身可靠能力边界的位置。在 4.5 上,这个边界很近:我们的构建处于 generator solo 能做好的边缘,evaluator 在整个构建中捕捉到有意义的问题。在 4.6 上,模型的原始能力提升了,所以边界向外移动。过去需要 evaluator 检查才能连贯实现的任务,现在通常已经处在 generator 自己能处理好的范围内;对于这个边界内的任务,evaluator 变成了不必要的开销。但对于构建中仍然处在 generator 能力边缘的部分,evaluator 继续带来真实 lift。

实际含义是,evaluator 不是一个固定的 yes-or-no 决策。当任务超出当前模型可靠 solo 能力时,它值得付出成本。

在结构简化的同时,我还添加了 prompting 来改进 harness 如何把 AI features 构建进每个 app,具体来说,是让 generator 构建一个 proper agent,能通过 tools 驱动 app 自身功能。这经历了真实迭代,因为相关知识足够新,Claude 的训练数据覆盖得较薄。但通过足够调优,generator 能正确构建 agents。

为了测试更新后的 harness,我使用以下 prompt 生成一个 Digital Audio Workstation(DAW),也就是用于作曲、录音和混音的音乐制作程序:

Build a fully featured DAW in the browser using the Web Audio API.

这次运行仍然漫长且昂贵,大约 4 小时,token 成本为 124 美元。

大部分时间花在 builder 上。没有 Opus 4.5 曾经需要的 sprint decomposition,它仍然连贯运行了两个多小时。

Agent & Phase Duration Cost
Planner 4.7 min $0.46
Build (Round 1) 2 hr 7 min $71.08
QA (Round 1) 8.8 min $3.24
Build (Round 2) 1 hr 2 min $36.89
QA (Round 2) 6.8 min $3.09
Build (Round 3) 10.9 min $5.88
QA (Round 3) 9.6 min $4.06
Total V2 Harness 3 hr 50 min $124.70

和之前的 harness 一样,planner 把一行 prompt 扩展成了完整 spec。从 logs 中,我可以看到 generator model 很好地规划了 app 和 agent design,把 agent 接入,并在交给 QA 之前进行了测试。

话虽如此,QA agent 仍然捕捉到了真实缺口。在第一轮反馈中,它指出:

This is a strong app with excellent design fidelity, solid AI agent, and good backend. The main failure point is Feature Completeness — while the app looks impressive and the AI integration works well, several core DAW features are display-only without interactive depth: clips can’t be dragged/moved on the timeline, there are no instrument UI panels (synth knobs, drum pads), and no visual effect editors (EQ curves, compressor meters). These aren’t edge cases — they’re the core interactions that make a DAW usable, and the spec explicitly calls for them.

在第二轮反馈中,它再次捕捉到几个功能缺口:

Remaining gaps:

当 generator 独自工作时,仍然容易遗漏细节或 stub features,而 QA 仍然在捕捉这些最后一公里问题、让 generator 修复它们方面增加了价值。

基于 prompt,我期待的是一个程序,我可以在其中创建旋律、和声与鼓点,把它们编排成一首歌,并在过程中得到集成 agent 的帮助。下面的视频展示了结果。

这个 app 距离专业音乐制作程序还很远,agent 的歌曲创作能力显然还需要很多工作。此外,Claude 实际上听不见声音,这使得 QA feedback loop 在音乐品味方面效果较弱。

但最终 app 拥有一个功能性音乐制作程序的所有核心部分:在浏览器中运行的工作 arrangement view、mixer 和 transport。除此之外,我能够完全通过 prompting 拼出一小段歌曲片段:agent 设置 tempo 和 key,铺下 melody,构建 drum track,调整 mixer levels,并添加 reverb。歌曲创作的核心 primitives 已经存在,agent 也能够自主驱动它们,使用 tools 从头到尾创建一个简单 production。你也许会说它还不是 pitch-perfect,但它正在接近。

随着模型继续改进,我们大致可以预期它们能够工作更久,并处理更复杂的任务。在某些情况下,这意味着围绕模型的 scaffold 会随着时间变得不那么重要,开发者可以等待下一个模型,并看到某些问题自行解决。另一方面,模型越强,就越有空间开发能够完成复杂任务的 harness,而这些任务超出了模型 baseline 能力。

带着这个想法,这项工作中有一些经验值得继续保留。针对你正在构建的模型做实验,阅读它在真实问题上的 traces,并调优它的表现以达到期望结果,始终是好实践。在处理更复杂任务时,有时可以通过分解任务并把专门 agent 应用于问题的各个方面来获得提升空间。而当一个新模型到来时,通常也应该重新审视 harness,剥离那些不再对表现 load-bearing 的部分,并添加新的部分,以实现此前可能无法达到的更强能力。

从这项工作出发,我的信念是,随着模型改进,有趣的 harness 组合空间不会缩小。相反,它会移动,而 AI engineers 的有趣工作,就是不断找到下一个新颖组合。

特别感谢 Mike Krieger、Michael Agaby、Justin Young、Jeremy Hadfield、David Hershey、Julius Tarng、Xiaoyi Zhang、Barry Zhang、Orowa Sidker、Michael Tingley、Ibrahim Madha、Martina Long 和 Canyon Robbins 对这项工作的贡献。

也感谢 Jake Eaton、Alyssa Leonard 和 Stef Sequeira 帮助塑造这篇文章。