Harness 在普通英语里有“套具”“马具”“线束”的意思,放到 AI 工程语境里,直接翻译成其中任何一个都不太准确。它不是一个单独的提示词,也不是某个模型能力,而是包在 Agent(智能体)外层的一整套运行框架。
更准确地说,Agent Harness 负责给智能体提供:
- 可执行的工作环境;
- 清晰的任务边界;
- 可读写的上下文材料;
- 工具调用入口;
- 自动化测试和评估;
- 多个智能体之间的协作方式;
- 防止越界操作的安全约束。
可以把它理解成“智能体的运行容器 + 调度控制器 + 安全护栏 + 评估系统”。
flowchart TB
U[用户需求] --> H[Agent Harness]
H --> P[规划 Agent]
H --> G[生成 Agent]
H --> E[评估 Agent]
P --> D[(结构化文档 / 任务计划)]
D --> G
G --> FS[(代码仓库 / 文件系统)]
G --> T[工具调用<br/>Shell / Browser / API]
FS --> C[自动检查<br/>测试 / Linter / 类型检查 / CI]
T --> C
C --> E
E -->|通过| R[提交结果]
E -->|失败| F[反馈与修复任务]
F --> G
H --> M[(进度文件 / Git 历史 / 日志)]
M --> P
M --> G
M --> E
Prompt(提示词)告诉模型“你要做什么”,Harness 决定模型“在什么环境里做、能用哪些工具、做错后怎么发现、越界时怎么拦住、任务太长时如何保持连续性”。
这就是它被重视的原因。大语言模型能力越强,越有可能被交给更长、更复杂、更接近真实工程的任务;任务一旦变长,单靠一句提示词就很难维持质量。
为什么长周期 Agent 需要 Harness
大语言模型(Large Language Model,LLM)擅长生成局部内容,但长周期任务会暴露出几个稳定问题。
| 问题 | 表现 | Harness 的处理方式 |
|---|---|---|
| 上下文窗口有限 | 任务越做越长,模型忘记早期约定,甚至急着收尾 | 用结构化文档、进度文件、Git 历史和上下文压缩保存关键状态 |
| 自我评估偏宽 | 代码看起来完成了,但功能不可用;模型检查自己时也容易放过问题 | 引入独立评估 Agent,并接入真实测试、浏览器点击、自动化验收 |
| 复杂任务容易偏航 | 做着做着开始修改无关模块,或者生成不符合架构的实现 | 用任务分解、模块边界、依赖规则和合约约束控制范围 |
| 文档容易过时 | 长 AGENTS.md 或规则文件没人维护,反而误导模型 | 把文档拆成版本化文件,并让专门的文档维护 Agent 定期更新 |
| 工具调用有风险 | 模型可能执行破坏性命令、读取敏感文件、访问不该访问的网络 | 用沙箱、权限白名单、命令拦截和审计日志限制操作 |
“上下文焦虑”是一个很形象的说法:当模型意识到上下文窗口快被填满时,它可能倾向于快速结束任务,而不是继续耐心修复细节。更麻烦的是,模型通常不会承认自己在敷衍,它会把不完整的结果包装成“已经完成”。
这种问题很难靠“请认真一点”“不要偷懒”解决,因为它不是礼貌程度的问题,而是运行机制的问题。Harness 的思路不是继续堆提示词,而是改变智能体的工作组织方式。
Harness 的核心结构
一个实用的 Agent Harness 通常包含五层。
flowchart LR
A[任务入口] --> B[规划层]
B --> C[执行层]
C --> D[评估层]
D --> E[反馈层]
E --> C
B --> S[(状态与上下文)]
C --> S
D --> S
C --> X[工具与环境]
X --> Y[安全边界]
任务入口:把自然语言需求变成可管理任务
用户给出的需求往往很粗,例如:
做一个网页小游戏编辑器,支持角色移动、关卡配置和预览。
如果直接丢给一个代码 Agent,它很可能先写界面,再补一点逻辑,最后给出一个“看起来差不多”的结果。Harness 会先把需求转成更细的规格:
- 角色需要支持哪些移动方式;
- 关卡配置的数据结构是什么;
- 预览模式和编辑模式如何切换;
- 哪些功能必须有自动化测试;
- 哪些交互需要浏览器端验证;
- 哪些文件可以改,哪些文件不能改。
这一步通常由规划 Agent 完成。规划 Agent 不负责写代码,只负责把模糊需求拆成可执行任务。
执行层:让生成 Agent 只做一件事
生成 Agent 的职责越混杂,越容易失控。较稳的做法是让它只根据明确规格生成代码、修改文件、运行命令,而不是同时承担产品、架构、测试和验收。
一个常见闭环是:
sequenceDiagram
participant User as 用户
participant Harness as Harness
participant Planner as 规划 Agent
participant Generator as 生成 Agent
participant Evaluator as 评估 Agent
participant Tools as 测试/浏览器/CI
User->>Harness: 提交需求
Harness->>Planner: 生成规格和任务拆分
Planner-->>Harness: 返回计划
Harness->>Generator: 分配一个明确子任务
Generator->>Tools: 修改代码并运行检查
Tools-->>Generator: 返回执行结果
Generator-->>Harness: 提交补丁和说明
Harness->>Evaluator: 验收补丁
Evaluator->>Tools: 执行自动化测试和真实交互检查
Tools-->>Evaluator: 返回结果
Evaluator-->>Harness: 通过或打回
Harness->>Generator: 失败时生成修复任务
这个设计把“会不会写”和“写得对不对”分开。生成 Agent 负责产出,评估 Agent 负责挑错。两者使用不同提示词、不同标准,必要时还可以使用不同模型。
评估层:不能只问模型“你觉得对吗”
让模型审查自己的输出,经常会得到过于乐观的结论。Harness 里的评估层应该尽量接近真实环境,而不是只做语言层面的评论。
| 评估方式 | 能发现的问题 | 示例 |
|---|---|---|
| 单元测试 | 函数逻辑错误、边界条件遗漏 | pytest、go test、cargo test |
| Linter(代码检查工具) | 风格问题、潜在 bug、非法依赖 | ESLint、Ruff、Checkstyle |
| 类型检查 | 参数类型不匹配、空值风险 | TypeScript、mypy、Pyright |
| 浏览器自动化 | 页面按钮点不动、表单状态错误、路由异常 | Playwright、Puppeteer |
| 架构规则检查 | 跨层调用、模块循环依赖、破坏边界 | dependency-cruiser、自定义脚本 |
| 产品验收清单 | 功能缺失、交互不符合规格 | 逐项对照 PRD 或任务文档 |
真正有用的评估不是一句“请检查是否有问题”,而是带着标准去跑环境。
例如,网页小游戏编辑器不能只检查页面是否渲染,还要验证:
- 角色是否能响应键盘;
- 碰撞检测是否生效;
- 编辑模式保存后的关卡能否被预览模式读取;
- 刷新页面后配置是否丢失;
- 控制台是否出现运行时错误。
如果评估 Agent 发现失败,它不应该只给出“有 bug”,而要生成可执行反馈:
失败项:角色移动
复现步骤:
1. 打开 /editor
2. 点击 Preview
3. 按下 ArrowRight
期望结果:
角色向右移动至少 16px。
实际结果:
角色保持不动,控制台无报错。
可能原因:
键盘事件监听绑定在编辑模式容器上,进入预览模式后容器失去焦点。
建议修复:
将 keydown 监听绑定到 window,并在退出预览模式时移除监听。
这种反馈能让生成 Agent 进入下一轮修复,而不是重新猜测问题。
上下文层:把记忆放进系统,而不是塞进一次对话
长任务不能依赖一次超长上下文。更可靠的方式是把关键状态沉淀到仓库里,让模型每次需要时读取。
一种常见目录结构如下:
repo/
├── AGENTS.md
├── docs/
│ ├── architecture.md
│ ├── product-spec.md
│ ├── coding-rules.md
│ ├── decisions/
│ │ ├── 0001-state-management.md
│ │ └── 0002-api-boundary.md
│ └── tech-debt.md
├── harness/
│ ├── progress.md
│ ├── tasks/
│ │ ├── task-001.md
│ │ └── task-002.md
│ ├── evals/
│ │ ├── ui-checklist.md
│ │ └── architecture-checklist.md
│ └── scripts/
│ ├── run_all_checks.sh
│ └── check_dependencies.py
├── src/
├── tests/
└── package.json
AGENTS.md 不适合写成几千行规则大全。更好的做法是让它成为一个入口索引:
# AGENTS.md
你在这个仓库中工作时,必须先阅读以下文件:
- docs/product-spec.md:产品目标和功能范围
- docs/architecture.md:系统架构和模块边界
- docs/coding-rules.md:代码风格、测试要求和提交规则
- harness/progress.md:当前任务进度和未解决问题
工作规则:
1. 不要修改 docs/architecture.md 中标记为 stable 的接口,除非任务明确要求。
2. 每次修改后运行 `npm test` 和 `npm run lint`。
3. 如果新增跨模块依赖,必须运行 `node harness/scripts/check_dependencies.js`。
4. 任务完成前更新 harness/progress.md,记录改动、测试结果和遗留问题。
5. 不能读取 `.env`、`secrets/`、生产数据库配置。
这样做有两个好处:
- 规则更短,模型更容易遵守;
- 细节放在版本化文档里,可以被审查、更新和回滚。
约束层:把人类判断变成机器规则
工程团队经常会说“不要跨层调用”“不要把业务逻辑写进 UI 组件”“不要绕过服务层直接访问数据库”。人能听懂这些架构偏好,模型却很容易为了完成眼前任务走捷径。
Harness 需要把这些偏好转成硬约束。
例如,前端项目可以用依赖规则限制模块访问:
// dependency-rules.js
module.exports = {
forbidden: [
{
name: "ui-cannot-import-db",
from: { path: "^src/ui" },
to: { path: "^src/db" },
severity: "error",
comment: "UI 层不能直接访问数据库,只能调用 service 层。"
},
{
name: "domain-cannot-import-ui",
from: { path: "^src/domain" },
to: { path: "^src/ui" },
severity: "error",
comment: "领域层不能依赖 UI。"
}
]
}
生成 Agent 仍然可以自由写代码,但只要违反边界,检查就会失败,补丁无法进入主分支。规则不再只是建议,而是系统行为的一部分。
Anthropic 式 Harness:角色拆分和长任务闭环
在长时间运行的应用开发任务中,一个典型 Harness 会拆出三个角色:
| 角色 | 职责 | 不该做的事 |
|---|---|---|
| Planner | 把需求拆成规格、里程碑和验收标准 | 不直接写业务代码 |
| Generator | 根据规格实现功能、修复缺陷 | 不自行修改验收标准 |
| Evaluator | 运行测试、模拟用户、检查质量并打回问题 | 不替生成 Agent 写大段实现 |
这种拆分的价值在于减少“同一个模型既当运动员又当裁判”的问题。生成 Agent 可能会倾向于认为自己完成了任务,评估 Agent 则只关心结果是否满足标准。
长周期任务的结果通常会出现一个取舍:Harness 版本耗时更长、成本更高,但结果更接近可用软件。
| 对比项 | 无 Harness | 有 Harness |
|---|---|---|
| 工作方式 | 单个 Agent 直接从需求写到结束 | 规划、生成、评估循环推进 |
| 验收方式 | 主要依赖模型自述和局部检查 | 自动测试、真实交互、独立评估 |
| 常见结果 | 界面可能能看,但核心功能容易坏 | 功能完整度更高,缺陷更容易被发现 |
| 成本 | 较低 | 较高 |
| 时间 | 较短 | 较长 |
| 适合任务 | 低风险原型、一次性草稿 | 长任务、复杂应用、需要验收的工程交付 |
这说明 Harness 不是免费午餐。它通过更多轮次、更多工具调用和更严格验收换取稳定性。对于几分钟能完成的小任务,完整 Harness 可能过重;对于需要运行数小时甚至数天的工程任务,缺少 Harness 往往会在后期付出更高返工成本。
Codex 式 Harness:把工作流做成工程系统
另一个方向是把 Harness 做成团队工作流。核心思路不是让工程师把所有规则写进一个超长提示词,而是搭建一个让代码 Agent 能长期工作的工程环境。
关键做法包括:
AGENTS.md保持短,只作为导航入口;- 架构文档、产品规格、设计决策、技术债务都放进
docs/; - 文档和代码一样走版本管理;
- 用专门 Agent 定期扫描过时文档并提出更新;
- 所有代码变更必须经过测试、Linter、类型检查和 CI(Continuous Integration,持续集成);
- 用脚本强制检查模块边界,而不是只靠口头约定;
- 生产监控、内部工具、测试用例也纳入智能体可读写范围。
这种模式下,工程师的工作重心会发生变化:少一些逐行实现,多一些环境设计、约束设计、验收设计和异常处理。代码仍然重要,但“让 Agent 在正确轨道上持续产出”的能力变得更关键。
可以把它类比为给智能体搭建一个受控工厂:
flowchart TB
Spec[产品规格] --> Task[任务队列]
Arch[架构文档] --> Task
Debt[技术债务清单] --> Task
Task --> Codex[代码 Agent]
Codex --> Patch[代码补丁]
Patch --> Tests[测试]
Patch --> Lint[Linter]
Patch --> Types[类型检查]
Patch --> Boundary[依赖边界检查]
Tests --> Gate{质量门禁}
Lint --> Gate
Types --> Gate
Boundary --> Gate
Gate -->|通过| Merge[合并]
Gate -->|失败| Feedback[结构化反馈]
Feedback --> Task
Merge --> Docs[更新文档]
Docs --> Spec
Docs --> Arch
Docs --> Debt
在这个系统里,智能体不是凭感觉“帮你写点代码”,而是在一套明确规则中持续推进任务。规则写得越清楚,反馈越可执行,智能体越容易稳定工作。
一个最小可用 Agent Harness 怎么搭
不必一开始就做复杂平台。一个最小可用 Harness 可以从四件事开始:
- 任务规格文件;
- 自动化检查脚本;
- 独立评估步骤;
- 进度和决策记录。
1. 写清任务规格
# harness/tasks/task-001.md
## 目标
为小游戏编辑器增加预览模式,用户可以从编辑模式切换到预览模式,并控制角色移动。
## 功能要求
- 编辑模式中可以放置角色出生点。
- 点击 Preview 后进入预览模式。
- 预览模式中方向键控制角色移动。
- 点击 Exit Preview 返回编辑模式。
- 返回编辑模式后,地图配置不能丢失。
## 验收标准
- Playwright 测试覆盖模式切换和方向键移动。
- 控制台无运行时错误。
- `npm test` 通过。
- `npm run lint` 通过。
## 不允许修改
- 不修改 `src/domain/map-schema.ts` 的公开类型。
- 不引入新的全局状态库。
规格要具体到“怎么验收”,否则评估 Agent 很难判断完成度。
2. 准备统一检查脚本
#!/usr/bin/env bash
set -euo pipefail
npm run lint
npm test
npm run test:e2e
node harness/scripts/check_dependencies.js
生成 Agent 每次修改后都运行同一个脚本,评估 Agent 也运行同一个脚本。这样能减少“我这里能跑”的不确定性。
3. 定义评估 Agent 的输入输出
评估 Agent 不要自由发挥,可以要求固定格式:
# harness/evals/ui-checklist.md
你是评估 Agent,只判断补丁是否满足任务验收标准。
必须检查:
1. 功能是否完整。
2. 测试是否覆盖关键路径。
3. 是否违反架构约束。
4. 是否出现用户可见的交互问题。
5. 是否留下未说明的 TODO。
输出格式:
- result: pass 或 fail
- failed_items: 失败项列表
- reproduction: 可复现步骤
- evidence: 命令输出、截图描述或日志
- fix_suggestion: 面向生成 Agent 的修复建议
结构化输出比自然语言评论更适合进入下一轮自动修复。
4. 记录进度和决策
# harness/progress.md
## 当前任务
task-001:增加预览模式。
## 已完成
- 增加 Preview / Exit Preview 按钮。
- 增加 preview 状态。
- 增加角色出生点读取逻辑。
## 测试结果
- npm run lint:通过
- npm test:通过
- npm run test:e2e:失败
## 当前问题
Playwright 测试中,进入预览模式后方向键没有触发角色移动。
## 下一步
检查键盘事件监听绑定位置,优先确认焦点是否停留在 canvas 容器。
这份文件的作用类似短期记忆。上下文被重置后,新的 Agent 也能继续任务,而不是重新分析整个仓库。
简化版 Harness 伪代码
下面是一个极简流程,用来表达 Harness 的控制逻辑:
from dataclasses import dataclass
@dataclass
class EvalResult:
passed: bool
feedback: str
def call_agent(role: str, prompt: str, context: list[str]) -> str:
"""调用指定角色的模型。真实实现中可接入 Claude、Codex 或其他模型。"""
raise NotImplementedError
def run_checks() -> tuple[bool, str]:
"""运行测试、Linter、类型检查和架构规则。"""
raise NotImplementedError
def apply_patch(patch: str) -> None:
"""应用模型生成的代码修改。"""
raise NotImplementedError
def load_context() -> list[str]:
return [
read("AGENTS.md"),
read("docs/product-spec.md"),
read("docs/architecture.md"),
read("harness/progress.md"),
]
def evaluate(task: str, patch_summary: str, check_output: str) -> EvalResult:
context = load_context() + [
read("harness/evals/ui-checklist.md"),
f"任务:{task}",
f"补丁说明:{patch_summary}",
f"检查输出:{check_output}",
]
report = call_agent(
role="evaluator",
prompt="判断补丁是否满足验收标准,按固定格式输出。",
context=context,
)
passed = "result: pass" in report.lower()
return EvalResult(passed=passed, feedback=report)
def run_harness(task: str, max_rounds: int = 5) -> None:
context = load_context()
plan = call_agent(
role="planner",
prompt=f"把任务拆成可执行步骤,并写出验收标准:{task}",
context=context,
)
write("harness/current-plan.md", plan)
feedback = ""
for round_id in range(1, max_rounds + 1):
context = load_context() + [
read("harness/current-plan.md"),
feedback,
]
patch = call_agent(
role="generator",
prompt=f"执行第 {round_id} 轮修改,只解决当前任务。",
context=context,
)
apply_patch(patch)
checks_ok, check_output = run_checks()
result = evaluate(task, patch_summary=patch, check_output=check_output)
if checks_ok and result.passed:
write("harness/progress.md", "任务通过,记录最终修改和测试结果。")
return
feedback = result.feedback
write("harness/progress.md", f"第 {round_id} 轮失败:\n{feedback}")
raise RuntimeError("达到最大迭代次数,任务仍未通过。")
真实系统会更复杂,例如需要处理补丁冲突、工具调用权限、并发任务、日志追踪、成本控制和人工审批。但核心闭环就是:规划、执行、检查、评估、反馈、再执行。
Harness 适合什么场景
Harness 并不适合所有 AI 任务。它的价值主要出现在任务周期长、失败成本高、需要持续迭代的场景。
| 场景 | 是否适合 | 原因 |
|---|---|---|
| 复杂代码生成 | 适合 | 需要测试、架构约束和多轮修复 |
| 大型重构 | 适合 | 容易破坏模块边界,需要自动检查 |
| UI 自动生成 | 适合 | 需要浏览器验收和交互测试 |
| 文档维护 | 适合 | 可用 Agent 扫描代码变化并更新文档 |
| 一次性脚本 | 不一定 | 完整 Harness 可能比任务本身更重 |
| 简单问答 | 不适合 | 直接问模型更快 |
| 安全关键系统的无人值守修改 | 谨慎 | 必须有人类审批、权限隔离和回滚机制 |
| 需求还没稳定的产品探索 | 谨慎 | 规格频繁变化会让自动验收失效 |
判断是否需要 Harness,可以问三个问题:
- 任务会不会超过一次对话能稳定处理的范围?
- 错误是否能通过自动化方式发现?
- 任务失败后的返工成本是否高于搭建 Harness 的成本?
如果答案大多是“是”,Harness 通常有价值。
容易踩的坑
把 Harness 当成更长的提示词
长提示词只能增加模型一次调用时能看到的信息,不能解决评估、工具、安全和状态管理问题。Harness 的关键是系统闭环,而不是提示词长度。
评估标准太模糊
“代码质量要高”“界面要好看”“实现要优雅”都不是好标准。可执行标准应该像这样:
- 页面加载后 2 秒内出现编辑器;
- 点击 Preview 后工具栏隐藏;
- 按下 ArrowRight 后角色 x 坐标增加;
src/ui不允许导入src/db;- 所有新增函数必须有单元测试覆盖。
文档没人维护
过时文档比没有文档更危险,因为模型会认真遵守错误信息。文档需要纳入版本管理,并通过检查或专门 Agent 定期更新。
约束过细导致模型无法工作
Harness 不是把每一行实现都规定死。应该约束边界、接口、安全和验收标准,把局部实现空间留给生成 Agent。约束太松会失控,约束太细会让模型陷入反复失败。
忽略安全边界
只要 Agent 能执行命令,就必须考虑权限问题。最低限度要做到:
- 在沙箱中运行;
- 默认禁止读取密钥文件;
- 高风险命令需要人工确认;
- 网络访问使用白名单;
- 所有工具调用写入日志;
- 重要变更可回滚。
Harness 该怎么翻译
“执行框架”强调运行,但少了约束和安全边界;“管控层”强调约束,但少了工具环境和任务闭环;“脚手架”强调支撑结构,但不够表达评估和调度;“线束”在汽车和硬件行业已有固定含义,迁移到 AI 语境容易误解。
在技术交流中,比较稳妥的写法是:
Harness(智能体运行框架)
第一次出现时解释清楚,后面直接用 Harness。它在 AI 工程里同时包含环境、执行、约束、调度、评估和上下文管理,很难用一个中文词完整覆盖。
核心结论
Agent Harness 解决的不是“让模型更聪明”,而是“让模型在复杂任务中更可靠”。
模型负责生成,Harness 负责组织工作方式。它把人类工程师原本散落在经验里的判断,转成可执行的系统规则:哪些文档要读,哪些边界不能越界,哪些测试必须通过,失败后如何反馈,任务中断后如何恢复。
随着模型能力增强,Harness 不会消失,只会承担更高层的任务。简单的上下文修补可能被新模型内化,但更复杂的协作、评估、安全和工程治理仍然需要外部系统承载。真正稳定的 Agent 应用,往往不是靠一个强提示词跑出来的,而是靠一套能持续纠错、持续约束、持续积累上下文的 Harness 运行起来的。