给 AI 装上缰绳、护栏、仪表盘和刹车——不是限制它的能力,而是让能力在正确的轨道上释放。
当我们将复杂任务交给 AI Agent 时,一个根本性矛盾出现了:
Harness Engineering(缰绳工程) 就是解决这个矛盾的系统性方法。它不是限制 AI 的能力,而是为 AI 的能力提供正确的轨道、实时的反馈、安全的边界和可控的介入机制。
本文将深入探讨 Harness Engineering 的完整架构:上下文管理、多 Agent 协同、相互制约机制、以及如何让"编写-审计-打回-重做"成为可重复的工程流程。
Harness Engineering 由五个相互关联的层次组成,每一层解决不同维度的控制问题。
AI Agent 的能力上限,取决于它掌握的上下文质量。上下文管理解决的是**"Agent 知道什么、应该知道什么、不应该知道什么"**的问题。
┌──────────────────────────────────────────┐ │ 战略上下文(持久化) │ │ spec.md / todo.md / 项目架构文档 │ │ 作用:定义目标、边界、验收标准 │ │ 更新频率:低(用户编写,任务周期内不变) │ ├──────────────────────────────────────────┤ │ 战术上下文(会话级) │ │ 当前任务描述 / 依赖关系 / 约束条件 │ │ 作用:指导单次执行的优先级和顺序 │ │ 更新频率:中(任务切换时更新) │ ├──────────────────────────────────────────┤ │ 执行上下文(瞬时) │ │ 当前文件内容 / 编译错误 / 运行时状态 │ │ 作用:支撑具体操作的决策 │ │ 更新频率:高(每步操作后刷新) │ └──────────────────────────────────────────┘
上下文的传递和更新遵循严格的规则:
用户编写 spec.md │ ▼ ┌─────────────────┐ │ 上下文注入阶段 │ 启动 Agent 时,注入 spec + todo + 相关代码文件 │ (Context Load) │ 限制:不注入无关文件,控制上下文窗口大小 └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 上下文更新阶段 │ 每完成一个任务,更新 todo.md 状态 │ (Context Sync) │ 审计通过后再 sync,失败的更新标记为 blocked └────────┬────────┘ │ ▼ ┌─────────────────┐ │ 上下文清理阶段 │ 任务完成后,清理临时上下文,保留关键决策记录 │ (Context GC) │ 写入 audit.md 作为长期记忆 └─────────────────┘
多 Agent 协同中,上下文管理更加复杂:
| Agent 角色 | 可读上下文 | 可写上下文 | 隔离原因 |
|---|---|---|---|
| 规划 Agent | spec.md, todo.md, 架构文档 | todo.md(状态标记) | 不接触代码,保持规划独立性 |
| 开发 Agent | spec.md, todo.md, 代码库 | 代码文件, todo.md(进度) | 专注实现,不受审计意见干扰 |
| 审计 Agent | spec.md, todo.md, 代码库, diff | audit.md | 只读代码,只写审计记录 |
关键原则:每个 Agent 只能写入自己负责的上下文区域,避免状态冲突。
这一层解决的是 "Agent 能做什么、不能做什么" 的问题。
| 级别 | 操作类型 | 示例 | 执行方式 |
|---|---|---|---|
| L1 安全操作 | 读取、查询、分析 | cat, grep, git status | 自动执行 |
| L2 安全写入 | 版本控制内的写入 | git commit, 代码修改 | 自动执行 |
| L3 受限操作 | 影响运行的操作 | git push, 数据库变更 | 需确认 |
| L4 危险操作 | 不可逆操作 | rm -rf, DROP TABLE | 需审批 |
| L5 禁止操作 | 系统级危险操作 | 删除根目录、修改权限 | 直接拦截 |
python# 边界约束检查伪代码
def check_boundary(agent_action, agent_role):
# 1. 检查操作类型是否在角色的允许列表中
if agent_action not in ROLE_PERMISSIONS[agent_role]:
return REJECT, f"角色 {agent_role} 不允许执行 {agent_action}"
# 2. 检查目标路径是否在允许范围内
if not is_path_allowed(agent_action.target, agent_role):
return REJECT, f"路径 {agent_action.target} 不在允许范围内"
# 3. 检查是否触及危险模式
risk_level = assess_risk(agent_action)
if risk_level >= RISK_CRITICAL:
return REQUIRE_APPROVAL, f"操作风险等级 {risk_level},需人工审批"
return APPROVE, "安全操作"
| 资源类型 | 限制 | 触发行为 |
|---|---|---|
| 最大执行时间 | 30 分钟 | 超时暂停,上报 Coordinator |
| 最大修改文件数 | 50 个 | 超过后暂停,等待确认 |
| 最大输出行数 | 10,000 行 | 截断输出,标记异常 |
| 并发任务数 | 3 个 | 超过排队等待 |
这一层解决的是 "Agent 在干什么、干得怎么样、有没有跑偏" 的问题。
┌─────────────────────────────────────────┐ │ 战略层可观测性 │ │ 指标:任务完成率、需求覆盖率、偏离度 │ │ 频率:每 30 分钟 │ │ 受众:用户 / 项目经理 │ ├─────────────────────────────────────────┤ │ 战术层可观测性 │ │ 指标:当前任务进度、依赖阻塞、资源使用 │ │ 频率:每 3 分钟 │ │ 受众:Coordinator / 审计 Agent │ ├─────────────────────────────────────────┤ │ 执行层可观测性 │ │ 指标:终端输出、文件变更、命令执行历史 │ │ 频率:实时 │ │ 受众:系统自动监控 │ └─────────────────────────────────────────┘
以 tmux + cron 巡检为例:
bash# 执行层:捕获实时输出
tmux capture-pane -t dev-session -p -S -100
# 战术层:对比需求与进度
# 1. 读取 todo.md,解析任务状态
# 2. 执行 git diff --stat,获取实际变更
# 3. 对比 spec.md,计算需求覆盖率
# 战略层:生成简报
# 汇总过去 30 分钟的所有战术层数据
# 生成 Markdown 报告,推送到 Slack/微信
最关键的观测指标是需求偏离度:
偏离度 = |实际修改的文件| ∩ |需求涉及的模块| 的补集
如果开发 Agent 修改了与当前任务无关的文件,偏离度升高,系统自动:
这是 Harness Engineering 最核心的部分——多个 Agent 如何在共享目标下独立工作、相互校验、形成闭环。
┌─────────────┐ │ 用户 (Human) │ │ 定义目标/验收 │ └──────┬──────┘ │ spec.md + todo.md ▼ ┌──────────────────────────────────────────────────┐ │ 协调层 (Coordinator) │ │ • 分发任务到正确的 Agent │ │ • 追踪整体进度 │ │ • 处理异常上报 │ │ • 维护上下文一致性 │ └────────┬─────────────┬──────────────┬────────────┘ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ 规划 Agent│ │ 开发 Agent│ │ 审计 Agent│ │ Planner │ │ Developer│ │ Auditor │ └────┬────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ 产出 │ 产出 │ 产出 │ todo.md │ 代码文件 │ audit.md ▼ ▼ ▼ ┌────────────────────────────────────────┐ │ 共享文件系统 │ │ spec.md ← 需求定义(只读) │ │ todo.md ← 任务状态(规划/开发可写) │ │ audit.md ← 审计记录(审计可写) │ │ code/ ← 代码(开发可写) │ └────────────────────────────────────────┘
这是 Harness Engineering 的核心价值——让质量保障成为自动化流程的一部分,而不是事后补救。
Step 1: 规划阶段 ───────────────────── 规划 Agent 读取 spec.md │ ├── 分解为可执行的任务列表 ├── 写入 todo.md,每个任务包含: │ - 任务描述 │ - 验收标准 │ - 依赖关系 │ - 预估工作量 │ └── 通知 Coordinator:规划完成 Step 2: 开发阶段 ───────────────────── Coordinator 从 todo.md 取下一个 pending 任务 │ ├── 启动开发 Agent,注入: │ - 当前任务描述 │ - 相关代码上下文 │ - 验收标准 │ ├── 开发 Agent 编写代码 │ - 实时写入代码文件 │ - 更新 todo.md 状态为 in_progress │ - 完成后标记 completed │ └── 通知 Coordinator:开发完成 Step 3: 审计阶段 ───────────────────── Coordinator 启动审计 Agent,注入: - 当前任务描述 - 验收标准 - git diff(代码变更) - spec.md 相关需求 │ ├── 审计 Agent 检查: │ 1. 功能是否满足验收标准? │ 2. 代码质量是否达标? │ 3. 是否引入了回归? │ 4. 是否偏离了需求? │ ├── 审计结果写入 audit.md: │ ```markdown │ ## 审计:任务 #3 - 用户认证模块 │ - 状态: ❌ 打回 │ - 问题: │ 1. 密码未加盐哈希,使用明文存储 │ 2. 缺少 token 过期处理 │ 3. 未处理并发登录场景 │ - 建议: │ 1. 使用 bcrypt 替代明文 │ 2. 添加 token 刷新机制 │ 3. 添加 session 管理 │ ``` │ └── 根据审计结果分流: ├── ✅ 通过 → 任务标记 approved,进入下一个任务 └── ❌ 打回 → 任务标记 blocked,附审计意见 │ └── 重新派发给开发 Agent ├── 注入审计意见作为额外上下文 ├── 开发 Agent 针对性修复 └── 重新进入审计阶段(循环直到通过) Step 4: 交付阶段 ───────────────────── 所有任务 approved 后: │ ├── 汇总所有 audit.md 记录 ├── 生成最终交付报告 ├── 执行完整性检查 └── 通知用户:可以验收
打回不是简单的"不行",而是一个结构化的反馈-修复循环:
打回流程: ┌─────────────────────────────────────┐ │ 1. 审计 Agent 发现问题 │ │ ├── 问题分类:功能/安全/性能/风格 │ │ ├── 严重程度:Critical/Major/Minor │ │ └── 附带修复建议 │ ├─────────────────────────────────────┤ │ 2. Coordinator 接收审计结果 │ │ ├── 将任务状态改为 blocked │ │ ├── 附加 audit.md 中的审计意见 │ │ └── 重新派发给开发 Agent │ ├─────────────────────────────────────┤ │ 3. 开发 Agent 接收打回 │ │ ├── 读取 audit.md 中的审计意见 │ │ ├── 针对性修复(不重写无关代码) │ │ ├── 更新代码文件 │ │ └── 标记 ready_for_reaudit │ ├─────────────────────────────────────┤ │ 4. 重新审计 │ │ ├── 审计 Agent 只检查被指出的问题 │ │ ├── 如果仍有问题 → 再次打回 │ │ └── 如果全部解决 → 标记 approved │ └─────────────────────────────────────┘ 最大打回次数:3 次 超过 3 次 → 升级为人工介入
规划 Agent ──制约──▶ 开发 Agent │ │ │ "你必须按照 │ "我可以在 │ todo.md 的 │ 实现中优化 │ 顺序执行" │ 依赖顺序" │ │ ▼ ▼ 开发 Agent ──制约──▶ 审计 Agent │ │ │ "代码已按 │ "你的代码 │ spec 实现, │ 有 3 个问题 │ 请审计" │ 需要修复" │ │ ▼ ▼ 审计 Agent ──制约──▶ 开发 Agent │ │ │ "打回,理由: │ "收到, │ 1. xxx │ 针对性修复 │ 2. yyy" │ 中..." │ │ ▼ ▼ 所有 Agent ──制约──▶ 彼此 │ │ 通过共享文件系统(spec/todo/audit) │ 形成闭环,任何一方都不能单方面 │ 推进不合格的工作产物
Agent 在迭代过程中会产生代码熵——冗余代码、废弃注释、不一致的命名。第五层确保交付物是干净的。
| 触发条件 | 清理范围 | 执行方式 |
|---|---|---|
| 单个任务 approved 后 | 该任务涉及的文件 | 审计 Agent 附带清理 |
| 里程碑完成后 | 整个模块 | 专用清理 Agent |
| 全部任务完成后 | 整个代码库 | 最终清理 + 人工复核 |
清理清单: - [ ] 删除未使用的 import 和变量 - [ ] 统一命名风格(camelCase/snake_case) - [ ] 移除调试代码(console.log, print) - [ ] 补充缺失的函数注释 - [ ] 格式化代码(统一缩进、换行) - [ ] 更新过时注释 - [ ] 检查并修复 lint 警告
| 维度 | 传统 AI 开发 | Harness 开发 |
|---|---|---|
| 过程可见性 | 看不到中间状态 | 每 3 分钟快照 |
| 质量保障 | 事后人工检查 | 自动化审计循环 |
| 偏离检测 | 完成后才发现 | 实时对比 spec |
| 失败恢复 | 从头开始 | 从检查点恢复 |
| 知识沉淀 | 一次性使用 | audit.md 长期记忆 |
"编写-审计-打回-重做"不是靠人的责任心,而是靠结构化的流程:
Harness 不是简单的定时任务,而是基于上下文状态的智能调度:
pythondef decide_next_action():
# 读取当前状态
todo_state = read_todo_md()
audit_state = read_audit_md()
agent_status = check_all_agents()
# 基于状态做决策
if any(task.status == "blocked" for task in todo_state):
# 有任务被打回,重新派发给开发 Agent
reassign_blocked_task()
elif all(task.status == "approved" for task in todo_state):
# 所有任务完成,触发清理
trigger_cleanup()
elif agent_status["developer"] == "stuck":
# 开发 Agent 卡住,自动确认或上报
handle_stuck_agent()
else:
# 正常推进下一个任务
start_next_task()
不要一开始就搭建完整五层架构。建议按以下顺序逐步引入:
Phase 1: 上下文管理(1 天) └── 编写 spec.md + todo.md,让 Agent 按文档执行 Phase 2: 可观测性(1 天) └── 添加 cron 巡检,每 3 分钟捕获状态 Phase 3: 审计闭环(2-3 天) └── 引入审计 Agent,实现"编写-审计-打回"循环 Phase 4: 多角色协同(3-5 天) └── 规划 Agent 独立,自动生成 todo.md Phase 5: 护栏与清理(持续优化) └── 边界约束、资源限制、自动清理
Harness Engineering 的本质是将软件工程的最佳实践自动化:
| 传统工程实践 | Harness 自动化 |
|---|---|
| 需求文档 | spec.md 注入 Agent 上下文 |
| 任务拆解 | 规划 Agent 自动生成 todo.md |
| 代码审查 | 审计 Agent 自动审查 + 打回 |
| 进度汇报 | cron 巡检自动生成简报 |
| 质量门禁 | 验收标准检查 + 偏离检测 |
| 知识沉淀 | audit.md 长期记录 |
Harness 不是限制 AI 的牢笼,而是让 AI 的能力可控、可追踪、可重复的工程框架。
当你可以放心地说"让 Agent 去跑,我会收到进度报告,有问题会被打回重做,完成后我会收到交付报告"——Harness Engineering 就真正落地了。
最后更新:2026-04-12
本文作者:lazyyoun
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!