ClaudeClaude Code系列第 10 / 10 篇
让 Claude 连续干几个小时:Anthropic 的两套长任务 harness,和用 claude -p 搭的最小版实测
长任务难在每个新会话都不记得之前做过什么。整理 Anthropic 工程博客的两套 harness:初始化代理加功能清单、进度文件和 git;规划、生成、评审分工。本站用 claude -p 和沙箱搭了最小版实测:结构被严格遵守,但比一个会话做完多花 10 倍,最有用的是独立验收。
本站已实测,测试日期:2026-10-16。
这篇解决一个问题:任务大到一个会话做不完时,怎么让 Claude 一轮接一轮地做下去,而不是做到一半乱了套。
难点在于每个新会话都不记得之前发生过什么。Anthropic 工程博客上有两篇文章专门讲这件事,给了两套做法:
- 初始化代理加编码代理(2025 年 11 月):第一轮只打基础,写一份功能清单、一个进度文件、一个启动脚本,建好 git;之后每一轮读这些文件接着做,一轮只做一项,做完提交。
- 规划、生成、评审(2026 年 3 月):在前一套的基础上,加一个把一句话需求扩成完整规格的规划代理,再加一个专门挑毛病的评审代理,因为让 Claude 评自己做的东西,它总是说好。
本站按第一套搭了一个最小版,用的是 Claude Code 的 claude -p,不用另写程序;末尾加了第二套里的评审。和“一个会话从头做完”的对照组比了一次:同一份需求、同样的沙箱和权限、同一个模型。
结论先说:
- 结构管用。 10 轮编码各做一项,没有乱改功能清单,没有提前收工,每一轮都提交并留下记录。
- 但这个任务一个会话就做得完。 对照组 151 秒、0.33 美元,本站的 25 项验收全过;harness 用了约 20 分钟、3.33 美元,验收也是全过。任务在模型能力范围之内时,harness 主要是开销,这和第二篇文章的结论一致。
- 最值钱的是独立验收。 两轮找出 9 处需求没写到的边界问题;它也很难满意,修完一轮又挑出两处。
长任务为什么会失败
第一篇文章说,即使有自动压缩,在 Agent SDK 上循环跑 Opus 4.5,只给一句“做一个 claude.ai 的克隆”,也做不出能用的网页应用。失败有两种:
- 想一口气做完。 做到一半上下文用完了,下一个会话接手的是一个做了一半、没有记录的功能,只能猜前面发生了什么,再花大量时间把应用修回能跑的状态。
- 过早宣布完成。 做了一部分以后,后来的会话看看周围,觉得进展不少,就说做完了。
第二篇又补了两种:
- 上下文焦虑。 有的模型觉得上下文快满了,就开始提前收尾。这时压缩不够用,要彻底清空、开一个新会话,靠一份交接文件把状态带过去。文章说 Sonnet 4.5 这个毛病明显,Opus 4.5 基本没有了,所以第二套里把清空上下文这一步去掉了,交给 Agent SDK 的自动压缩。
- 自评太宽。 让 Claude 评价自己做的东西,它会很有把握地夸,哪怕在人看来质量明显一般。设计这类没有对错标准的任务尤其严重。
第一套:初始化代理和编码代理
两个“代理”其实是同一个 harness、同样的工具,只是第一轮的提示词不同。
**第一轮(初始化代理)**打好四样东西:
| 文件 | 作用 |
|---|---|
feature_list.json |
把需求展开成一条条端到端的功能,每条带验证步骤,全部标成 "passes": false。克隆 claude.ai 那次写了 200 多条 |
claude-progress.txt |
进度记录:每一轮做了什么、下一轮做什么 |
init.sh |
启动开发服务器、跑一遍基本测试的脚本,后面每一轮先跑它 |
| 第一次 git 提交 | 后面可以看历史,也可以把改坏的退回去 |
功能清单用 JSON 而不用 Markdown,原因是模型不太会去乱改、整个重写 JSON 文件。提示词里还用了很重的语气,大意是“删掉或改动功能条目是不可接受的”,编码代理只能改 passes 这一个字段。
**之后每一轮(编码代理)**按固定的步骤开头:看工作目录,读进度文件和 git 记录,读功能清单挑优先级最高的未完成项,跑 init.sh 确认应用没坏,然后才开始做新功能。一轮只做一项,做完用 git 提交、写进度。
文章把四种失败和对策列成了一张表,大意如下:
| 问题 | 初始化代理做什么 | 编码代理做什么 |
|---|---|---|
| 过早宣布整个项目完成 | 按需求写一份结构化的功能清单 | 每轮开头读清单,只挑一项来做 |
| 留下 bug 或没记录的进度 | 建 git 仓库和进度文件 | 开头读进度和 git 记录、跑一遍基本测试;结尾提交并写进度 |
| 没测好就标成完成 | 写功能清单 | 每项都自己验证,仔细测过才标“通过” |
| 每轮都要琢磨怎么跑起来 | 写 init.sh |
开头先读、先跑 init.sh |
测试这一条最关键。 文章说,不明确要求的话,Claude 会改完代码、跑跑单元测试或者用 curl 调一下接口,就当做完了,发现不了端到端其实不通。明确要求它用浏览器自动化、像真人用户一样测之后,效果好了很多。他们给的是 Puppeteer 的 MCP 服务器。局限也有:浏览器原生的弹窗它看不到,依赖弹窗的功能 bug 就多。
官方的示例代码在 claude-quickstarts 的 autonomous-coding:用 Agent SDK 写的,两份提示词、沙箱加一个 Bash 命令白名单的 hook,克隆 claude.ai 的规格要 200 条功能,README 提醒每轮要 5 到 15 分钟、做完要好几个小时。
第二套:规划、生成、评审
第二篇的作者先在前端设计上试:没有约束时,Claude 做出来的页面能用,但普通、可预测。他的办法是把“好不好看”变成可以打分的四条标准(整体感、原创性、做工、可用性),让生成和评审分开:生成代理做页面,评审代理用 Playwright 的 MCP 真的打开页面点来点去、截图,再按标准打分写意见,意见回给生成代理改。一次跑 5 到 15 轮,最长 4 个小时。分开之后评审也还是偏宽,但把一个独立的评审调教得挑剔,比让生成代理批评自己容易得多。
然后把这个结构搬到全栈开发上,变成三个代理:
| 代理 | 做什么 |
|---|---|
| 规划 | 把一到四句话的需求扩成完整的产品规格。要求它在范围上大胆,只写产品和高层设计,不写实现细节,免得规格里的技术错误一路传下去 |
| 生成 | 按规格一个冲刺一个冲刺地做,每次一项功能,交给评审之前先自查 |
| 评审 | 用 Playwright 像用户一样操作运行中的应用,测界面、接口和数据库;按产品深度、功能、视觉设计、代码质量打分,每项有及格线,一项不及格这一轮就打回 |
每个冲刺开始前,生成和评审先谈一份“冲刺合同”:这一块做到什么算完成、怎么验证,谈妥了才写代码。两个代理之间靠文件沟通。评审找出的问题很具体,比如“矩形填充只在拖动的起点和终点放了砖块,fillRectangle 函数存在但没有在 mouseUp 时触发”。
花费和效果,文章给了两组数(都是 Anthropic 自己跑的):
| 做什么 | 怎么跑 | 时长 | 花费 |
|---|---|---|---|
| 复古游戏编辑器(Opus 4.5) | 一个代理直接做 | 20 分钟 | 9 美元 |
| 同上 | 完整的三代理 harness | 6 小时 | 200 美元 |
| 浏览器里的音乐制作软件(Opus 4.6,简化后的 harness) | 规划 4.7 分钟,三轮生成加评审 | 3 小时 50 分 | 124.70 美元 |
贵了 20 多倍,但单代理做的游戏编辑器核心功能是坏的:角色出现在画面上,按键没有反应;三代理那次能真的玩起来。
评审调教起来很费劲。 作者说开箱即用的 Claude 是个差劲的测试员:找到了真问题,又说服自己“问题不大”,照样放行;测得也浅,边界情况不碰。他的办法是读评审的日志,找出它和自己判断不一样的地方,改评审的提示词,来回好几轮。
模型变强以后,harness 要做减法。 换到 Opus 4.6 后,作者一次去掉一个组件看影响:冲刺可以整个去掉,模型自己能连续做两个多小时不乱;规划要留着,没有它生成代理会把范围做小;评审改成最后一次性验收。评审值不值,取决于任务在不在模型自己能可靠做好的范围之内:在范围内,评审是多余的开销;在边缘上,它仍然能抓到真问题。他引用了《Building effective agents》里的一句话作为原则:先找最简单的办法,需要时才加复杂度。harness 里的每个组件,都是在假设模型自己做不到某件事,这些假设要经常拿出来检验。
本站实测:用 claude -p 搭一个最小版
官方示例要写 Python、装 Agent SDK 和浏览器自动化。用 Claude Code 的人其实不用另写程序:claude -p 本身就是一个完整的智能体,每次调用是一个全新的会话,正好对应“每一轮都不记得之前的事”。harness 就是一个 shell 脚本,按顺序调几次 claude -p,每次给不同的提示词。
| 实测环境 | |
|---|---|
| 日期 | 2026-10-16 |
| 版本 | Claude Code 2.1.300,Ubuntu 24.04 |
| 模型 | Sonnet 5.5(--model sonnet),两组用同一个 |
| 连接方式 | 通过酷站AI 中转站,不是直连官方 |
| 任务 | 一个命令行记账工具:8 条命令(记账、列表、删除、撤销、月度汇总、预算、导出 CSV、导入 CSV)加错误处理,只用 Python 标准库。需求写在一页 app_spec.md 里 |
| 对照组 | 同一份需求、同样的沙箱和权限,一个 claude -p 会话做完 |
| 验收 | 本站另写的 25 项检查,按需求逐条跑命令,不给被测的会话看 |
文件
h40-harness/
├── app_spec.md 需求,两组都拿这一份
├── CLAUDE.md 项目约定,两组都有
├── sandbox.json 沙箱设置
├── harness.sh 长任务 harness
├── solo.sh 对照组:一个会话做完
└── prompts/
├── initializer.md 第一轮
├── coding.md 之后每一轮
├── qa.md 独立验收
├── fix.md 按验收报告修
└── solo.md 对照组
两道边界:沙箱和 dontAsk
无人值守地跑,没有人点确认,所以要先把能做的事框住。官方示例用的是沙箱加一个 Bash 命令白名单的 hook;C 编译器那个实验用的是 --dangerously-skip-permissions,但整个跑在容器里(见《智能体该搭成什么结构》)。我们在一台共用的服务器上跑,用的是 Claude Code 自带的 Bash 沙箱:
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false
}
}
三项的意思:打开沙箱,命令只能写项目目录、不能联网;沙箱起不来就不跑,而不是不带沙箱照跑;不允许命令申请跑到沙箱外面。再加上 --permission-mode dontAsk:文件工具只放行项目目录里的读写,其他要确认的一律直接拒绝,不会停下来等人。沙箱怎么配、挡得住什么,见《Claude Code 沙箱怎么配》。
有一个插曲:写这个脚本时,本站用的 Claude Code 开着 auto 模式。第一版脚本没开沙箱,只靠一份允许清单,里面有 Bash(python3 *),等于能执行任意代码。auto 模式的分类器把它拦了下来,理由是 Create Unsafe Agents:它不允许创建既没有沙箱、又没有逐个审批的智能体循环。加上沙箱以后才放行。
脚本
#!/usr/bin/env bash
# 最小的长任务 harness:每一轮都是一个全新的 claude -p 会话,前后两轮只靠目录里的文件交接。
# 用法:./harness.sh 项目目录 [编码轮数上限]
set -uo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
PROJECT=$(cd "$1" && pwd)
MAX=${2:-14}
LOGS="$HERE/logs/$(basename "$PROJECT")"
mkdir -p "$LOGS"
cd "$PROJECT"
OPTS=(--settings "$HERE/sandbox.json" --permission-mode dontAsk
--allowedTools Read Glob Grep "Write(./**)" "Edit(./**)" --model sonnet --max-budget-usd 2 --output-format json)
n=0
session() { # 参数:提示词的名字。每次都是新会话,不接着上一个。
n=$((n + 1))
local log="$LOGS/$(printf %02d $n)-$1.json"
claude -p "$(cat "$HERE/prompts/$1.md")" "${OPTS[@]}" > "$log"
jq -r --arg s "$1" '"\($s):\(.num_turns) 轮,\(.duration_ms / 1000 | floor) 秒,\(.total_cost_usd) 美元"' "$log"
}
left() { jq '[.[] | select(.passes == false)] | length' feature_list.json 2>/dev/null || echo "?"; }
passed() { tail -1 qa-report.md 2>/dev/null | grep -q "结论:通过"; }
session initializer
for ((i = 1; i <= MAX; i++)); do
[ "$(left)" = "0" ] && break
echo "还剩 $(left) 项"
session coding
done
session qa
if ! passed; then # 验收不通过:修一轮,再验收一次
session fix
session qa
fi
echo "结束:feature_list.json 里还剩 $(left) 项没通过;$(tail -1 qa-report.md)"
几个设计:每一轮都不带 --continue,是真正的新会话;--max-budget-usd 2 给每一轮设花费上限;--output-format json 让每一轮的轮数、耗时、花费都记进日志;循环靠 jq 数功能清单里还剩几项,全部通过就停,最多 14 轮。
提示词
初始化的提示词让它做五件事:读需求,写 feature_list.json(拆成 8 到 12 项能在命令行里验证的功能,每项带验证步骤,全部 "passes": false),写一条命令跑完全部测试的 init.sh,搭项目骨架,提交一次并写 claude-progress.txt。并且说明这一轮不实现功能,清单以后只能改 passes。
之后每一轮用的是这一份,照着官方那份的结构缩写的:
你在接着做一个长任务。这是一个全新的会话,你不记得之前做过什么。
第一步,摸清现状:读 claude-progress.txt;看 git log --oneline -10;读 feature_list.json,
数一数还有几项没通过;跑 ./init.sh,确认已经通过的功能没有坏。坏了先修,修好之前不做新功能。
第二步,只挑一项:feature_list.json 里排在最前面、passes 还是 false 的那一项。这一轮只做这一项。
第三步,实现并验证:写代码、写测试,然后按这一项的 steps 真的在命令行里跑一遍,像用户那样用,
看输出对不对。只跑单元测试不算验证。
第四步,收尾:确认无误后,把这一项的 passes 改成 true(只能改这个字段);git commit,写清楚做了什么;
在 claude-progress.txt 末尾追加这一轮的记录:做了哪一项、发现了什么问题、现在通过了几项、
下一轮该做什么。不要留下没提交的改动。
验收的提示词借的是第二套的思路:“你是验收的人,不是写代码的人……默认它有问题,除非你亲手验证过没有”,要求按需求逐条在命令行里用,重点试边界情况,把结果写进 qa-report.md,最后一行写“结论:通过”或“不通过”,不许改代码。修的那一轮读报告、按复现命令逐条修。
第一次跑失败了,教训在 CLAUDE.md 里
第一次跑,初始化那一轮就出了问题:它想用一条 Bash 命令、靠 heredoc 一次写好所有文件。这条命令被 dontAsk 拒绝了,它改用 Write 工具把文件写完,但没跑测试、也没提交。第一轮编码更糟:同样的长命令被拒以后,它停下来写了一段话请人“放开 Bash 权限”,然后就结束了。无人值守的时候,没有人会回答它。
我们停掉重来,在 CLAUDE.md 里加了几条,两组都一样:
- 这是无人值守的运行,没有人会回答你的问题。某条命令被拒绝时,换一种做法,不要停下来等人。
- 写文件、改文件用 Write 和 Edit 工具,不要在 Bash 里用 `cat >`、heredoc 写文件,这类命令会被拒绝。
- Bash 只用来跑测试、运行程序、看文件和用 git。一条命令只做一件事,不要把很多步骤串成一长条。
- 只用 Python 标准库,测试用 `python3 -m unittest`。
- 命令在沙箱里运行:提交时按文件名 `git add 文件1 文件2`,不要用 `git add -A` 或 `git add .`,沙箱里的占位文件会让它们失败。
- 临时文件放在项目目录里,不要写到项目外面。
最后一条之前那条,是沙箱那篇实测过的坑:Linux 上开了沙箱,git add -A 会被沙箱放的占位文件卡住。git 仓库也是我们在沙箱外面先建好的,因为沙箱里 .git/config 是只读的。
改完以后两组都没再卡住。所有会话里被拒绝的 Bash 命令一共三类,都是沙箱里也要走正常审批、dontAsk 下就被直接拒绝的:
| 被拒的命令 | 特点 |
|---|---|
| 用 heredoc 往文件里写一大段代码 | 一条命令里写好几个文件 |
mkdir -p run_tmp && cd run_tmp && export PYTHONPATH=.. && python3 -m ledger … |
用 export 改环境变量,还串了很多步 |
python3 -m ledger --file tmp/bad.json list; echo "exit=$?" |
带 $? 这类展开 |
官方文档的说法是,变量写在命令前面(比如 FOO=bar npm test)这类会改变实际运行什么的命令,即使在沙箱里也不自动放行。被拒之后,两组都换了一种写法接着做。
结果
harness 跑了 14 个会话:初始化 1 个,编码 10 个(初始化拆出了 10 项功能,正好一轮一项),验收 2 个,修复 1 个。
| 对照组:一个会话 | harness | |
|---|---|---|
| 会话数 | 1 | 14 |
| 耗时 | 151 秒 | 约 20 分钟(其中初始化加编码 12.5 分钟) |
| 花费 | 0.33 美元 | 3.33 美元(其中初始化加编码 2.30 美元) |
| 本站的 25 项验收 | 25/25 | 25/25 |
| 需求没写到的 9 个边界情况 | 处理好 5 个 | 处理好 6 个(经过一轮验收和修复) |
| 测试 | 19 个,210 行 | 67 个,927 行 |
| 代码 | 402 行 | 330 行 |
| git 提交 | 1 次 | 14 次 |
花费是 Claude Code 按官方价的估算,不是中转站实际扣的金额。编码的每一轮 19 到 27 轮对话、46 到 102 秒、0.18 到 0.29 美元,差别不大。
几处值得看:
- 结构被严格遵守。 10 轮编码各做一项,没有人想一口气做完,也没有人提前宣布完成;功能清单 10 次修改都只改了
passes字段;每一轮都先跑测试、最后提交,进度文件写了 124 行。第一套要解决的几种失败,这次一种都没出现。 - 但这个任务一个会话就做得完。 Sonnet 5.5 一个会话两分半钟做完,本站的 25 项检查全过。它还如实说了哪些只在测试里验证过、没有在命令行手动跑。对这么大的任务,harness 花了 10 倍的钱,换来的是更多的测试和一条一项一提交的记录。
- 最有用的是独立验收。 第一轮验收用了 96 轮对话,逐条在命令行里试,找出 7 处需求没写到的问题:分类可以是空字符串;金额写成阿拉伯-印度数字“٣”会被当成 3 收下;
list --category ""会列出全部;导入时多出来的列被悄悄截掉;数据文件的目录不存在、或者--file指向一个目录时,直接抛出 Python 的报错堆栈。修复那一轮把 7 处都修了,并补了测试。 - 单个会话反而写得更周全。 拿两轮验收的复现命令去试对照组,列数检查、目录不存在、
--file指向目录、数据文件结构不对这 4 种情况,对照组一开始就处理好了;harness 按功能一项项做出来的代码,这几处原本都没处理,是靠验收补上的。每一轮只看得到自己那一项,顾不上全局。 - 验收很难满意。 修完以后第二轮验收又挖出 2 处:预算的分类可以为空,引号没闭合的 CSV 行被当成正常数据导入。结论还是“不通过”。我们的脚本只修一轮,到此为止。这两处对照组同样没处理。不给验收定一条及格线,它会一直挑下去。
第二篇文章的作者说开箱即用的 Claude 是个太宽容的测试员;我们的验收提示词一上来就要求“默认它有问题”,结果是另一头:严,严到需求以外的问题也判不通过。两头都说明,评审的严格程度主要由提示词决定,要按你的需求去校准。
什么时候值得用
把两篇文章和这次实测放在一起,本站的看法:
| 你的情况 | 怎么做 |
|---|---|
| 任务一个会话做得完 | 不用 harness,让它一次做完 |
| 做完想多一层保险 | 另开一个会话专门验收,提示词写明“你不是写代码的人”,要求在命令行或浏览器里真的用一遍。这次一轮验收 0.4 美元左右,找出了自评发现不了的问题。“一个会话做完再加一轮验收”这个组合本站没有单独测,是根据上面的结果给的建议 |
| 任务大到一个会话做不完、要跑几个小时,或者要求每一步都有记录 | 用第一套:功能清单用 JSON、一轮只做一项、每轮先跑测试再提交、写进度文件 |
| 用了独立验收 | 事先说清楚及格线:需求没写的问题算不算失败。要么写进需求,要么告诉它记下来但不算失败 |
| 无人值守地跑 | 先定边界:沙箱加 dontAsk,或者整个跑在容器、虚拟机里;在 CLAUDE.md 里写明没有人会回答,命令被拒就换做法 |
| 换了更强的模型 | 照原文的原则,一次去掉一个组件,看它还是不是必需的 |
和本站其他文章的关系:要它朝一个目标连续做下去,Claude Code 有内置的 /goal 和 /loop,见《Harness 工程和 Loop 工程是什么》;要几个会话同时干,见《代理团队实战》和《动态工作流》;把 claude -p 接进脚本的基础用法,见《把 Claude Code 接进脚本和 CI》。
这次实测没有覆盖到的
- 任务太小,没有测到 harness 真正要解决的问题。 一个会话就做完了,没有碰到上下文用完、要好几个会话接力的情况。
- 没有规划代理。 需求是本站写好的,没有测“一句话扩成完整规格”这一步。
- 没有浏览器自动化。 做的是命令行工具,验证靠在命令行里跑。
- 两组各跑一次。 模型每次的表现不一样,不能当成稳定的结论。
- 第一次失败的那一轮没有算进花费。 两个会话约 0.4 美元,加上中途停掉的几个会话。
参考
- Justin Young:Effective harnesses for long-running agents(Anthropic 工程博客,2025-11-26)
- Harness design for long-running application development(Anthropic 工程博客,2026-03-24)
- claude-quickstarts / autonomous-coding(官方示例代码)
- Claude Code 文档:Sandboxing、Choose a permission mode