傻子可懂的Harness_Engineering保姆级教程
傻子可懂的 Harness Engineering 保姆级教程!零基础也能看懂,一个实战项目带你跑通全流程
你可能听过 Prompt Engineering、Context Engineering,现在又冒出来一个 Harness Engineering——这玩意到底是啥?跟前面俩啥关系?学了能干嘛?别急,这篇文章用最白的大白话,从零讲透,最后还带你跑一个实战项目。
一、用大白话讲清楚:Harness Engineering 到底是什么?
1.1 一个比喻你就懂了
想象你雇了一个超级聪明但没经验的实习生来帮你写代码。
- Prompt Engineering = 你告诉他"帮我写个登录功能"——这是在说话方式上下功夫,让他听懂你的需求。
- Context Engineering = 你把项目文档、代码规范、技术栈说明都放在他桌上——这是在信息环境上下功夫,让他有据可查。
- Harness Engineering = 你给他配了电脑、装了开发环境、设了代码审查流程、加了自动化测试、规定他不能直接改生产库——这是在整个工作系统上下功夫,让他能干活、干好活、不闯祸。
一句话总结:Harness Engineering 就是给 AI Agent 搭一套"工作台",让它能可靠地完成任务,而不是到处闯祸。
1.2 正式定义
Harness Engineering 这个概念由 Mitchell Hashimoto(HashiCorp 联合创始人、Terraform 创作者)首次明确提出。他的核心观点是:
每当发现 Agent 犯了一个错误,就花时间设计一个解决方案,确保这类错误永远不再发生。
这里的"解决方案"不是改 Prompt,不是换个模型,而是在 Agent 的运行环境中加约束、加工具、加反馈、加护栏——这套包裹在模型外面的完整系统,就叫 Harness(缰绳/安全带)。
1.3 为什么现在这么火?
OpenAI 的 Codex 团队用 3 个工程师、5 个月时间,写出了百万行级别的生产代码。他们能做到这件事的核心原因不是模型更强了,而是把 Harness 工程做到了极致——Agent 在一个高度约束、高度自动化的环境中工作,犯错率极低,产出极高。
2026 年初,多家科技公司披露 AI 项目延期,原因不是模型能力不够,而是 Harness 工程没到位——Agent 在不受约束的环境中频繁出错,人工兜底成本远超预期。
结论:模型决定上限,Harness 决定下限。
二、三者到底啥区别?一图讲透
2.1 层级关系
三者不是并列关系,而是一层套一层的包含关系:
┌─────────────────────────────────────────────┐
│ Harness Engineering │
│ ┌───────────────────────────────────────┐ │
│ │ Context Engineering │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ Prompt Engineering │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └───────────────────────────────────────┘ │
│ │
│ + 执行能力(工具、沙箱、命令) │
│ + 任务编排(流程控制、多步协调) │
│ + 反馈机制(验证、纠错、质量门禁) │
│ + 架构护栏(约束、权限、安全边界) │
└─────────────────────────────────────────────┘
2.2 对比表
| 维度 | Prompt Engineering | Context Engineering | Harness Engineering |
|---|---|---|---|
| 关注点 | 怎么跟 AI 说清楚需求 | 给 AI 提供什么信息 | 给 AI 搭什么工作系统 |
| 核心动作 | 调措辞、改指令 | 管上下文、控信息流 | 加约束、加工具、加护栏 |
| 解决的问题 | AI 听不懂/理解偏 | AI 信息不足/信息过载 | AI 乱操作/不可靠/闯祸 |
| 类比 | 教你怎么跟实习生说话 | 给实习生准备参考资料 | 给实习生配完整工作台 |
| 生效范围 | 单次对话 | 单次会话 | 整个工程生命周期 |
| 代表实践 | System Prompt、Few-shot | CLAUDE.md、RAG | AGENTS.md、工具白名单、CI 门禁 |
2.3 一个具体例子
你要让 AI 帮你改一个用户认证模块的 Bug:
- Prompt 层:“请修复 src/auth/login.ts 中的 token 过期验证逻辑,确保 refresh token 在 access token 过期后自动刷新。”
- Context 层:在 CLAUDE.md 中写明项目使用 JWT + Redis 方案,token 过期时间配置在 config/auth.ts,相关测试在 tests/auth/ 目录。
- Harness 层:规定 Agent 只能修改 auth/ 目录下的文件,改完必须跑
npm test,测试不通过就自动回滚,改动必须生成 PR 而不能直接 push 到 main 分支。
看到区别了吗?Prompt 是"说什么",Context 是"给什么信息",Harness 是"怎么管住它"。
三、五大核心模块拆解
一个完整的 Harness 系统由五个核心模块组成。下面逐个拆解,每个都配实际场景。
3.1 模块一:上下文架构(Context Architecture)
核心原则:Agent 应当恰好获得当前任务所需的上下文——不多不少。
给太少,Agent 不懂项目背景,瞎改一通;给太多,Agent 被信息淹没,抓不住重点。上下文架构就是解决"给什么、给多少、什么时候给"的问题。
分层策略:
| 层级 | 加载时机 | 内容示例 | 上下文占用 |
|---|---|---|---|
| Tier 1:会话常驻 | 每次会话自动加载 | AGENTS.md / CLAUDE.md、项目结构概览 | 最小 |
| Tier 2:按需加载 | 特定任务被触发时 | API 文档、数据库 Schema、设计文档 | 中等 |
| Tier 3:临时注入 | 当前任务需要时 | 报错日志、Git Diff、特定文件内容 | 较大 |
实际场景:
你让 Agent 修一个支付接口的 Bug。Tier 1 的 CLAUDE.md 告诉它项目用 TypeScript + Express;Tier 2 在它打开支付模块时自动加载 Stripe API 文档和订单数据库 Schema;Tier 3 在它遇到报错时注入具体的错误日志。
关键操作: 在项目根目录创建 CLAUDE.md,写入项目核心信息:
# 项目概览
- 技术栈:TypeScript + Express + PostgreSQL
- 包管理:pnpm
- 测试框架:Vitest
# 代码规范
- 使用严格 TypeScript,禁止 any
- API 路由定义在 src/routes/
- 数据库操作只通过 src/repositories/ 层
# 常见约束
- 不要修改 migrations/ 目录下的已有迁移文件
- 环境变量通过 .env.local 管理,不要硬编码
3.2 模块二:执行能力(Execution Capability)
核心原则:Agent 不只是"说",还得能"做"——而且是在受控环境中做。
光有脑子不行,还得有手。执行能力就是给 Agent 装上"手":读写文件、运行命令、调用 API、操作数据库。但这些能力必须在受控范围内,不能让它想干啥干啥。
执行能力的三个层次:
| 层次 | 能力 | 示例 |
|---|---|---|
| 工具调用 | Agent 可调用的预定义函数 | 读写文件、搜索代码、执行终端命令 |
| 沙箱环境 | 隔离的执行空间 | 本地沙箱、Docker 容器、远程虚拟机 |
| 权限控制 | 限制 Agent 能做什么 | 文件白名单、命令黑名单、网络隔离 |
实际场景:
Claude Code 的执行能力设计:Agent 可以读写项目文件、执行 shell 命令、调用 Git 操作,但所有操作都在用户本地的项目目录内进行,且危险操作(如 rm -rf、DROP TABLE)会被拦截或要求确认。
关键操作: 在 CLAUDE.md 中声明工具权限:
# 工具权限
- 允许执行:git, npm, pnpm, vitest, eslint, tsc
- 禁止执行:rm -rf, DROP, TRUNCATE, 直接操作生产数据库
- 文件修改范围:仅限 src/ 和 tests/ 目录
3.3 模块三:任务编排(Task Orchestration)
核心原则:复杂任务不是一步完成的,需要拆解、调度、协调。
Agent 拿到一个大任务(比如"重构整个认证模块"),不能一上来就动手改代码。它需要先拆成小步骤,按顺序执行,每步验证后再进行下一步,遇到问题能回退或调整策略。
编排模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| 线性编排 | A → B → C → D,逐步执行 | 简单的修 Bug、加功能 |
| 条件编排 | 根据上一步结果决定下一步 | 需要判断的场景(测试通过才提交) |
| 并行编排 | 多个子任务同时进行 | 互不依赖的修改(改前端 + 改后端) |
| 递归编排 | Agent 自己拆解子任务并编排 | 复杂的重构、多模块改动 |
实际场景:
你要让 Agent 实现"用户注册功能"。线性编排如下:
1. 分析需求 → 2. 设计数据模型 → 3. 写数据库迁移 → 4. 实现 API 接口
→ 5. 写单元测试 → 6. 跑测试验证 → 7. 更新 API 文档 → 8. 提交 PR
每一步完成后,Agent 检查结果,确认无误再进入下一步。如果第 6 步测试失败,自动回到第 4 步修复代码。
关键操作: 在 CLAUDE.md 中定义任务流程:
# 任务流程
1. 任何代码修改前,先阅读相关现有代码
2. 修改后必须运行 pnpm test 确认测试通过
3. 新功能必须附带测试用例
4. 所有修改通过 PR 提交,不要直接 push 到 main
3.4 模块四:反馈机制(Feedback Mechanism)
核心原则:Agent 每做一步,都要有反馈告诉它做得对不对。
没有反馈,Agent 就是在黑暗中摸索。反馈机制就是给 Agent 装"眼睛":代码能不能编译、测试能不能通过、Lint 有没有报错、功能是否符合预期。
反馈类型:
| 类型 | 来源 | 示例 |
|---|---|---|
| 编译反馈 | 编译器/类型检查 | TypeScript 报错、构建失败 |
| 测试反馈 | 自动化测试 | 单元测试失败、集成测试不通过 |
| Lint 反馈 | 代码检查工具 | ESLint 报错、格式不符合规范 |
| 运行反馈 | 实际运行结果 | 接口返回 500、页面白屏 |
| 人工反馈 | 人类审查 | Code Review 意见、需求变更 |
实际场景:
Agent 修改了一段代码后,Harness 自动执行以下反馈链:
tsc 编译 → ESLint 检查 → Vitest 测试 → 手动 Review
↓ ↓ ↓ ↓
通过 ✅ 通过 ✅ 失败 ❌ ——
↓
Agent 自动修复 → 重新测试 → 通过 ✅ → 进入 Review
关键操作: 配置自动化的反馈流水线:
# 反馈流程
- 每次文件保存后自动运行 tsc --noEmit
- 每次代码修改后自动运行 pnpm test
- ESLint 错误视为阻断性错误,必须修复
- 测试覆盖率不得低于现有水平
3.5 模块五:架构护栏(Architecture Guardrails)
核心原则:有些事 Agent 永远不能做,有些边界永远不能越。
护栏是 Harness 的最后一道防线。不管 Agent 多聪明,有些红线不能碰:不能删生产数据、不能绕过安全检查、不能违反架构规范。护栏不是"建议",是"硬约束"。
护栏类型:
| 类型 | 说明 | 示例 |
|---|---|---|
| 文件护栏 | 限制可修改的文件范围 | 禁止修改 config/、migrations/ |
| 命令护栏 | 限制可执行的命令 | 禁止 rm -rf、sudo、curl 外部服务 |
| 架构护栏 | 限制代码架构选择 | 禁止引入新依赖、禁止跨层调用 |
| 安全护栏 | 限制敏感操作 | 禁止硬编码密钥、禁止提交 .env 文件 |
| 流程护栏 | 限制操作流程 | 必须通过 PR、必须通过 CI |
实际场景:
Mitchell Hashimoto 在 Ghostty 项目中的 AGENTS.md 就是典型的架构护栏实践。这个文件里每一条规则都对应一次 Agent 犯过的错误——Agent 犯了新类型的错,就加一条新规则。这个文件会持续增长,形成一道越来越厚的防护墙。
关键操作: 在 CLAUDE.md 中设置护栏:
# 护栏规则
- 禁止修改 prisma/migrations/ 下的已有迁移文件
- 禁止在 src/ 之外创建新目录
- 禁止引入未在 package.json 中声明的新依赖
- 禁止在代码中出现硬编码的密钥、Token、密码
- 禁止直接操作 main 分支,所有改动走 PR
- 数据库查询必须通过 Repository 层,禁止在路由中直接写 SQL
四、实战项目:用 Harness 思维开发一个 Todo API
光说不练假把式。下面我们用一个真实的实战项目,带你走完 Harness 在方案设计 → 编码开发 → 测试验证 → 功能扩展四个阶段的全流程。
项目背景: 用 Node.js + Express + SQLite 开发一个 Todo REST API,支持增删改查。
4.1 阶段一:方案设计——先搭 Harness,再写代码
很多人拿到需求就开写,结果 Agent 到处乱改、反复出错。正确做法是先把 Harness 搭好,再让 Agent 干活。
Step 1:创建项目骨架
mkdir todo-api && cd todo-api
npm init -y
npm install express better-sqlite3
npm install -D vitest eslint typescript @types/node @types/express
Step 2:搭建目录结构
todo-api/
├── src/
│ ├── index.ts # 入口文件
│ ├── routes/
│ │ └── todos.ts # 路由定义
│ ├── repositories/
│ │ └── todoRepo.ts # 数据库操作层
│ ├── services/
│ │ └── todoService.ts # 业务逻辑层
│ └── types/
│ └── index.ts # 类型定义
├── tests/
│ └── todos.test.ts # 测试文件
├── CLAUDE.md # ← Harness 核心配置
├── package.json
└── tsconfig.json
Step 3:编写 CLAUDE.md(这是最关键的一步)
# Todo API 项目
## 技术栈
- Runtime: Node.js + TypeScript
- Framework: Express
- Database: SQLite (better-sqlite3)
- Test: Vitest
- Lint: ESLint
## 项目结构
- src/routes/ — 路由定义,只处理 HTTP 请求/响应
- src/services/ — 业务逻辑,不直接操作数据库
- src/repositories/ — 数据库操作,不包含业务逻辑
- src/types/ — TypeScript 类型定义
## 代码规范
- 严格 TypeScript,禁止 any
- 三层架构:Route → Service → Repository,禁止跨层调用
- 每个 API 端点必须有对应的测试用例
- 错误处理统一使用自定义 AppError 类
## 护栏规则
- 禁止在 routes/ 中直接写 SQL
- 禁止在 services/ 中直接操作数据库
- 禁止修改 package.json 中的依赖(需人工确认)
- 数据库文件存放在 data/todo.db,禁止修改路径
- 所有 API 响应遵循 { success: boolean, data?: any, error?: string } 格式
## 开发流程
1. 先在 types/ 中定义类型
2. 在 repositories/ 中实现数据操作
3. 在 services/ 中实现业务逻辑
4. 在 routes/ 中实现 API 端点
5. 在 tests/ 中编写测试
6. 运行 pnpm test 确认通过
这一步的本质: 在 Agent 动手之前,就把上下文(项目结构、技术栈)、护栏(禁止跨层、禁止改依赖)、流程(开发顺序、测试要求)全部定义好。这就是 Harness 思维——先建轨道,再让火车跑。
4.2 阶段二:编码开发——让 Agent 在轨道上跑
现在 Harness 搭好了,让 Agent 开始写代码。
给 Agent 的指令:
请根据 CLAUDE.md 中的项目规范,实现 Todo API 的完整功能:
1. 在 src/types/index.ts 中定义 Todo 类型
2. 在 src/repositories/todoRepo.ts 中实现 CRUD 数据操作
3. 在 src/services/todoService.ts 中实现业务逻辑
4. 在 src/routes/todos.ts 中定义 API 路由
5. 在 src/index.ts 中组装 Express 应用
Agent 在 Harness 约束下的工作过程:
读取 CLAUDE.md → 理解三层架构约束
↓
定义 Todo 类型(id, title, completed, createdAt)
↓
实现 Repository 层(CRUD 操作,直接操作 SQLite)
↓
实现 Service 层(调用 Repository,处理业务逻辑)
↓
实现 Route 层(调用 Service,处理 HTTP 请求响应)
↓
组装 Express 应用,注册路由
因为 CLAUDE.md 中明确规定了三层架构和禁止跨层调用,Agent 不会在 Route 里直接写 SQL,也不会在 Service 里直接操作数据库。护栏在默默生效。
Agent 生成的核心代码示例:
src/types/index.ts:
export interface Todo {
id: number;
title: string;
completed: boolean;
createdAt: string;
}
export interface CreateTodoInput {
title: string;
}
export interface UpdateTodoInput {
title?: string;
completed?: boolean;
}
src/repositories/todoRepo.ts:
import Database from 'better-sqlite3';
import { Todo, CreateTodoInput, UpdateTodoInput } from '../types';
const db = new Database('data/todo.db');
db.exec('CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY, title TEXT NOT NULL, completed INTEGER DEFAULT 0, createdAt TEXT DEFAULT (datetime("now")))');
export const todoRepo = {
findAll: (): Todo[] => db.prepare('SELECT * FROM todos').all().map(row => ({ ...row, completed: !!row.completed })),
findById: (id: number): Todo | undefined => { const row = db.prepare('SELECT * FROM todos WHERE id = ?').get(id); return row ? { ...row, completed: !!row.completed } : undefined; },
create: (input: CreateTodoInput): Todo => { const result = db.prepare('INSERT INTO todos (title) VALUES (?)').run(input.title); return todoRepo.findById(result.lastInsertRowid as number)!; },
update: (id: number, input: UpdateTodoInput): Todo | undefined => { const sets = []; const values = []; if (input.title !== undefined) { sets.push('title = ?'); values.push(input.title); } if (input.completed !== undefined) { sets.push('completed = ?'); values.push(input.completed ? 1 : 0); } if (sets.length === 0) return todoRepo.findById(id); values.push(id); db.prepare(`UPDATE todos SET ${sets.join(', ')} WHERE id = ?`).run(...values); return todoRepo.findById(id); },
delete: (id: number): boolean => db.prepare('DELETE FROM todos WHERE id = ?').run(id).changes > 0,
};
4.3 阶段三:测试验证——反馈机制自动生效
代码写完了,Harness 的反馈机制开始工作。
给 Agent 的指令:
请为 Todo API 编写完整的测试用例,覆盖所有 CRUD 操作和边界情况。
Agent 在 Harness 约束下的工作:
它知道测试框架是 Vitest(从 CLAUDE.md 读取),知道每个端点必须有测试(护栏要求),于是生成:
tests/todos.test.ts:
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import { app } from '../src/index';
describe('Todo API', () => {
beforeEach(async () => {
// 每个测试前清空数据库
});
it('应该创建一个新 Todo', async () => {
const res = await request(app).post('/api/todos').send({ title: '测试任务' });
expect(res.body.success).toBe(true);
expect(res.body.data.title).toBe('测试任务');
expect(res.body.data.completed).toBe(false);
});
it('应该获取所有 Todo', async () => {
await request(app).post('/api/todos').send({ title: '任务1' });
await request(app).post('/api/todos').send({ title: '任务2' });
const res = await request(app).get('/api/todos');
expect(res.body.data).toHaveLength(2);
});
it('应该更新 Todo', async () => {
const created = await request(app).post('/api/todos').send({ title: '待完成' });
const res = await request(app).put(`/api/todos/${created.body.data.id}`).send({ completed: true });
expect(res.body.data.completed).toBe(true);
});
it('应该删除 Todo', async () => {
const created = await request(app).post('/api/todos').send({ title: '待删除' });
const res = await request(app).delete(`/api/todos/${created.body.data.id}`);
expect(res.body.success).toBe(true);
});
it('创建时缺少 title 应该返回错误', async () => {
const res = await request(app).post('/api/todos').send({});
expect(res.body.success).toBe(false);
});
});
运行测试:
pnpm test
如果测试失败,Agent 会根据反馈自动修复代码,直到所有测试通过。这就是反馈闭环:写代码 → 跑测试 → 失败 → 修复 → 再测试 → 通过。
4.4 阶段四:功能扩展——Harness 持续演进
项目跑起来了,现在要加新功能:给 Todo 加上优先级和分类标签。
错误做法: 直接让 Agent 开干,它可能乱改数据库、破坏现有接口。
正确做法: 先更新 Harness,再让 Agent 干活。
Step 1:更新 CLAUDE.md
在原有内容基础上追加:
## 新增功能:优先级与标签
### 数据库变更
- 新增 priority 字段(枚举:low / medium / high,默认 medium)
- 新增 tags 字段(JSON 字符串,存储标签数组)
- 必须创建新的迁移文件,禁止修改已有表结构
### API 变更
- GET /api/todos 支持 ?priority=high&tag=work 筛选参数
- 响应格式保持 { success, data, error } 不变
### 护栏
- 禁止删除已有字段
- 现有测试必须继续通过
- 新增字段必须有默认值,确保向后兼容
Step 2:让 Agent 在更新后的 Harness 下工作
请根据 CLAUDE.md 中新增的需求,为 Todo 添加优先级和标签功能。
注意遵守所有护栏规则,确保现有功能不受影响。
Agent 的工作流程:
读取更新后的 CLAUDE.md → 理解新需求和护栏
↓
更新 Todo 类型(新增 priority、tags 字段)
↓
创建数据库迁移(新增字段,设默认值,不改已有数据)
↓
更新 Repository 层(新增筛选查询)
↓
更新 Service 层(新增筛选逻辑)
↓
更新 Route 层(新增查询参数处理)
↓
运行全量测试 → 确认旧测试通过 + 新测试通过
关键点: 因为 Harness 中规定了"禁止修改已有表结构"和"现有测试必须继续通过",Agent 会选择新增字段而非修改字段,会确保向后兼容。护栏再次默默生效。
五、核心心法:Harness 的三条铁律
跑完实战项目,总结三条贯穿始终的铁律:
铁律一:约束优于指令
与其告诉 Agent “请小心不要跨层调用”,不如直接在 CLAUDE.md 中写"禁止跨层调用"并在代码审查中拦截。硬约束 > 软建议,能自动拦截的就不要靠 Agent 自觉。
铁律二:每次犯错加一条规则
Mitchell Hashimoto 的做法:Agent 每犯一个新类型的错误,就在 AGENTS.md 中加一条规则。这个文件会越来越长,但 Agent 犯错的概率会越来越低。Harness 是活的,不是一次性的。
铁律三:先搭轨道再让火车跑
不要一上来就让 Agent 写代码。先把项目结构、代码规范、护栏规则、开发流程在 CLAUDE.md 中定义好,再让 Agent 开始工作。前期多花 30 分钟搭 Harness,后期省 3 小时修 Bug。
六、不同工具的 Harness 实践对照
| 工具 | Harness 配置文件 | 核心能力 | 适合场景 |
|---|---|---|---|
| Claude Code | CLAUDE.md | 上下文管理 + 工具调用 + 沙箱执行 | 全栈开发、项目级编码 |
| Cursor | .cursorrules | 上下文管理 + 编辑器集成 | 前端开发、快速迭代 |
| Codex | AGENTS.md | 任务编排 + CI 集成 + PR 管理 | 大规模代码生成、团队协作 |
| Windsurf | .windsurfrules | 上下文管理 + 多文件编辑 | 全栈开发 |
不管用什么工具,Harness 的核心思想不变: 约束行为、提供上下文、建立反馈、设置护栏。
七、从今天开始:你的 Harness 行动清单
- 今天: 在你当前项目根目录创建一个
CLAUDE.md,写上项目技术栈、目录结构、代码规范 - 本周: 每次发现 Agent 犯错,就在 CLAUDE.md 中加一条规则,持续积累
- 本月: 为项目配置自动化测试和 Lint,让反馈机制自动运转
- 长期: 把 CLAUDE.md 当成项目的"活文档",随项目演进持续更新
记住:Harness 不是一次性搭建的,而是在一次次犯错中长出来的。 每一条规则背后,都是一次真实的踩坑。这就是 Mitchell Hashimoto 说的——“每当 Agent 犯错,就花时间设计一个解决方案,确保这类错误永远不再发生。”
一句话带走: Prompt Engineering 教你怎么说,Context Engineering 教你给什么信息,Harness Engineering 教你怎么管住 AI 让它可靠干活。三者一层套一层,Harness 是最外层也是最重要的一层——因为模型决定上限,Harness 决定下限。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)