一声棒喝,本不立文字
偏要著録,已是二义

Anthropic · 工程博客

长时间运行的应用开发的 harness 设计

Harness design for long-running application development

二〇二六年五月八日 · 英文原文

Labs 成员 Prithvi Rajasekaran 研究提升 Claude frontend design 与 autonomous coding,构建 generator/evaluator 及 planner/generator/evaluator 多 agent harness,使用 criteria、Playwright MCP、artifacts 和 Claude Agent SDK。实验涵盖复古游戏制作器与 DAW,Opus 4.5/4.6 下运行约4小时,DAW 成本约124美元。

作者:Prithvi Rajasekaran,我们 Labs 团队成员。

过去几个月,我一直在研究两个相互关联的问题:让 Claude 产出高质量的 frontend 设计,以及让它在无人干预的情况下构建完整应用。这项工作源于我们早先在 frontend design skill 和 long-running coding agent harness 上的实践。当时我和同事通过 prompt engineering 和 harness design,把 Claude 的表现提升到远高于 baseline 的水平,但两者最终都遇到了上限。

为了突破这些限制,我开始寻找能同时适用于两个差异很大的领域的新型 AI engineering 方法:一个领域由主观品味定义,另一个领域由可验证的正确性和可用性定义。受 Generative Adversarial Networks(GANs)的启发,我设计了一种包含 generator agent 和 evaluator agent 的 multi-agent 结构。要构建一个能够可靠地、并且有品味地给输出打分的 evaluator,首先需要开发一组 criteria,把“这个设计好吗?”这类主观判断转化为具体、可评分的条目。

随后,我把这些技术应用到 long-running autonomous coding 上,并沿用了我们早期 harness 工作中的两条经验:把构建过程拆解成可处理的块,以及使用结构化 artifacts 在不同 session 之间交接上下文。最终结果是一个三 agent 架构——planner、generator 和 evaluator——能够在持续数小时的 autonomous coding session 中产出丰富的 full-stack 应用。

我们此前已经展示过,harness design 对 long running agentic coding 的有效性有显著影响。在早期实验中,我们使用 initializer agent 将产品 spec 分解成 task list,并使用 coding agent 一次实现一个 feature,然后交接 artifacts,以便在 session 之间传递上下文。更广泛的开发者社区也逐渐收敛到类似洞察,例如 “Ralph Wiggum” 方法使用 hooks 或 scripts 让 agents 保持持续迭代循环。

但一些问题仍然顽固存在。对于更复杂的任务,agent 仍然容易随着时间推移偏离轨道。在拆解这个问题时,我们观察到 agents 执行这类任务时常见的两种 failure mode。

第一,随着 context window 被填满,models 在长任务上往往会失去连贯性(见我们关于 context engineering 的文章)。一些 models 还会表现出 “context anxiety”:当它们接近自己认为的 context limit 时,会开始过早收尾。Context resets——完全清空 context window 并启动一个新的 agent,同时结合结构化 handoff 来携带前一个 agent 的状态和下一步——可以解决这两个问题。

这不同于 compaction,后者是在原位置总结对话早期部分,让同一个 agent 可以在缩短后的历史上继续工作。compaction 保留了连续性,但没有给 agent 一个干净的起点,这意味着 context anxiety 仍然可能持续存在。reset 提供了一个干净的起点,代价是 handoff artifact 必须包含足够状态,让下一个 agent 能顺畅接手工作。在我们早期测试中,我们发现 Claude Sonnet 4.5 表现出足够强的 context anxiety,以至于单靠 compaction 不足以实现强长任务表现,因此 context resets 成为 harness design 中的关键部分。这解决了核心问题,但也给每次 harness run 增加了 orchestration 复杂度、token 开销和延迟。

第二个我们此前没有处理的问题是 self-evaluation。当被要求评价自己产出的工作时,agents 往往会自信地称赞该工作——即使在人类观察者看来,质量显然平庸。这个问题在设计这类主观任务上尤其明显,因为它没有等价于可验证软件测试的二元检查。一个 layout 是显得精致还是普通,是一种判断,而 agents 在给自己的工作打分时会稳定地偏向正面。

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

我从 frontend design 开始实验,因为 self-evaluation 问题在这里最为明显。如果不加干预,Claude 通常会倾向于安全、可预期的 layouts:技术上可用,但视觉上并不突出。

我为 frontend design 构建 harness 时有两个关键洞察。第一,虽然 aesthetics 无法完全还原为一个分数——个人品味也总会存在差异——但可以通过编码设计原则和偏好的 grading criteria 来改善它。“这个设计美吗?”很难得到一致回答,但“这是否符合我们的优秀设计原则?”给了 Claude 一个具体的评分参照。第二,通过将 frontend generation 与 frontend grading 分离,我们可以创建一个 feedback loop,推动 generator 产出更强的结果。

基于这一思路,我写了四条 grading criteria,并在 prompt 中同时提供给 generator 和 evaluator agents:

我强调 design quality 和 originality,而不是 craft 和 functionality。Claude 默认在 craft 和 functionality 上已经得分较高,因为所需的技术能力对 model 来说往往自然具备。但在 design 和 originality 上,Claude 经常产出充其量只是乏味的结果。这些 criteria 明确惩罚高度泛化的 “AI slop” 模式,并通过更高权重强调 design 和 originality,推动 model 在 aesthetics 上承担更多风险。

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

我基于 Claude Agent SDK 构建这个 loop,使 orchestration 保持简单。generator agent 首先根据 user prompt 创建一个 HTML/CSS/JS frontend。我给 evaluator 提供了 Playwright MCP,让它能够在给每条 criterion 打分并写出详细 critique 之前,直接与 live page 交互。实践中,evaluator 会自行浏览页面、截图,并仔细研究实现,然后给出评估。这些反馈会作为下一轮迭代的输入流回 generator。每次生成我运行 5 到 15 次迭代,generator 通常会在每次迭代中根据 evaluator 的 critique 朝更有辨识度的方向推进。由于 evaluator 是主动浏览页面,而不是给静态截图打分,每个 cycle 都需要实际的 wall-clock time。完整运行最长可达四小时。我还指示 generator 在每次 evaluation 后做出战略决策:如果分数趋势良好,就细化当前方向;如果方法不奏效,就转向完全不同的 aesthetic。

在多次运行中,evaluator 的评估会随着迭代改善,然后进入平台期,但仍有提升空间。有些 generations 是渐进式细化。另一些则会在迭代之间发生明显的 aesthetic 转向。

criteria 的措辞以我未完全预料到的方式引导了 generator。包含 “the best designs are museum quality” 这样的短语,会把设计推向某种特定的视觉收敛,说明与 criteria 相关的 prompting 会直接塑造输出的特征。

虽然分数通常会随迭代提升,但这种模式并不总是干净的线性关系。后续实现整体上往往更好,但我经常看到自己更偏好中间某次迭代而不是最后一次。实现复杂度也往往会在多轮中增加,因为 generator 会响应 evaluator 的反馈,尝试更有野心的解决方案。即使在第一次迭代中,输出也明显好于完全没有 prompting 的 baseline,这说明 criteria 及其相关语言本身已经在任何 evaluator feedback 进一步细化之前,就把 model 从泛化默认值中拉了出来。

一个值得注意的例子是,我 prompt model 为一家荷兰艺术博物馆创建网站。到第九次迭代时,它已经为一个虚构博物馆产出了一个干净的暗色主题 landing page。页面在视觉上很精致,但基本符合我的预期。然后,在第十个 cycle,它完全推翻了原有方案,把网站重新想象为空间体验:一个用 CSS perspective 渲染的 3D room,带有棋盘格地板,艺术作品以自由形式的位置挂在墙上,并用门廊式导航在 gallery rooms 之间移动,而不是滚动或点击。这种创意跃迁,是我此前没有在 single-pass generation 中见过的。

有了这些发现后,我把这种受 GAN 启发的模式应用到 full-stack development。generator-evaluator loop 很自然地映射到 software development lifecycle,其中 code review 和 QA 扮演着与 design evaluator 相同的结构角色。

在我们早期的 long-running harness 中,我们已经通过 initializer agent、一次处理一个 feature 的 coding agent,以及 session 之间的 context resets,解决了多 session coding 的连贯性问题。Context resets 是一个关键突破:harness 使用 Sonnet 4.5,它表现出前文提到的 “context anxiety” 倾向。创建一个能在 context resets 之间良好工作的 harness,是让 model 保持在任务上的关键。Opus 4.5 基本上自行消除了这种行为,因此我能够在这个 harness 中完全去掉 context resets。agents 在整个 build 中以一个连续 session 运行,并由 Claude Agent SDK 的自动 compaction 处理过程中的 context 增长。

在这项工作中,我基于原始 harness 的基础构建了一个三 agent system,每个 agent 都解决我在先前运行中观察到的一个具体缺口。系统包含以下 agent personas:

Planner:我们此前的 long-running harness 要求用户预先提供详细 spec。我想自动化这一步,所以创建了一个 planner agent,它接收一个简单的 1-4 句 prompt,并将其扩展为完整的 product spec。我 prompt 它在 scope 上保持有野心,并聚焦于 product context 和 high level technical design,而不是详细的 technical implementation。这样强调是因为我担心,如果 planner 试图预先指定细粒度技术细节,并且其中有错,那么 spec 中的错误会级联到下游实现中。更明智的做法似乎是约束 agents 要产出的 deliverables,并让它们在工作过程中自己找出路径。我还要求 planner 找机会把 AI features 融入 product specs。(见底部 Appendix 中的示例。)

Generator:早期 harness 中 one-feature-at-a-time 的方法很适合 scope management。我在这里应用了类似模型,指示 generator 按 sprints 工作,每次从 spec 中取一个 feature。每个 sprint 使用 React、Vite、FastAPI 和 SQLite(后来是 PostgreSQL)stack 实现应用,并要求 generator 在每个 sprint 结束时先 self-evaluate 自己的工作,再交接给 QA。它也使用 git 做 version control。

Evaluator:早期 harness 生成的应用往往看起来不错,但实际使用时仍然有真实 bug。为了捕捉这些问题,evaluator 使用 Playwright MCP 像用户一样点击运行中的应用,测试 UI features、API endpoints 和 database states。然后,它会根据发现的 bugs,以及一组仿照 frontend 实验设计、并在这里改造为覆盖 product depth、functionality、visual design 和 code quality 的 criteria,给每个 sprint 打分。每个 criterion 都有一个硬阈值;只要有任何一项低于阈值,该 sprint 就失败,generator 会收到关于出错之处的详细反馈。在每个 sprint 之前,generator 和 evaluator 会协商一份 sprint contract:在写任何代码之前,就这一块工作的 “done” 具体意味着什么达成一致。之所以需要这样做,是因为 product spec 有意保持高层次,而我希望有一个步骤来弥合 user stories 与可测试实现之间的差距。generator 提出它将构建什么,以及如何验证成功;evaluator 审查该提案,以确保 generator 正在构建正确的东西。两者会迭代直到达成一致。

通信通过文件处理:一个 agent 写入文件,另一个 agent 读取文件,并在该文件中回应,或写入一个新文件供前一个 agent 继续读取。然后 generator 会基于已达成一致的 contract 进行构建,再把工作交给 QA。这让工作忠实于 spec,同时避免过早过度指定实现细节。

在这个 harness 的第一个版本中,我使用 Claude Opus 4.5,同时用同一组 user prompts 跑完整 harness 和一个 single-agent system 作比较。我使用 Opus 4.5,是因为在我开始这些实验时,这是我们最好的 coding model。

我写了以下 prompt 来生成一个复古视频游戏制作器:

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

harness 的成本高出 20 倍以上,但输出质量差异立刻就很明显。

我期望看到一个 interface,可以在其中构建关卡及其组成部分(sprites、entities、tile layout),然后点击 play 实际游玩该关卡。我先打开 solo run 的输出,初始应用看起来符合这些预期。

然而,随着我点击浏览,问题开始浮现。layout 浪费空间,固定高度面板让大部分 viewport 空着。workflow 很僵硬。尝试填充关卡时,它提示我先创建 sprites 和 entities,但 UI 中没有任何东西引导我按这个顺序操作。更重要的是,实际游戏坏了。我的 entities 出现在屏幕上,但没有任何东西响应输入。深入代码后发现,entity definitions 与 game runtime 之间的 wiring 是坏的,而且界面上没有任何迹象说明问题在哪里。

评估完 solo run 后,我把注意力转向 harness run。这次运行从同一个一句话 prompt 开始,但 planner 步骤把该 prompt 扩展成一个 16-feature spec,分布在十个 sprints 中。它远远超出了 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 提供了我们的 frontend design skill,它读取并使用该 skill,为应用创建了一套 visual design language,并把它纳入 spec。对于每个 sprint,generator 和 evaluator 都会协商一份 contract,定义该 sprint 的具体实现细节,以及用于验证完成度的可测试 behaviors。

这个应用一开始就比 solo run 更精致、更顺滑。canvas 使用了完整 viewport,panels 尺寸合理,interface 具有一致的 visual identity,并且与 spec 中的设计方向一致。solo run 中的一些笨拙之处仍然存在——workflow 依然没有明确说明你应该先构建 sprites 和 entities,再尝试填充关卡,我必须通过到处试探来弄明白。这更像是 base model 在 product intuition 上的缺口,而不是 harness 旨在解决的问题;不过它也提示了一个方向:在 harness 内进行有针对性的迭代,可能进一步提升输出质量。

在使用各个 editors 的过程中,新运行相比 solo 的优势更加明显。sprite editor 更丰富、功能更完整,tool palettes 更清晰,color picker 更好,zoom controls 也更可用。

因为我要求 planner 把 AI features 融入 specs,这个应用还内置了 Claude integration,让我可以通过 prompting 生成游戏的不同部分。这显著加快了 workflow。

最大的差异在 play mode。我实际上能够移动 entity 并游玩游戏。physics 仍有一些粗糙之处——我的角色跳上平台后最终与平台重叠,这在直觉上不对——但核心功能可用,而 solo run 没能做到。移动了一会儿后,我确实碰到了 AI game level construction 的一些限制。有一堵很大的墙,我无法跳过去,所以被卡住了。这说明 harness 还可以处理一些常识性改进和 edge cases,以进一步打磨应用。

阅读 logs 时可以清楚看到,evaluator 让 implementation 保持与 spec 对齐。每个 sprint 中,它都会逐项走查 sprint contract 的 test criteria,并通过 Playwright 操作运行中的应用,对任何偏离预期行为的地方提交 bugs。contracts 很细粒度——仅 Sprint 3 就有 27 条 criteria,覆盖 level editor——evaluator 的 findings 也足够具体,不需要额外调查即可采取行动。下表展示了我们的 evaluator 识别出的几个问题示例:

让 evaluator 达到这个水平需要不少工作。开箱即用时,Claude 是一个糟糕的 QA agent。在早期运行中,我看到它识别出真实问题,然后又说服自己这些问题没什么大不了,并批准工作。它也倾向于做表层测试,而不是探测 edge cases,因此更隐蔽的 bugs 经常漏掉。调优循环是阅读 evaluator 的 logs,找出它的判断与我的判断分歧的例子,并更新 QA 的 prompt 来解决这些问题。这个开发循环进行了好几轮,evaluator 的评分方式才达到我认为合理的程度。即便如此,harness 输出仍然显示出 model 的 QA 能力限制:小的 layout 问题、某些地方感觉不直观的 interactions,以及 evaluator 未彻底覆盖的更深层嵌套 features 中未发现的 bugs。显然,通过进一步调优还有更多 verification 空间可以获得。但与 solo run 相比——后者的应用核心功能根本不能工作——提升是显而易见的。

第一组 harness 结果令人鼓舞,但它也很庞大、缓慢且昂贵。合乎逻辑的下一步,是寻找在不降低性能的情况下简化 harness 的方法。这部分是常识,部分也来自一个更一般的原则:harness 中的每个 component 都编码了一个关于 model 不能独立完成什么的假设,而这些假设值得进行 stress testing;原因既在于它们可能是错的,也在于随着 models 改进,它们会很快过时。我们的博客文章 Building Effective Agents 将底层思路表述为“找到尽可能简单的解决方案,只在需要时增加复杂度”,这也是任何维护 agent harness 的人都会持续遇到的模式。

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

在经历这些迭代循环时,我们也发布了 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 结构此前帮助 model 将工作拆解为块,以便连贯地处理。考虑到 Opus 4.6 的改进,我们有充分理由相信 model 可以原生处理这项工作,而无需这种拆解。

我保留了 planner 和 evaluator,因为两者都继续提供明显价值。没有 planner 时,generator 会 under-scope:面对原始 prompt,它会在没有先制定 spec 的情况下开始构建,最终创建出的应用不如 planner 产出的那样功能丰富。

移除 sprint construct 后,我把 evaluator 改为在运行结束时进行 single pass,而不是按 sprint 打分。由于 model 能力强了很多,evaluator 在某些运行中是否 load-bearing 也随之变化;它是否有用,取决于任务相对于 model 能独立可靠完成范围的位置。在 4.5 上,这个边界很近:我们的 builds 位于 generator solo 能力的边缘,evaluator 能在整个 build 中捕捉有意义的问题。在 4.6 上,model 的原始能力提升,边界向外移动。过去需要 evaluator 检查才能连贯实现的任务,现在往往已经在 generator 能独立良好处理的范围内;对于这些边界内的任务,evaluator 就成了不必要的开销。但对于 build 中仍位于 generator 能力边缘的部分,evaluator 仍然带来真实提升。

实际含义是,evaluator 并不是一个固定的是或否决策。当任务超出当前 model solo 可靠完成的范围时,它就值得付出成本。

在结构简化的同时,我还增加了 prompting,以改进 harness 将 AI features 构建进每个应用的方式,特别是让 generator 构建一个真正的 agent,能够通过 tools 驱动应用自身功能。这需要真实迭代,因为相关知识足够新,Claude 的训练数据覆盖较薄。但经过足够调优后,generator 能够正确构建 agents。

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

这次运行仍然漫长且昂贵,耗时约 4 小时,token 成本约 $124。

大部分时间花在 builder 上,它在没有 Opus 4.5 所需的 sprint decomposition 的情况下,连贯运行了两个多小时。

与之前的 harness 一样,planner 把一行 prompt 扩展成完整 spec。从 logs 中可以看到,generator model 在规划应用和 agent design、接入 agent,以及交给 QA 前进行测试方面做得不错。

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

在第二轮反馈中,它再次捕捉到几个 functionality gaps:

如果完全放任 generator 自行工作,它仍然容易遗漏细节或用 stub features 顶替;QA 仍然能通过捕捉这些最后一公里问题,为 generator 修复提供价值。

基于 prompt,我期望得到一个程序,可以在其中创建 melodies、harmonies 和 drum patterns,把它们编排成一首歌,并在过程中获得集成 agent 的帮助。下面的视频展示了结果。

这个应用距离专业音乐制作程序还很远,agent 的 song composition 能力显然也还有很多工作要做。此外,Claude 实际上听不见,这让 QA feedback loop 在 musical taste 方面不那么有效。

但最终应用具备了一个功能性音乐制作程序的所有核心部分:在浏览器中运行的 working arrangement view、mixer 和 transport。不止如此,我还能够完全通过 prompting 组合出一段简短歌曲片段:agent 设置 tempo 和 key,铺设 melody,构建 drum track,调整 mixer levels,并添加 reverb。song composition 的核心 primitives 已经存在,agent 也可以自主驱动它们,用 tools 从头到尾创建一个简单作品。你可以说它还不是 pitch-perfect,但已经在接近了。

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

基于这一点,这项工作中有几条经验值得延续。针对你正在构建所依赖的 model 做实验、阅读它在真实问题上的 traces,并调优其表现以达成你想要的结果,这始终是好实践。在处理更复杂的任务时,有时可以通过拆解任务,并为问题的不同方面应用 specialized agents,获得额外空间。当新 model 到来时,通常也应该重新审视 harness,去除那些不再对性能 load-bearing 的部分,并添加新的部分,以获得此前可能无法实现的更强能力。

从这项工作中,我的信念是,随着 models 改进,有趣的 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 帮助打磨本文。

planner agent 生成的示例 plan。

译自 Anthropic · 工程博客 · 录于 二〇二六年五月八日