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 只在当前浏览器中解析,不会上传。这里验证的是实验合同,不是服务器认证或第三方背书。
概念校准与一周复习
三道题检查你是否掌握了本章边界、交付证据和恢复方法。答案只保存在当前浏览器。