Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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发布, 密钥, 权限, 依赖升级等人工门禁高风险决策无人负责
Auditprompt/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 能复现结果时才成立.

最小搭建步骤

  1. 将稳定规则写入 AGENTS.md: 目录职责, 允许命令, 禁止动作, 完成时必须提交的证据; 不要记录 token, 客户数据或临时任务细节.
  2. 用 permissions.yaml 声明只读默认, 可写 glob, 禁止网络/秘密, 需要审批的删除/依赖/发布动作. 执行器必须实际解析或人工执行该策略, 否则它只是文档.
  3. 选 3 至 5 个有黄金答案的低风险任务, 写入 eval-cases.yaml: 初始 commit, 任务, 允许文件, 断言, 禁止修改和评分规则.
  4. 在实现 runner, 策略执行, 断言和结果输出后, 才可用干净工作区运行每个 case, 并保存 prompt/context 摘要, 工具调用, diff, 命令, 退出码和人工 review 结果.
  5. 只在固定任务, 模型 / 工具版本和预算不变时比较 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 推导的信息应按需重新读取, 避免 “记忆” 成为过期事实源.

四, 权限, 审批与审计

默认采用最小权限:

  1. 先只读探索, 再开放必要写入路径.
  2. 网络, 秘密, 生产数据和发布凭据默认不可用.
  3. 删除, 发布, 权限变更, 依赖升级和安全策略修改需要人工批准.
  4. 工具输入要校验; 不可信仓库内容和网页可能包含 prompt injection.
  5. 审计记录至少包括模型 / 工具版本, 输入来源, tool calls, diff, 验证结果和批准者.

多 agent 的 Planner/Builder/Reviewer/Verifier 分工只有在上下文隔离, owner 清楚且验证独立时才有价值; 多角色共享同一错误不构成独立证据的边界, 详见 Loop Engineering 第七节.

五, 用 Eval/CI 证明方法有效

维护一组代表真实工作的固定任务, 按固定指标集对比 harness 变更前后; 指标维度与示例详见 Loop Engineering 第六节.

比较 harness 变更前后必须固定任务集, 代码基线, 模型/工具版本和最大预算. CI 应保存失败样本, 而不是只汇报平均分. 指标下降时应回滚 instructions/tool/permission 变更.

六, Android Harness 的验证阶梯

  1. 纯逻辑: 单元测试和边界用例.
  2. ViewModel/Flow: runTest, test dispatcher, fake repository, 状态恢复.
  3. UI: Compose/Espresso, screenshot, 字号, 深色模式, 无障碍和键盘路径.
  4. 性能: Macrobenchmark/Perfetto/Profiler 的前后基线.
  5. 权限/发布/隐私: 多版本设备矩阵, 商店政策和法务复核.

每项都应记录 “运行了什么, 在哪个环境, 结果是什么, 哪些没运行及原因”. 各层中 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 能报告越权路径, 缺失验证与审批事件; 若没有执行器, 明确记录其为待接入的治理文档.

版本与参考资料