Harness Engineering
术语边界: Harness Engineering 不是跨厂商统一学科定义. 本章用它指围绕 coding agent 配置 instructions, context, tools, sandbox/permissions, tests/evals, approval 和 audit 的工程系统. 方法是否有效必须由固定任务集和 CI/eval 结果证明, 不能只靠一次演示.
适用范围: 本章用于 coding-agent harness 工程实践; 本章工作定义不构成跨厂商标准, 也不代表任一客户端版本或能力. 核验日期与来源见文末.
本章聚焦 agent 的 “运行环境与护栏”. 概览见 Vibe Coding, context/MCP/安全治理见 AI Coding 工程化进阶, 反馈循环见 Loop Engineering.
学习目标
能设计一个用于阻止范围外写入, 评估小型任务并保留证据的最小 Harness 目录与配置契约草图. 文件名和 YAML 字段是团队示例, 不是所有 agent 客户端都自动支持的产品事实.
一, Harness 的核心构成
| 构成 | 最小内容 | 失败时的风险 |
|---|---|---|
| Instructions | 项目规则, 架构边界, 禁止事项, 完成定义 | agent 按通用经验破坏本地约定 |
| Context/State | 当前任务, 决策, 进度, 非显然约束 | 跨会话遗忘或使用过期事实 |
| Tools | 读写代码, 搜索, 构建, 测试, 设备或浏览器 | 只能猜测, 或工具能力失控 |
| Sandbox/Permissions | 可读写路径, 网络, 秘密和命令白名单 | 越权读取, 破坏性写入或数据泄露 |
| Tests/Evals | 固定任务, 断言, 质量指标, 回归基线 | Demo 成功被误当成稳定能力 |
| Approval | 发布, 密钥, 权限, 依赖升级等人工门禁 | 高风险决策无人负责 |
| Audit | prompt/context/tool call/diff/命令/结果记录 | 结果不可复现, 事故无法追踪 |
AGENTS.md 与 CLAUDE.md 不能当作等价的通用标准:
AGENTS.md是面向 coding agents 的开放格式, 具体客户端支持范围需查其文档.CLAUDE.md是 Claude Code 的产品约定, 其加载和作用域语义以 Anthropic 官方文档为准.- 团队可以从同一事实源生成不同客户端文件, 但不能假设所有工具会读取同名文件或采用相同优先级.
二, 最小 Harness 目录与配置契约草图
repo/
├── AGENTS.md # 项目规则,可改范围,完成定义
├── docs/architecture.md # 架构和依赖方向
├── agent/
│ ├── task.yaml # goal/non-goal/owner/risk/acceptance
│ ├── permissions.yaml # 可写路径,网络,命令和审批点
│ └── eval-cases.yaml # 固定任务集,预期结果和评分规则
└── scripts/
├── run-harness.sh # 拟实现:解析策略,调用 agent,执行断言并产出结果
├── verify-change.sh # 面向任务的测试/检查入口
└── collect-evidence.sh # 保存命令,版本,diff 和结果
<!-- AGENTS.md: team example; a future runner must read this file explicitly. -->
# Search harness rules
- Only modify paths permitted by `agent/permissions.yaml` for the current task.
- Do not read secrets, request network access, or execute commands outside the policy allowlist.
- Return a patch plus the evidence required by `task.yaml`; report blocked and unrun work instead of bypassing policy.
一个最小任务记录应包含:
goal: 修复搜索页旋转后重复请求
non_goals: 不升级依赖, 不重写导航
write_scope: [feature/search]
required_checks: [unit_test, process_recreation_test]
approval_required: [dependency_change, manifest_change]
audit: [model_version, tool_version, commands, diff, results]
若实现 run-harness.sh, 其最小输入应为 task.yaml, permissions.yaml, eval-cases.yaml, 固定 base_ref 与 agent 输出的 patch; 其最小输出应为机器可读的 assertion-results.json 和审计事件流. 该 runner 必须在应用 patch 前后分别计算 canonical path 的变更集, 执行声明的检查, 并以输出中的 passed 决定是否允许该 case 通过.
{
"case_id": "search-state-restoration",
"base_ref": "<pinned-commit>",
"patch_id": "sha256:<patch-bytes>",
"assertions": [
{"id": "changed_paths_subset_of_allowed_paths", "required": true, "passed": true, "evidence": "changed-paths.txt"},
{"id": "regression_test", "required": true, "passed": true, "evidence": "verify-change.log"},
{"id": "no_dependency_or_manifest_change", "required": true, "passed": true, "evidence": "changed-paths.txt"}
],
"score": 3,
"max_score": 3,
"passed": true,
"unrun": [],
"audit_event_ids": ["audit-001", "audit-002"]
}
实现后的 runner 的通过条件应固定为: 所有 required assertion 为 passed: true, unrun 为空, 没有未批准的策略事件, 并且 score == max_score. 示例字段不是 YAML 或 JSON 自带执行语义; 当前仓库未实现 runner, 策略执行, 断言或结果输出, 本节只是 “最小 Harness 目录与配置契约草图”, 不能声称 harness 可运行或已经运行.
配置文件本身不是证据. Harness 只有在 agent 确实遵守写入范围, 门禁能拦住失败, eval 能复现结果时才成立.
最小搭建步骤
- 将稳定规则写入
AGENTS.md: 目录职责, 允许命令, 禁止动作, 完成时必须提交的证据; 不要记录 token, 客户数据或临时任务细节. - 用
permissions.yaml声明只读默认, 可写 glob, 禁止网络/秘密, 需要审批的删除/依赖/发布动作. 执行器必须实际解析或人工执行该策略, 否则它只是文档. - 选 3 至 5 个有黄金答案的低风险任务, 写入
eval-cases.yaml: 初始 commit, 任务, 允许文件, 断言, 禁止修改和评分规则. - 在实现 runner, 策略执行, 断言和结果输出后, 才可用干净工作区运行每个 case, 并保存 prompt/context 摘要, 工具调用, diff, 命令, 退出码和人工 review 结果.
- 只在固定任务, 模型 / 工具版本和预算不变时比较 harness 变更前后; 出现越权, 回归或证据缺失时回退规则.
配置契约片段, 需由尚未实现的 runner 执行:
# agent/permissions.yaml
default: deny
precedence: deny_overrides_allow
read_scope:
- AGENTS.md
- docs/**
- feature/search/**
write_allow:
- feature/search/**
deny:
- .github/workflows/**
- gradle/libs.versions.toml
- "**/*.keystore"
- "**/.env"
- "**/.env.*"
- "**/credentials/**"
- "**/*credential*"
- "**/*secret*"
command_allowlist:
- "./scripts/verify-change.sh"
- "./gradlew :feature:search:testDebugUnitTest"
network: deny
approval_required: [delete, dependency_change, network, release]
audit_events: [policy_evaluated, path_resolved, command_requested, approval_requested, approval_granted, assertion_completed]
策略解析顺序应为: 先把请求路径相对仓库根目录 canonicalize, 再拒绝逃出根目录, 包含符号链接跳转或命中 deny 的路径; 之后才匹配 read_scope 或 write_allow. deny_overrides_allow 在所有冲突中优先. 命令必须以结构化 argv 与 allowlist 精确匹配, 不能接受任意 shell 拼接; 网络默认拒绝, 只有批准事件才能临时开启. 每次拒绝, 路径解析, 命令请求, 审批及断言完成都写入审计事件, 且不记录秘密原文.
# agent/eval-cases.yaml
- id: search-state-restoration
task: Restore a saved query without duplicate network load
base_ref: <pinned-commit>
allowed_paths: [feature/search/**]
required_checks: [":feature:search:testDebugUnitTest"]
assertions:
- old implementation fails the new regression test
- changed_paths_subset_of_allowed_paths
- no dependency_or_manifest_change
这些断言中的 changed_paths_subset_of_allowed_paths 代表 runner/CI 需要实现的检查, 不是 YAML 自带语义.
三, Instructions 与状态连续性
Instructions 应写稳定且可执行的规则, 例如先读哪些文档, 依赖方向, 禁止引入的库, 允许的验证命令和完成前必须提交的证据. 不要把临时任务细节无限堆入全局说明.
跨会话状态只记录:
- 已确认的决策和理由.
- 当前进度, 阻塞和 owner.
- 已运行检查的命令, 环境和结果.
- 无法从仓库推导的约束.
能从代码或 Git 推导的信息应按需重新读取, 避免 “记忆” 成为过期事实源.
四, 权限, 审批与审计
默认采用最小权限:
- 先只读探索, 再开放必要写入路径.
- 网络, 秘密, 生产数据和发布凭据默认不可用.
- 删除, 发布, 权限变更, 依赖升级和安全策略修改需要人工批准.
- 工具输入要校验; 不可信仓库内容和网页可能包含 prompt injection.
- 审计记录至少包括模型 / 工具版本, 输入来源, tool calls, diff, 验证结果和批准者.
多 agent 的 Planner/Builder/Reviewer/Verifier 分工只有在上下文隔离, owner 清楚且验证独立时才有价值; 多角色共享同一错误不构成独立证据的边界, 详见 Loop Engineering 第七节.
五, 用 Eval/CI 证明方法有效
维护一组代表真实工作的固定任务, 按固定指标集对比 harness 变更前后; 指标维度与示例详见 Loop Engineering 第六节.
比较 harness 变更前后必须固定任务集, 代码基线, 模型/工具版本和最大预算. CI 应保存失败样本, 而不是只汇报平均分. 指标下降时应回滚 instructions/tool/permission 变更.
六, Android Harness 的验证阶梯
- 纯逻辑: 单元测试和边界用例.
- ViewModel/Flow:
runTest, test dispatcher, fake repository, 状态恢复. - UI: Compose/Espresso, screenshot, 字号, 深色模式, 无障碍和键盘路径.
- 性能: Macrobenchmark/Perfetto/Profiler 的前后基线.
- 权限/发布/隐私: 多版本设备矩阵, 商店政策和法务复核.
每项都应记录 “运行了什么, 在哪个环境, 结果是什么, 哪些没运行及原因”. 各层中 AI 适合做什么, 人 / 流水线必须兜底什么, 见 Vibe Coding 的质量边界.
七, 面试怎么答
面试官问「你配过什么 harness」→ 我讲实际配置过的事实: 一份 instructions 规则 (AGENTS.md/CLAUDE.md 的目录职责, 禁止事项, 完成定义), 一份权限策略 (默认只读, 可写 glob, 命令白名单, 网络默认拒绝), 和一组固定任务 eval; 没有实现 runner 的部分我会说成「配置契约草图」, 不夸大成已运行的产出. 面试官问「CLAUDE.md 怎么落地」→ 它是 Claude Code 的产品约定, 加载和作用域语义以官方文档为准, 不假设其它客户端读同名文件; 落地只写稳定规则, 不把临时任务细节堆进全局说明. 面试官问「权限与验证怎么控制」→ 高风险动作人工审批, 校验走闭环: eval 在干净工作区跑固定任务, 记录模型/工具版本与命令结果, 只在基线一致时比较配置变更前后; 无法执行的检查必须标记, 不让配置文件冒充证据.
高频面试题
Q1: Harness Engineering 和 Prompt Engineering 的区别?
Prompt 解决单次表达; harness 约束整个运行环境, 工具权限, 状态, eval, 审批和审计.
Q2: 为什么不能只看编译通过?
编译不能证明需求语义, 恢复路径, 安全边界, 性能和设备行为正确.
Q3: 怎样避免 agent 越改越多?
明确 non-goals 和 write scope, 默认只读, 小步 diff, 范围外修改自动失败, 高风险动作人工批准.
练习与预期证据
为一个真实小模块创建一份 instructions 配置, 一个权限规则和两个 eval case (修复与拒绝越权各一条).预期结果: runner 或 reviewer 能报告越权路径, 缺失验证与审批事件; 若没有执行器, 明确记录其为待接入的治理文档.
版本与参考资料
- 最后核验: 2026-08-07.
- AGENTS.md 开放格式
- Claude Code memory / CLAUDE.md
- Anthropic: Building effective agents
- 各客户端的 instructions 加载顺序, 权限和工具能力可能变化, 落地前应核对对应版本官方文档.