为长时运行的 Agent 设计有效的 Harness
来源: Anthropic Engineering | 作者: Justin Young | 日期: 2025-11-26 原文链接: https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents
一句话总结
当任务需要跨越多个上下文窗口时,仅靠 compaction 不足以让 agent 稳定推进;需要用”初始化 agent + 编码 agent”的两段式 harness,把长程任务显式拆解为可跨会话接力的增量工程。
速览
- 光有 compaction 不够——即使是 Opus 4.5 跑在 Claude Agent SDK 上循环执行,仅靠一句”做个 claude.ai 克隆”也做不出产品级 Web App
- 两种典型失败模式——要么想一把梭,导致功能半途而废;要么后期环视代码库看到”做过事”,就宣布完工
- 两段式 harness——首个会话用”初始化 agent”搭环境;后续会话用”编码 agent”做增量推进
- 三件交接物——
init.sh启动脚本、claude-progress.txt进度文件、初始 git commit,构成跨会话的”工作记忆” - 功能清单用 JSON 存——200+ 条端到端功能全部初始标记为 failing,JSON 相比 Markdown 更不容易被模型乱改
- 强指令防止改测试——用”删除或修改测试是不可接受的”这种措辞,阻止 agent 通过改测试来”通关”
- 每次只做一个功能——增量式推进是破解”一把梭”倾向的关键
- git commit + progress 文件双轨——既能记录状态,又能让 agent 用 git 回滚到可工作的版本
- 测试必须端到端——必须明确要求用浏览器自动化(如 Puppeteer MCP)像真人一样验证,单跑 curl 或单测不够
- 开场三步仪式——每次会话先跑
pwd、读 progress/git、读 feature list 选任务,是让 agent”进入状态”的标准动作
核心内容
问题的本质:跨会话的”轮班交接”难题
长时运行 agent 要像”轮班工程师”一样工作——每个新 agent 上任时对上一班一无所知。上下文窗口有限,复杂项目装不进一个窗口,就必须有一套机制在会话之间传递状态。
compaction 的不足:Claude Agent SDK 自带 compaction 能让单次会话跑更久,理论上可以无限接力。但实际上,就算是 Opus 4.5 跑在其上循环,只给”做个 claude.ai 克隆”这种高层 prompt,也做不到产品级。原因是 compaction 不能总把清晰的指令传给下一个 agent。
两种失败模式:
- 一把梭失败:agent 想一次做完整个应用,上下文耗尽时功能半途而废且无文档,下一个 agent 只能靠猜还原
- 过早宣告完工:后期 agent 环视代码库,看到”做了不少事”,就宣布任务完成
问题可分解为两部分——初始环境必须为所有功能奠定基础(让 agent 能逐个功能推进),以及每次会话结束时环境要”干净”(可合并到 main 那种:无重大 bug、有文档、下一人能直接开始)。
两段式 harness 架构
核心设计:初始化 agent(Initializer)+ 编码 agent(Coding),两者的系统 prompt、工具集、harness 完全相同,差别仅在于初始用户 prompt 不同。
- 初始化 agent:首个会话专用。职责是搭好
init.sh、claude-progress.txt、写初始 git commit - 编码 agent:后续每个会话。职责是做增量进展,并留下结构化更新
关键洞察:要让 agent 从空白上下文快速理解当前状态——claude-progress.txt + git 历史就是解药。这些做法的灵感来自观察高效软件工程师的日常工作方式。
环境管理:功能清单、增量推进、测试
功能清单(Feature list)
初始化 agent 会基于用户初始 prompt 写一份详尽的功能需求文件。claude.ai 克隆案例里列了 200+ 条功能,每条带有分类、描述、步骤、passes 字段。所有功能初始标记为 failing,让后续 agent 看到”完整功能长什么样”。
具体格式示例:
{
"category": "functional",
"description": "New chat button creates a fresh conversation",
"steps": [
"Navigate to main interface",
"Click the 'New Chat' button",
"Verify a new conversation is created",
"Check that chat area shows welcome state",
"Verify conversation appears in sidebar"
],
"passes": false
}
两条关键约束:
- 编码 agent 只能改
passes字段,不能增删其他内容 - 用强措辞 prompt:“删除或修改测试是不可接受的,因为这可能导致功能缺失或出 bug”
格式选型:最终选定 JSON 而不是 Markdown——模型对 JSON 更谨慎,不容易不恰当地覆盖或改写。
增量式推进(Incremental progress)
每次只做一个功能。这是破解”一把梭”的核心。
干净状态的要求:修改代码后,环境要留在”可合并到 main”的状态。做法:
- 提交到 git,commit message 要有描述性
- 在 progress 文件里写阶段性总结
- 这样模型能用 git 回滚坏改动、恢复可工作版本
副作用:效率也提升了——agent 不再需要花时间猜”刚才发生了什么”或把基础应用重新跑通。
测试(Testing)
最后一个失败模式:Claude 倾向于在没有端到端测试的情况下就把功能标为完成。它会写单测、会用 curl 打本地服务器,但意识不到”功能没有真正端到端工作”。
解法:明确要求使用浏览器自动化工具,像真人一样测。在 Web App 场景下,这一要求让 Claude 表现大幅提升,能识别并修复单看代码发现不了的 bug。
当前限制:
- Claude 的视觉能力有限,浏览器自动化工具本身也有限制,并非所有 bug 都能被发现
- 具体例子:Claude 通过 Puppeteer MCP 看不到浏览器原生 alert 弹窗,依赖 alert 的功能往往更容易出 bug
会话开场的”三步仪式”
每个编码 agent 会话开头都要跑一套朴素但有效的流程:
- 运行
pwd确认工作目录(限定可编辑范围) - 读 git 日志和 progress 文件,获取最近工作状态
- 读功能清单,挑一个优先级最高、尚未完成的功能做
额外加一步:让初始化 agent 写一个能启动开发服务器的 init.sh。在实现新功能前先做一次基础端到端测试。
claude.ai 克隆里的实操:每次会话先启动本地开发服务器 → 用 Puppeteer MCP 新建对话、发消息、收回复 → 确认应用没坏 → 有坏就先修、再做新功能。反过来,如果 agent 不做验证直接写新代码,只会让问题雪上加霜。
典型开场消息:
[Assistant] I'll start by getting my bearings and understanding the current state of the project.
[Tool Use] <bash - pwd>
[Tool Use] <read - claude-progress.txt>
[Tool Use] <read - feature_list.json>
[Assistant] Let me check the git log to see recent work.
[Tool Use] <bash - git log --oneline -20>
[Assistant] Now let me check if there's an init.sh script to restart the servers.
<Starts the development server>
[Assistant] Excellent! Now let me navigate to the application and verify that some fundamental features are still working.
<Tests basic functionality>
[Assistant] Based on my verification testing, I can see that the fundamental functionality is working well...
<Starts work on a new feature>
失败模式 × 对策总表
| 问题 | 初始化 Agent 怎么做 | 编码 Agent 怎么做 |
|---|---|---|
| 过早宣布整个项目完工 | 基于输入 spec,生成结构化 JSON 功能清单 | 会话开始读清单,选一个功能开始做 |
| 环境里留下 bug 或未记录的进度 | 写初始 git repo + progress notes | 开场读 progress + git log,跑基础测试抓未记录 bug;结束时写 commit + progress 更新 |
| 过早将功能标为 done | 建功能清单 | 自我验证所有功能,测试通过后才标 passing |
| 搞不清如何启动应用 | 写 init.sh 启动开发服务器 | 会话开始时读 init.sh |
未来方向
单 agent vs 多 agent 架构仍是开放问题。文章推测:像测试 agent、QA agent、代码清理 agent 这样的专用 agent,可能在软件开发生命周期的子任务上表现更好,但尚未定论。
领域泛化。当前 demo 针对全栈 Web App 开发优化。未来方向是推广到科研、金融建模等其他长程 agent 任务——这些经验的一部分或全部很可能可以迁移。
名言金句
- “Imagine a software project staffed by engineers working in shifts, where each new engineer arrives with no memory of what happened on the previous shift.”(把长时运行 agent 比作”轮班工程师”——每个新工程师上任都没有上一班的记忆。)
- “Compaction isn’t sufficient.”(即使前沿模型 + 循环 + compaction 也解决不了长程任务的状态接力问题。)
- “It is unacceptable to remove or edit tests because this could lead to missing or buggy functionality.”(用强措辞阻止 agent 通过改测试”通关”。)
- “Inspiration for these practices came from knowing what effective software engineers do every day.”(这些做法的灵感来自观察高效工程师的日常工作方式。)
可行建议(给 Agent 构建者)
- 首个会话用不同的 prompt——专门搭环境,不要混在编码流程里
- 用 JSON 存功能清单——模型比对 Markdown 更谨慎,不容易乱改
- 把所有功能初始标为 failing——让后续 agent 看到完整”目标形态”
- 每次只做一个功能——拒绝一把梭
- git commit + progress 文件双轨记录——既能让 agent 回滚,又能给下一个 agent 清晰上下文
- 提供浏览器自动化测试工具——让 agent 能像真人一样端到端验证
- 每次开场跑三步仪式:
pwd→ 读 progress/git → 选功能 - 写
init.sh自动化启动——省下每次摸索启动命令的 token - 在实现新功能前先做基础 e2e 测试——抓未记录的 bug,避免在坏掉的地基上继续盖房子
- 用强措辞保护关键约束——比如”不可接受删除测试”
资源清单
- Claude Agent SDK 文档
- 配套 quickstart:autonomous-coding
- Claude 4 prompting 指南——多上下文窗口工作流最佳实践
- Puppeteer MCP server(用于浏览器自动化测试)
