Anthropic 在 claude-quickstarts 仓库中放了一个 autonomous-coding 示例项目。整个项目只有 8 个 Python 文件、约 500 行代码、一条 pip 依赖(claude-code-sdk>=0.0.25),却能驱动 Claude 在多个 session 之间持续工作数小时,从零构建一个带 200 个测试用例的完整 Web 应用。

这个项目的价值不在于它有多复杂,恰恰在于它有多简单。它是 Anthropic 对"长程自主编码的最小可行 harness"的官方回答,每一个设计决策都值得拆开看。

项目全景

1
2
3
4
5
6
7
8
9
10
11
12
13
autonomous-coding/
├── prompts/
│ ├── app_spec.txt # 应用规格说明书
│ ├── initializer_prompt.md # 初始化 Agent 的 prompt
│ └── coding_prompt.md # 编码 Agent 的 prompt
├── autonomous_agent_demo.py # CLI 入口
├── agent.py # Agent session 循环
├── client.py # SDK 客户端配置
├── security.py # Bash 命令白名单
├── progress.py # 进度追踪
├── prompts.py # Prompt 加载工具
├── test_security.py # 安全模块测试
└── requirements.txt # 唯一依赖:claude-code-sdk

技术栈极简:Python 3 + Claude Agent SDK。没有 LangChain,没有向量数据库,没有消息队列,没有状态机框架。所有跨 session 的状态管理靠一个 JSON 文件和 git commit 完成。

双 Agent 架构

sequenceDiagram
    participant H as Harness (agent.py)
    participant I as Initializer Agent
    participant F as feature_list.json
    participant C as Coding Agent
    participant G as Git

    H->>I: Session 1: 读取 app_spec.txt
    I->>F: 生成 200 条测试用例 (passes: false)
    I->>G: git init + 首次提交
    I-->>H: Session 1 结束

    loop 每个后续 Session
        H->>C: 新 context window(无记忆)
        C->>F: 读取当前进度
        C->>G: git log 了解历史
        C->>C: 回归验证 → 选特性 → 实现 → 浏览器验证
        C->>F: passes: false → true
        C->>G: git commit
        C-->>H: Session N 结束(等待 3s)
    end

这个项目的核心架构决策是把一个长程任务拆成两种角色的 Agent,各自在独立的 context window 中运行。

Initializer Agent(第一个 session)

Initializer 只运行一次,职责是把模糊的应用规格转化为可执行的工作清单:

  1. 读取 app_spec.txt(默认是构建一个 claude.ai 的功能克隆)
  2. 生成 feature_list.json——包含 200 个测试用例,每个用例有描述、验证步骤和 passes: false 标记
  3. 创建 init.sh 环境初始化脚本
  4. 初始化 git 仓库,首次提交
  5. 搭建项目骨架目录

Initializer prompt 里有一条硬约束值得注意:

IT IS CATASTROPHIC TO REMOVE OR EDIT FEATURES IN FUTURE SESSIONS.

这条约束保证了 feature_list.json 是一个 append-only 的进度账本。后续的 Coding Agent 只能把 passesfalse 翻成 true,不能修改测试定义本身。这个设计把"定义完成标准"和"执行开发工作"彻底分离——Initializer 定标准,Coding Agent 按标准干活。

Coding Agent(第二个 session 起)

每个 Coding session 都从一个空白的 context window 开始,没有任何前序记忆。Coding Agent 的 prompt 定义了一个 10 步工作流:

  1. 环境感知:读 app_spec.txtfeature_list.jsonclaude-progress.txt、git log——这四个文件就是全部的"记忆"
  2. 启动服务:执行 init.sh
  3. 回归验证:先重测所有已标记为 passing 的特性,修复回归 bug
  4. 特性选择:从 feature_list.json 中挑一个优先级最高的 failing 特性
  5. 实现:编码
  6. 浏览器验证:通过 Puppeteer MCP 工具在真实浏览器中验证 UI(prompt 明确禁止只跑 curl 或用 JS eval 绕过 UI)
  7. 更新状态:只修改 feature_list.json 中对应条目的 passes 字段
  8. 提交:git commit
  9. 写进度笔记:更新 claude-progress.txt
  10. 清理:确保应用处于可运行状态

这个 10 步流程的设计有一个关键特征:每一步都是幂等的。session 在任何步骤崩溃或超时后,下一个 session 从步骤 1 重新开始,通过读取文件系统状态自动恢复到正确的工作点。不需要 checkpoint 机制,不需要事务回滚。

跨 Session 状态持久化

长程 Agent 的核心难题是:context window 有边界,但任务没有。每个 session 结束后,所有 in-context 的信息全部丢失。这个项目用三个文件解决这个问题:

文件 作用 写入者 读取者
feature_list.json 完成标准 + 进度状态 Initializer 创建,Coding Agent 更新 passes 字段 每个 Coding session 开头
claude-progress.txt 自然语言的进度笔记 每个 session 结束时 下一个 session 开头
git history 代码变更的完整记录 每个 session 多次 commit 下一个 session 通过 git log 读取

feature_list.json 的数据结构直接决定了整个系统的运作方式。每个条目包含:

1
2
3
4
5
6
7
8
9
10
{
"description": "User can create new conversation",
"category": "functional",
"steps": [
"Click new conversation button",
"Verify empty chat area appears",
"Verify conversation appears in sidebar"
],
"passes": false
}

进度信息被编码成了一个布尔字段的翻转。不需要复杂的状态机,不需要百分比进度条,不需要依赖图。200 个 passes: false → true 的翻转就是整个项目从零到完成的全部轨迹。

progress.py 中的进度统计函数直接数这些布尔值:

1
2
3
4
5
6
def count_passing_tests(project_dir: Path) -> tuple[int, int]:
feature_list = json.loads(
(project_dir / "feature_list.json").read_text()
)
passing = sum(1 for f in feature_list if f.get("passes"))
return passing, len(feature_list)

纵深防御安全模型

自主编码 Agent 能执行 shell 命令,安全问题不可回避。这个项目实现了三层防御:

flowchart LR
    CMD[Bash 命令] --> L1{OS 沙箱}
    L1 -->|通过| L2{文件系统限制}
    L1 -->|拒绝| B1[操作系统拦截]
    L2 -->|在项目目录内| L3{命令白名单 Hook}
    L2 -->|越界| B2[SDK 拒绝]
    L3 -->|在 ALLOWED_COMMANDS 内| OK[执行]
    L3 -->|不在白名单| B3[Hook 拦截]
    L3 -->|高危命令| V{额外校验}
    V -->|通过| OK
    V -->|不通过| B3

第一层:OS 级沙箱

client.py 在创建 SDK 客户端时启用沙箱:

1
2
3
4
5
6
7
8
9
10
11
security_settings = {
"sandbox": True,
"permissions": {
"mode": "acceptEdits",
"allow": [
"./**",
"Bash(*)",
*[f"mcp__puppeteer__{tool}" for tool in PUPPETEER_TOOLS]
]
}
}

acceptEdits 模式自动批准文件读写,但文件系统操作被限制在项目目录(./**)内。

第二层:文件系统限制

SDK 客户端的 cwd 被锁定到项目目录的绝对路径。所有文件操作都以这个目录为根,Agent 无法读写项目目录之外的文件。

第三层:Bash 命令白名单

security.py 实现了一个 PreToolUse hook,在每个 Bash 命令执行前拦截并校验:

1
2
3
4
5
6
7
8
9
10
11
12
ALLOWED_COMMANDS = {
# 文件检查
"ls", "cat", "head", "tail", "wc", "grep",
# 文件操作
"cp", "mkdir", "chmod",
# Node.js 开发工具
"npm", "node", "npx",
# 版本控制
"git",
# 进程管理
"ps", "lsof", "sleep", "pkill",
}

注意白名单里没有 curlwgetpythonrmechotouch。Agent 不能下载任意文件,不能执行 Python 脚本,不能删除文件,甚至不能用 echo 写文件(必须通过 SDK 的 Write 工具)。

对高危命令还有额外校验。pkill 只允许杀 node/npm/npx/vite/next 等开发服务器进程;chmod 只允许 +x(设置可执行权限),数字模式如 777755 全部拒绝;脚本执行只允许 ./init.sh

hook 的注册方式:

1
2
3
4
5
6
7
8
ClaudeCodeOptions(
hooks={
"PreToolUse": [
HookMatcher(matcher="Bash", hooks=[bash_security_hook]),
],
},
...
)

bash_security_hook 是一个 async 函数,返回空字典表示放行,返回 {"decision": "block", "reason": "..."} 表示拦截。这个 hook 机制是 Claude Agent SDK 提供的标准拦截点,所有工具调用都可以用同样的模式做预校验。

Agent 循环的实现

agent.py 中的 run_autonomous_agent 函数是整个系统的主循环:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
创建项目目录

检测 feature_list.json 是否存在
↓ 不存在 ↓ 已存在
使用 Initializer 使用 Coding
Prompt Prompt
↓ ↓
└───── 创建 Client ──┘

运行 Session

检查结果状态
↓ 成功 ↓ 错误
等待 3 秒 重试
↓ ↓
下一轮迭代 ←───────┘

session 类型的判断逻辑简洁直接:feature_list.json 存在就跑 Coding Agent,不存在就跑 Initializer。不需要额外的状态标记或配置项。

每个 session 都创建一个全新的 ClaudeSDKClient 实例,带着全新的 context window。client.py 中设置了 max_turns=1000,给单个 session 足够的工具调用预算。session 之间有 3 秒的自动延迟(AUTO_CONTINUE_DELAY_SECONDS = 3),允许用户用 Ctrl+C 中断。

run_agent_session 函数负责单个 session 内的流式输出处理:接收 Claude 的文本输出和工具调用结果,对长文本做截断显示,对被安全 hook 拦截的命令显示 [BLOCKED] 标记。

Prompt 设计的关键决策

两份 prompt 的设计有几个关键决策。

强制浏览器验证。Coding prompt 要求通过 Puppeteer MCP 工具在真实浏览器中验证 UI,明确禁止"只跑 curl"或"用 JS eval 绕过 UI"。这意味着 Agent 必须像真实用户一样操作应用——点击按钮、填写表单、检查渲染结果。client.py 注册了 7 个 Puppeteer 工具:navigatescreenshotclickfillselecthoverevaluate

回归测试前置。每个 Coding session 不是直接开始新特性开发,而是先重测所有已通过的特性。prompt 甚至列出了常见的 UI 回归模式——白底白字、随机字符显示——要求发现回归后先修复再继续。这个设计承认了一个现实:自主编码 Agent 在实现新特性时经常破坏已有功能。

无限时间心理暗示。两份 prompt 都包含"You have unlimited time across many sessions. Focus on quality over speed"。这不是技术约束,是行为引导——防止 Agent 为了赶进度而牺牲代码质量或跳过验证步骤。

状态不可变约束feature_list.json 中的测试定义被标记为不可修改。Coding Agent 唯一允许的写操作是把 passesfalse 改成 true。禁止删除、合并、重排序、修改描述。这个约束防止 Agent 通过降低验收标准来"通过"测试。

从 500 行代码提炼的 Harness 模式

这个项目虽小,但浓缩了几个在更大规模系统中反复出现的 harness 设计模式。

模式一:文件系统即数据库

所有跨 session 的状态都持久化到文件系统——JSON 文件存结构化数据,纯文本文件存自然语言笔记,git 存变更历史。不需要数据库进程,不需要网络连接,不需要序列化/反序列化框架。Agent 启动后 cat 几个文件就能恢复全部上下文。

这个模式的适用条件是:单 Agent 串行执行。一旦需要多个 Agent 并发写同一个状态文件,文件系统就不够用了,需要引入锁或者切换到真正的数据库。autonomous-coding 项目的 session 严格串行,所以文件系统完全够用。

模式二:验收标准与执行分离

Initializer 定义"什么算完成",Coding Agent 执行"怎么做到"。两者运行在不同的 context window 中,通过 feature_list.json 交接。这个分离带来两个好处:

  • 防止 Agent 在执行过程中不自觉地降低标准(“这个特性太难了,改一下测试定义吧”)
  • 允许人工审核验收标准——在 Initializer 跑完之后、Coding Agent 启动之前,可以检查和调整 feature_list.json

模式三:每个 Session 从零开始感知环境

Coding Agent 的 prompt 不假设任何先验知识,第一步就是用 bash 命令扫描环境:读 spec、读 feature list、读 progress notes、查 git log。这个"先看后做"的模式让每个 session 都是自包含的——即使上一个 session 异常退出,下一个 session 也能通过读取文件系统状态自动恢复。

模式四:Hook 做安全而非 Prompt 做安全

安全约束不是写在 prompt 里说"请不要执行危险命令",而是通过 PreToolUse hook 在 SDK 层面硬拦截。prompt 可以被 jailbreak,hook 不会。这是"纵深防御"在 Agent 安全领域的标准实践——prompt 是第一道软防线,hook 是第二道硬防线,OS 沙箱是第三道兜底。

模式五:最小工具集

Agent 能用的工具被严格限制:6 个内置工具(Read/Write/Edit/Glob/Grep/Bash)+ 7 个 Puppeteer 工具 + 白名单内的 shell 命令。没有"给 Agent 所有工具让它自己选"的思路。工具集越小,Agent 的行为空间越可预测,出错时的排查范围越窄。

局限与权衡

这个项目是一个教学性质的 demo,有明确的局限:

串行执行。所有 session 严格串行,不支持多个 Agent 并行开发不同特性。对于 200 个特性的规模,总耗时在"多个小时"级别。生产级系统需要并行化。

无错误恢复策略。如果 Agent 把代码写坏了(比如引入了无法编译的语法错误),除了下一个 session 的回归测试能发现之外,没有自动回滚机制。git revert 不在白名单里。

验收标准的质量取决于模型feature_list.json 里 200 个测试用例的质量完全取决于 Initializer Agent 的输出。如果模型生成了模糊或不可测试的验收标准,后续的 Coding Agent 也无法弥补。

安全白名单偏保守。没有 rm 意味着 Agent 不能删除文件(即使是自己创建的临时文件),没有 echo 意味着不能用 shell 重定向写文件。这些限制在 demo 场景下可以接受,在生产场景下可能需要放宽。

与同类方案的对比定位

2026 年的 AI 编码 Agent 领域已经有多个方案。autonomous-coding 项目在这个图谱中的位置是:

维度 autonomous-coding SWE-Agent OpenHands Aider
定位 Harness 参考实现 学术研究平台 开源 Agent 平台 结对编程 CLI
任务粒度 从零构建完整应用 单个 issue/bug fix 多种编码任务 单次对话内的修改
Session 管理 多 session 串行 单 session 单 session 单 session
状态持久化 文件系统 + git Docker 快照 沙箱 runtime git
安全模型 三层纵深防御 Docker 隔离 沙箱 runtime
代码量 ~500 行 ~10000 行 ~50000 行 ~15000 行

autonomous-coding 不是要和这些项目竞争,而是展示一种最小化的 harness 设计方法——用最少的基础设施代码,把模型的编码能力释放到跨 session 的长程任务上。

“Dive into Claude Code” 项目的分析给出过一个数据:Claude Code 的架构中 98.4% 是基础设施代码,1.6% 是 AI 调用。autonomous-coding 项目把这个比例推向了极端——几乎没有基础设施代码,纯粹依赖 Claude Agent SDK 提供的标准原语。它验证的命题是:当 SDK 足够好的时候,harness 可以非常薄

参考资料