Harness 与治理
用运行环境控制工具、权限、副作用、预算、人工确认、恢复和平台差异。
谁控制权限、运行环境、副作用、平台适配和恢复?概念校准
Harness
- 它是什么
- 承载模型、工具、状态、权限、日志、预算、人工确认和恢复的运行控制环境。
- 它不是什么
- 它能限制风险并保留证据,但不能直接阻止模型产生幻觉。
Skill
- 它是什么
- 包含指令、资源、适用边界、版本和兼容信息的可复用能力包。
- 它不是什么
- 不等于一段 Prompt,也不意味着其中脚本可以被无条件执行。
工具
- 它是什么
- Agent 可通过明确参数、权限和结果合同调用的受约束能力。
- 它不是什么
- 工具不等于 MCP;本地函数、命令或 HTTP 客户端也可以是工具。
多平台适配与迁移#
建立 Codex、Claude Code、WorkBuddy 的能力模型、目录和权限适配器。
Python 3.12 · pytest · Pydantic · SQLite · deterministic Mock概念校准\n\n在开始操作代码前,先把本章涉及的概念放回正确的工程边界。\n\n\n\n### Harness harness\n\n- 它是什么: 承载模型、工具、状态、权限、日志、预算、人工确认、隔离和恢复的运行控制环境。\n- 它不是什么: 它可以缩小风险、拒绝动作并保留证据,但不能保证模型输出永远正确,也不能代替领域验收。\n- 与相邻概念的关系: Agent Loop 在 Harness 中运行;Tool、Memory 和 MCP 连接受其策略约束;Observability 记录过程,Evaluation 判断行为。\n- 在 HeatStack Forge 中的位置: Forge Harness 管理工作目录、工具、确认队列、预算、Checkpoint、审计日志和回滚,把模型建议与真实副作用分开。\n- 典型误用与修正: 误用是把系统提示词当成安全边界。修正方法是把权限、路径、网络、密钥、预算和回滚落实在模型之外的控制中。\n\n### Skill skill\n\n- 它是什么: 包含指令、资源、适用边界、版本和兼容元数据的可复用能力包,可被 Agent 平台发现、安装或引用。\n- 它不是什么: 它不等于一段 Prompt,也不代表附带脚本可以自动执行;实际执行仍受平台、工具合同和权限策略约束。\n- 与相邻概念的关系: Prompt 可以是 Skill 的一部分,Tool 可以被 Skill 引用,Harness 决定安装与运行边界,适配器处理平台差异。\n- 在 HeatStack Forge 中的位置: Forge 将需求抽取能力打包为带 Manifest、测试、资源清单和风险说明的 Skill,并适配三个目标平台。\n- 典型误用与修正: 误用是复制一个 Prompt 文件就宣称跨平台兼容。修正方法是声明依赖、入口、资源、版本、权限与验收测试。\n\n### 工具 tool\n\n- 它是什么: Agent 可通过明确参数、权限、执行边界和结果合同调用的受约束能力,例如函数、命令、数据库查询或 HTTP 客户端。\n- 它不是什么: 工具不等于 MCP,也不等于任意脚本。协议可以暴露能力,但 Harness 仍决定是否允许调用及如何处理副作用。\n- 与相邻概念的关系: Agent Loop 选择工具,Schema 约束参数,Harness 执行权限检查和隔离,Verification 检查结果是否推进目标。\n- 在 HeatStack Forge 中的位置: Forge 的 tool registry 保存合同与风险等级;执行前生成变更计划,高风险写操作等待确认,执行后记录结果与恢复信息。\n- 典型误用与修正: 误用是让模型根据工具名称自由拼参数。修正方法是严格 Schema、确定性校验、超时、幂等键和拒绝分支测试。\n\n#
用平台无关合同描述 Skill 需要的工具、资源和确认能力
先看一个具体问题#
如何声明 Skill 需要哪些能力,并区分必需与可选?#
你正在为 Forge 平台构建跨平台适配层,需要让同一个 Skill 在不同平台上安装时能够判断该平台是否满足其能力需求。
当前代码库中还没有 forge/adapters 包,也没有任何能力模型,因此测试 test_required_capabilities_are_reported 和 test_optional_capability_is_not_a_blocker 在导入阶段就失败了。
你需要创建一个平台无关的合同模型,用 CapabilityRequirement 表示单个能力需求(包含名称和是否必需),用 SkillContract 表示一个 Skill 的全部能力需求,并实现 missing_capabilities 函数来计算缺失的必需能力。
关键决策是:当某个能力被标记为可选时,即使平台没有提供该能力,也不应该阻止安装。
先做判断#
在实现 required_names 方法时,以下哪种做法能正确区分必需与可选能力?
- 返回所有能力名称,不管
required标志。 - 只返回
required=True的能力名称。 - 只返回
required=False的能力名称。 - 返回能力名称和
required标志的元组。
判断依据: 正确做法是只返回 required=True 的能力名称,因为 missing_capabilities 只应报告缺失的必需能力,可选能力缺失不应成为安装障碍。
它是什么#
平台无关能力合同是什么#
平台无关能力合同是一组数据结构,用于声明一个 Skill 在运行时需要哪些外部能力,而不关心这些能力在具体平台上如何实现。
CapabilityRequirement 封装了能力名称和一个布尔标志 required,默认值为 True,表示该能力是必需的。
SkillContract 聚合了一个 Skill 的所有能力需求,并提供 required_names 方法来提取所有必需能力的名称集合。
missing_capabilities 函数接收一个合同和一个可用能力集合,返回所有缺失的必需能力。
它不是什么#
平台无关能力合同不是什么#
它不是平台映射表,不负责将能力名称转换为具体平台的 API 或配置。
它也不是安装器,不会执行任何安装逻辑或检查平台是否真的具备这些能力。
它不包含任何平台特定的实现细节,只描述需求本身。
它与相邻概念的关系#
合同内部各组件的关系#
SkillContract 包含多个 CapabilityRequirement,每个需求都有名称和必需标志。
required_names 方法遍历所有需求,过滤出 required=True 的项,并返回它们的名称集合。
missing_capabilities 调用 required_names 得到必需能力集合,然后与可用能力集合做差集,得到缺失的必需能力。
本阶段的边界决定: 合同模型只负责描述需求,不负责平台映射或安装;判断平台是否满足需求由 missing_capabilities 完成,但实际安装决策由上层 Harness 根据缺失能力集合做出。
把规范 Skill 合同映射为 Codex 目录、工具声明和确认策略
先看一个具体问题#
从规范合同到 Codex 安装计划#
你需要在 forge/adapters/codex.py 中实现 plan_codex 函数,把 SkillContract 转换为 CodexInstallPlan。
当前测试 test_codex_path_and_tools_are_mapped 和 test_codex_write_approval_is_preserved 都失败,因为模块尚未创建。
关键决策是:目标路径必须符合 Codex 的 skills/<skill_id>/SKILL.md 结构,工具列表必须来自合同要求,审批模式必须固定为 ask-before-writes。
如果审批模式被错误地设为 full-access,就会绕过写操作前的确认,导致权限提升。
先做判断#
在实现 plan_codex 时,审批模式应该如何处理?
- 根据可用能力动态决定
- 始终设为
ask-before-writes - 设为
full-access以简化安装 - 由调用者传入参数
判断依据: 正确选项是“始终设为 ask-before-writes”。适配器必须保留安全语义,不能因为平台差异而提升权限。
它是什么#
适配器是安全语义的翻译器#
适配器把规范合同中的能力要求映射为平台特定的安装计划,同时保持权限边界不变。
对于 Codex,这意味着生成正确的目录路径、工具声明和审批模式。
审批模式 ask-before-writes 确保任何写操作前都需要用户确认,这是合同隐含的安全要求。
它不是什么#
适配器不是权限决策者#
适配器不能随意更改权限模式,即使安装成功也不能提升权限。
它不负责执行安装,也不验证平台是否真正遵守审批模式。
适配器只是生成计划,实际执行由 Harness 控制。
它与相邻概念的关系#
合同、适配器与 Harness 的关系#
SkillContract 定义能力要求,适配器将其转换为平台计划,Harness 负责执行并强制权限。
missing_capabilities 函数用于检查可用能力是否满足合同,不满足时应抛出异常。
目标路径由 home 和 skill_id 拼接,工具列表来自合同的 required_names()。
本阶段的边界决定: 适配器必须保持安全语义,不能提升权限;审批模式固定为 ask-before-writes,即使平台支持更宽松的模式。
生成 Claude Code 命令白名单,并拒绝无法表达的必需能力
先看一个具体问题#
为 Claude Code 平台生成命令白名单,并拒绝无法表达的必需能力#
当前仓库中只有 Codex 适配器,还没有 Claude 适配器,因此测试在导入 forge.adapters.claude 时直接失败。
你需要新建 forge/adapters/claude.py,实现 plan_claude 函数,将技能契约中的能力需求映射为 Claude Code 允许的工具列表。
如果某个必需能力无法在 Claude Code 中表达,适配器必须抛出 ValueError,而不是默认映射到通用 shell 访问。
本阶段的目标是让两个测试通过:test_known_capabilities_map_to_tools 和 test_unknown_required_capability_is_rejected。
先做判断#
当适配器遇到一个无法映射到 Claude Code 工具的必需能力时,最安全的做法是什么?
- 默认映射到 Bash,因为 Bash 可以执行任何命令
- 抛出异常,拒绝生成计划
- 忽略该能力,继续生成计划
- 记录警告并映射到只读工具
判断依据: 正确选项是“抛出异常,拒绝生成计划”。因为默认映射到 Bash 会扩大权限,可能让模型执行未预期的操作;忽略能力会导致技能无法正常工作;映射到只读工具同样不准确。适配器必须明确拒绝无法表达的必需能力,以保证安全性和语义正确性。
它是什么#
显式能力映射表#
显式能力映射表是一个从能力名称到平台工具名称的字典,例如 {"read_files": "Read", "write_files": "Edit", "shell": "Bash"}。
适配器只允许映射表中列出的能力,任何不在表中的能力都被视为无法表达,必须拒绝。
这种设计让能力边界清晰可见,避免意外扩大权限,也便于审计和测试。
它不是什么#
不是通用 shell 访问的别名#
显式映射表不是把未知能力默认映射到 Bash 的机制,那样会失去能力约束。
它也不是一个可以随意扩展的开放集合;添加新映射需要同时考虑平台是否真正支持该能力。
映射表只负责名称转换,不负责权限检查或执行隔离,这些由 Harness 层处理。
它与相邻概念的关系#
与技能契约和平台计划的关系#
技能契约(SkillContract)定义了技能需要哪些能力,适配器读取这些能力并转换为平台允许的工具列表。
plan_claude 返回的 ClaudePlan 包含技能文件路径和允许的工具元组,供后续部署使用。
如果契约中的能力不在映射表中,适配器通过 missing_capabilities 函数检测缺失并抛出 ValueError。
本阶段的边界决定: 适配器必须拒绝无法表达的必需能力,而不是扩大权限;这保证了平台计划的安全性和语义正确性。
将能力映射到 WorkBuddy 工作区权限,并保留只读与写入的差异
先看一个具体问题#
从能力需求推导 WorkBuddy 工作区访问级别#
当前仓库中还没有 forge/adapters/workbuddy.py,因此测试在导入 plan_workbuddy 时直接失败,错误为 ModuleNotFoundError: No module named 'forge.adapters.workbuddy'。
本阶段需要创建该文件,实现 plan_workbuddy 函数,根据 Skill 的能力需求决定 WorkBuddy 工作区访问级别。
如果 Skill 需要 write_files 能力,则工作区访问级别应为 write;否则应保持 read,不能默认提升权限。
测试 test_write_skill_receives_write_access 和 test_read_only_skill_stays_read_only 分别验证写入和只读两种场景。
先做判断#
在实现 plan_workbuddy 之前,请预测:如果适配器总是请求写入权限,会发生什么?
- 所有 Skill 都能正常工作,因为写入权限更强大。
- 只读 Skill 会意外获得写入权限,违反最小权限原则。
- 测试会全部通过,因为测试不检查权限级别。
- 适配器会抛出异常,因为无法处理只读 Skill。
判断依据: 正确答案是第二个选项。如果适配器总是请求写入权限,只读 Skill 会意外获得写入权限,这违反了最小权限原则,并且测试 test_read_only_skill_stays_read_only 会失败。
它是什么#
WorkBuddy 适配器是什么#
WorkBuddy 适配器是一个将 Skill 的能力需求映射到 WorkBuddy 工作区访问级别的组件。
它接收 SkillContract 对象,检查其中是否包含 write_files 能力,然后生成一个 WorkBuddyPlan,其中包含配置路径和访问级别。
适配器必须根据能力需求推导访问级别,而不是硬编码为 write 或 read。
它不是什么#
WorkBuddy 适配器不是什么#
它不是一个通用的权限管理系统,也不负责执行实际的写入操作。
它不会自动提升权限,也不会忽略 Skill 的能力需求。
它不处理 shell 能力,如果检测到 shell 能力,会抛出 ValueError,因为 WorkBuddy 配置文件不暴露 shell。
它与相邻概念的关系#
与其他组件的关系#
plan_workbuddy 依赖于 SkillContract 的 required_names() 方法来获取能力名称集合。
WorkBuddyPlan 是一个冻结的数据类,包含 config_path 和 workspace_access 两个字段。
测试文件 tests/test_stage.py 导入 plan_workbuddy 并验证其行为,因此适配器必须与测试期望一致。
本阶段的边界决定: 适配器必须根据 write_files 能力是否存在来决定访问级别,不能默认提升权限;同时要拒绝 shell 能力,因为 WorkBuddy 不支持 shell。
把三个平台的安装计划规范化为可比较、可审计的配置记录
先看一个具体问题#
如何将不同平台的安装计划统一为可比较的配置记录?#
当前 forge/adapters/config.py 文件尚未创建,因此 normalize_plan 函数不存在,导致测试在导入阶段就失败。
我们需要实现一个规范化函数,它接收平台标识符和安装计划数据类,返回包含 schemaVersion 和 configuration 的字典。
关键决策在于:规范化过程中必须验证平台标识符是否受支持,否则未知平台会混入发布配置,造成后续审计和部署风险。
先做判断#
在实现 normalize_plan 时,如果传入一个未知平台标识符(如 "mystery-agent"),函数应该如何处理?
- 直接序列化并返回配置记录,因为规范化只负责格式转换
- 抛出
ValueError,因为规范化必须验证平台标识符 - 返回一个带有默认平台字段的配置记录
- 忽略平台字段,只序列化计划数据类
判断依据: 正确选项是抛出 ValueError。规范化不是简单的格式转换,它必须确保平台标识符属于受支持的适配器注册表,否则未知平台会进入发布配置,破坏后续的可审计性和部署安全性。
它是什么#
规范化平台配置是什么#
规范化平台配置是将不同平台的安装计划转换为统一结构的过程,该结构包含平台标识符、模式版本和配置数据。
它必须验证平台标识符是否在受支持的适配器注册表中,并检查计划是否为数据类且包含安装路径。
规范化后的配置记录是可比较、可审计的,能够被下游部署流程安全地消费。
它不是什么#
规范化平台配置不是什么#
规范化不等于简单的序列化,它不能接受任意平台名称而不加验证。
规范化不负责执行安装或修改计划内容,它只负责将计划转换为标准格式并确保其符合基本约束。
规范化不是平台适配器本身,它不处理平台特定的通信协议或认证逻辑。
它与相邻概念的关系#
与其他组件的关系#
规范化函数 normalize_plan 被测试文件 tests/test_stage.py 直接调用,测试期望它验证平台并返回正确的 schemaVersion。
它依赖于 Python 的 dataclasses 模块来序列化计划数据类,并检查计划是否包含安装路径字段。
规范化后的配置记录将被后续的部署流程使用,因此必须保证平台标识符的有效性。
本阶段的边界决定: 规范化必须验证平台标识符,拒绝未知平台;同时检查计划是否为数据类且包含安装路径,否则抛出相应异常。
区分阻塞缺失和可接受降级,并生成可解释的平台兼容报告
先看一个具体问题#
平台缺少可选能力时被错误判为不兼容#
在上一阶段,你已经把不同平台的原始配置规范化为统一的 SkillContract,其中每个能力都带有 required 标志。
现在需要实现 evaluate 函数,根据平台实际可用的能力集合生成 CompatibilityReport,报告必须区分阻塞缺失和可接受降级。
当前 forge/adapters/compatibility.py 文件尚不存在,运行测试会直接报 ModuleNotFoundError,因为测试文件 tests/test_stage.py 试图导入 evaluate。
如果简单地把所有缺失能力都放进 blockers,那么缺少可选能力 git 的平台也会被标记为不兼容,这违背了合同语义。
先做判断#
在实现 evaluate 之前,请预测:如果平台缺少一个标记为可选的能力(例如 git),CompatibilityReport.compatible 应该是什么?
False,因为任何缺失都意味着平台不完整True,因为可选能力缺失不应阻塞兼容性False,但把缺失项放入degradationsTrue,但把缺失项放入blockers
判断依据: 正确答案是 True,可选能力缺失只应记录为降级,不影响整体兼容性。只有必需能力缺失才会导致 compatible=False。
它是什么#
兼容性报告是合同与平台能力的差异分析#
CompatibilityReport 是一个不可变数据类,包含 compatible 布尔值、blockers 元组和 degradations 元组。
evaluate 函数遍历 SkillContract 中的每个 CapabilityRequirement,检查其名称是否在 available 集合中。
对于缺失的能力,根据其 required 标志决定放入 blockers 还是 degradations。
最终 compatible 仅由 blockers 是否为空决定,与 degradations 无关。
它不是什么#
兼容性报告不是简单的缺失列表#
它不把所有缺失能力混为一谈,而是保留每个能力的必需/可选语义。
它不负责修改平台配置或自动安装缺失能力,只做静态评估。
它不保证平台在运行时一定成功,只反映合同层面的静态兼容性。
它与相邻概念的关系#
与已有组件的关系#
SkillContract 和 CapabilityRequirement 来自 forge/adapters/capabilities.py,本阶段不修改它们。
evaluate 函数被测试文件 tests/test_stage.py 直接调用,测试期望返回的 CompatibilityReport 字段精确匹配。
后续阶段可能会使用该报告来决定是否允许部署或触发迁移流程。
本阶段的边界决定: 只有当缺失的能力的 required 属性为 True 时,才将其加入 blockers;否则加入 degradations,并且 compatible 只取决于 blockers 是否为空。
用统一合同检查安装路径、权限不升级和能力覆盖,阻止不安全适配器发布
先看一个具体问题#
如何自动检查适配器计划是否符合安全与能力要求?#
在上一阶段,我们生成了兼容性报告,但报告只描述差异,并不会阻止不安全的适配器发布。
现在需要实现一个一致性检查函数 check_plan,它接收平台名、配置字典和所需能力集合,返回 ConformanceResult。
如果配置缺少安装路径、未覆盖所需能力,或者审批模式为 full-access 但所需能力中不包含 unattended_write,则必须判定为不通过并给出具体错误。
当前代码库中还没有 forge/adapters/conformance.py 文件,因此测试在导入阶段就会失败。
先做判断#
在实现 check_plan 之前,请预测:如果只检查配置是否非空,能否通过 test_permission_escalation_fails_conformance 测试?
- 能通过,因为配置非空且包含 capabilities。
- 不能通过,因为 full-access 审批模式会授予超出所需能力的权限。
- 能通过,因为测试只检查 passed 是否为 False。
- 不能通过,因为配置缺少 target_path。
判断依据: 正确答案是“不能通过,因为 full-access 审批模式会授予超出所需能力的权限”。该测试传入 approval_mode 为 full-access,但所需能力只有 read_files,这属于权限升级,必须被拒绝。仅检查非空无法发现这种语义违规。
它是什么#
一致性检查是什么#
一致性检查是对适配器生成的配置计划进行语义验证,确保其满足安全与能力合同。
它检查三个关键维度:安装路径是否存在且非空、所需能力是否被配置声明、审批模式是否导致权限升级。
检查结果通过 ConformanceResult 数据类返回,包含平台名、是否通过以及错误元组。
它不是什么#
一致性检查不是什么#
它不只是检查配置字典是否非空或字段是否存在,因为形状正确不代表语义安全。
它不负责实际执行安装或修改系统状态,只对计划进行静态分析。
它不能替代领域验收,也不能保证模型输出永远正确,只验证适配器计划是否符合预定义规则。
它与相邻概念的关系#
与其他组件的关系#
check_plan 使用配置字典和所需能力集合作为输入,这些数据来自 Skill 的元数据和适配器生成的计划。
ConformanceResult 的 errors 元组会被测试断言,例如 test_permission_escalation_fails_conformance 检查 errors 中包含 “permission escalation”。
该检查位于 forge/adapters/conformance.py,与 capabilities.py、compatibility.py 等模块共同构成适配器层。
本阶段的边界决定: 一致性检查必须验证语义,包括权限不升级和能力覆盖,而不仅仅是配置形状。如果只检查非空,就会批准授予未声明 full-access 的适配器,导致安全风险。
完成本章#
让同一个 Forge Skill 通过适配器部署到三个平台。
本地实验自检
- 未开始
- 2阅读中
- 3实验已下载
- 4测试结果已读取
- 5本地自检通过
verification.json 只在当前浏览器中解析,不会上传。这里验证的是实验合同,不是服务器认证或第三方背书。
概念校准与一周复习
三道题检查你是否掌握了本章边界、交付证据和恢复方法。答案只保存在当前浏览器。