Harness 是什么
先给结论
模型负责生成候选决策,Agent 负责围绕目标循环调用模型和工具,Harness 负责把这套循环变成一个可运行、可约束、可恢复、可评测的系统。
如果只有一个聊天窗口,模型可能会给出一段看起来合理的代码;如果有 Harness,系统还会负责:
- 收集仓库规则和任务上下文;
- 选择并限制可用工具;
- 保存会话和执行轨迹;
- 在修改后运行格式化、测试和安全检查;
- 把失败结果反馈给 Agent;
- 在高风险操作前暂停并等待批准。
五个核心部件
Model
Model 处理语言、代码和结构化输出。它不天然知道仓库约定,也不应该直接拥有所有系统权限。
Agent
Agent 是目标、状态、模型和工具组成的循环。它会根据当前上下文决定下一步是继续分析、调用工具、修改文件,还是结束任务。
Harness
Harness 是 Agent 的运行时。它提供上下文构建、工具注册、权限、状态、重试、日志、审批、评测和恢复机制。
Tool
Tool 是一个有明确输入和输出的动作,例如读取文件、搜索符号、运行测试或创建 Pull Request。工具的描述应足够精确,输入应做 schema 校验。
Feedback
Feedback 是执行结果,不是模型的自我评价。编译错误、测试失败、代码审查意见和用户确认都可以成为下一轮上下文。
一次代码任务如何运行
text
用户:为订单接口增加幂等键支持
1. Harness 读取仓库规则、相关文件和测试命令
2. Agent 规划:定位接口、数据模型、迁移和测试
3. Harness 只开放读取、编辑和测试工具
4. Agent 修改代码
5. Hook 执行格式化和单元测试
6. 测试失败 -> 错误摘要回到 Agent
7. 测试通过 -> Reviewer Subagent 检查风险
8. Harness 输出 diff、测试结果和未决事项最小伪代码
go
for state := NewTaskState(goal); !state.Done(); {
context := BuildContext(state, repositoryRules, recentOutputs)
decision := model.Decide(context, availableTools)
if decision.NeedsApproval() {
approval := guardrail.Request(decision)
if !approval.Allowed {
state.Stop("operation rejected")
break
}
}
result := executor.Call(decision.Tool, decision.Input)
state.Append(decision, result)
if verifier.HasBlockingFailure(result) {
state.AddFeedback(verifier.Summarize(result))
}
}Harness 设计的四个边界
把规则放在上下文,把门禁放在 Hook
“请记得运行测试”适合写入规则;“未通过测试不能结束”应由 Hook 或 Runner 强制执行。前者依赖模型遵循,后者是确定性约束。
把事实放在工具结果,不放在提示词猜测
仓库状态、测试结果、权限和当前分支都应从工具或运行时获取。不要把可能过期的状态硬编码进系统提示。
把高风险动作拆成可审查步骤
删除文件、写生产数据、发布镜像和推送代码应有清晰的预览、审批和审计记录。
把长任务拆成可恢复阶段
规划、实现、验证和审查各自保存状态。单次上下文耗尽时,可以从阶段检查点继续,而不是从头再来。
常见误区
| 误区 | 后果 | 改进 |
|---|---|---|
| 把所有仓库内容塞进上下文 | 成本高、重点丢失 | 按任务检索和分层加载 |
| 给 Agent 一个万能 Shell | 越权和破坏风险 | 工具白名单、参数校验、沙箱 |
| 只看最终代码,不存轨迹 | 无法复盘和评测 | 记录决策、工具调用和验证结果 |
| 只用提示词约束测试 | 模型可能跳过 | 用 Hook/CI 做硬门禁 |
| 只追求一次成功 | 版本升级后回归 | 建立任务集和持续评测 |
练习
- 为一个 Go HTTP 接口画出 Context、Model、Tool、Verifier 的调用链。
- 列出 Java 服务中三个应该只读、三个需要审批的工具。
- 设计一个“测试失败后最多自动修复两次”的状态机。
