最近在折腾 AI Coding Agent(Claude Code / Cursor / 自定义 Agent)时,我发现一个很常见的问题:
模型会写代码,但不一定会"按流程工作"。
它可能:
- 需求还没对齐,直接开始改代码
- 改着改着 scope creep,顺手改了别的模块
- 一次改 8 个文件,最后根本不知道哪一步坏了
- 自己说"完成了",但没有可验证的状态
- 长会话后上下文压缩,开始"假装记得自己干过什么"
我慢慢意识到:
问题不是模型不会写代码,而是 缺一个 Harness(约束层)。
于是我尝试给 AI Agent 搭了一个 Mini Harness。
它不负责生成代码,而是负责:
把 Agent 约束在一个可恢复、可审计、可推进的轨道里运行。
什么是 Harness?
很多人第一次听到 Harness,会想到:
- Test Harness
- Browser Harness
- Runtime Harness
但在 AI Agent 里,我更喜欢把它理解成:
Harness = Agent 的运行约束层(Execution Constraint Layer)
它不是模型本身。
而是:
限制模型什么时候能做事、能做什么、做完怎么验证。
一句话:
Prompt 负责思考,Harness 负责约束。
为什么 Prompt 不够?
一开始我也尝试过:
直接写超长 Prompt。
比如:
- 不允许跳步骤
- 每次只能改一个任务
- 不能改计划外文件
- 高风险任务必须暂停
- 做完要验证
- 要记录状态
结果很快发现问题。
Prompt 再长,本质还是:
soft rule(软约束)
模型可以"知道",但不一定"遵守"。
尤其长会话后:
上下文压缩、任务切换、状态漂移,问题会越来越明显。
所以我开始拆:
Prompt ≠ Harness
我设计的 Mini Harness
核心结构很简单:
text
HARNESS.md # 协议说明(spec)
state.yaml # 全局状态机
ledger.yaml # 任务队列
audit.log # 审计日志
git commits # checkpoint
它本质是一个:
Stateful Checkpointed Agent Harness
第一层:状态机(FSM)
我先把 Agent 强制放进状态机里。
text
NONE
↓
RESTATE
↓
PLAN
↓
EXECUTE
↓
DONE
外加:
text
BYPASS
用于小改动逃逸。
RESTATE:需求对齐
禁止 Agent 直接干活。
先复述:
- 需求是什么
- 范围内是什么
- 范围外是什么
- 隐性假设
- 验收标准
目的:
防止 AI 一上来 scope creep。
PLAN:拆任务
写入:
ledger.yaml
yaml
tasks:
- id: T1
title: Refactor auth middleware
files:
- src/auth.ts
risk: medium
status: proposed
每个任务:
- 有 DoD
- 有文件范围
- 有依赖
- 有风险等级
Agent 不能跳过。
EXECUTE:一次一个任务
只允许处理:
yaml
status: in_progress
那一条。
防止:
Agent 一次乱改一堆文件。
这其实是:
single-flight execution
第二层:RALPH MODE(自动串行执行)
后来我发现:
每个 task 都让用户回复 next 很烦。
于是加了 auto mode。
text
PLAN
↓
ok auto
↓
EXECUTE(auto)
Agent 自动循环:
text
load task
edit
verify
commit
advance
repeat
直到:
- DoD 失败
- 风险高
- 越界
- 文件超白名单
- 用户 stop
这部分开始有点像真正的 Coding Agent。
第三层:Scope Guard
这是我觉得最关键的一层。
Agent 最大问题不是写错代码。
而是:
写太多。
所以我加:
yaml
files:
- src/auth.ts
任务执行时:
只能改这些文件。
超出:
直接 Boundary Intercept。
本质:
filesystem scope isolation
第四层:Checkpoint
每个任务通过 DoD 后:
bash
git add -A
git commit -m "harness: T2 update auth"
为什么一定要 commit?
因为它解决三个问题:
1. 可回滚
坏了直接回退。
2. 可恢复
新 session 看 git log 就知道做到哪。
3. 可审计
一个 task 一个 commit。
第五层:State Rehydration
LLM 最大的问题之一:
长会话后会"假装记得"。
所以我禁止 Agent 依赖纯会话历史。
每次任务开始:
必须重读:
text
state.yaml
ledger.yaml
git log
重新恢复状态。
这叫:
state rehydration
我后来发现:最大的坑其实不是 Agent,而是 Token
一开始我把整个 HARNESS.md 塞进 Prompt。
很快发现:
太胖了。
因为混了:
- protocol
- examples
- docs
- rationale
- runtime
于是我拆成三层。
正确的 Harness 结构:重 Spec,轻 Runtime
Layer 1:Spec(长文档)
负责:
协议说明。
不是每轮都喂模型。
Layer 2:State(磁盘)
yaml
current_stage: EXECUTE
active_task_id: T2
execution_mode: auto
只存真实状态。
Layer 3:Runtime(最小注入)
每轮只注入:
yaml
stage: EXECUTE
task: T2
rules:
- only modify task.files
- DoD must pass
- high risk => stop
- emit state diff
控制在几百 token。
而不是几千 token。
它算真正的 Harness 吗?
我的答案是:
算。
但更准确说:
它不是 Runtime Harness。
也不是 Test Harness。
它是:
Stateful Checkpointed Agent Harness
一个轻量级 AI Coding Agent orchestration layer。
下一步怎么升级?
如果继续做 v1,我会补:
1. Verifier
检查:
- schema 是否合法
- 是否只有一个 in_progress
- state 是否脏
2. File Sandbox
自动 diff:
bash
git diff --name-only
验证是否越界。
3. Transactional Commit
先 commit 成功,再推进 state。
避免 split-brain。
4. Watchdog
限制 auto loop 次数。
防止死循环。
最后
我原本以为自己在写 Prompt。
后来发现:
我其实在写一个 Agent Harness。
Prompt 在教 AI 思考。
Harness 在约束 AI 行为。
而真正稳定的 Agent,往往两者都需要。