← 工程跃迁 / 第 06 章
架构定位

能力扩展

通过能力包、外部证据、持久状态和标准协议扩展 Agent 能做的事情。

如何为 Agent 增加 Skill、检索、记忆和外部协议能力?

概念校准

Skill

它是什么
包含指令、资源、适用边界、版本和兼容信息的可复用能力包。
它不是什么
不等于一段 Prompt,也不意味着其中脚本可以被无条件执行。

工具

它是什么
Agent 可通过明确参数、权限和结果合同调用的受约束能力。
它不是什么
工具不等于 MCP;本地函数、命令或 HTTP 客户端也可以是工具。

Skill、Prompt 与 Agent 能力包#

把需求抽取器封装成有 Manifest、资源边界和兼容元数据的 Skill。

本章只做一件事交付可安装、可审查、覆盖三个场景的 Forge Skill。
开始前,Forge 已经具备已完成 Module 05 的“Prompt、上下文工程与结构化输出”,其通过验收的 solution 是本章起点。
完成后,Forge 将能够把 Forge 的需求抽取能力封装为可安装 Skill。
卡住时的最小恢复点只打包 Markdown、Schema 和测试夹具,不附带自动执行脚本。
下载本章实验仓库Python 3.12 · pytest · Pydantic · SQLite · deterministic Mock
阶段能力链
01验证能力包必须包含入口说明、资源目录和可审查的相对路径02区分可读取资源与可执行脚本,并默认禁止安装或运行脚本03用声明变量渲染 Prompt,并拒绝缺失或未声明变量以避免平台隐式依赖04解析包含 ID、版本、入口、文件哈希和权限声明的严格 Manifest05解析 SemVer 并根据主版本判断升级是否需要迁移06根据平台能力、所需功能和最低客户端版本给出兼容或缺口报告07为三场景生成排序稳定、哈希可复核且不自动执行脚本的发布清单
每一步只增加一种可验证能力;后一步建立在前一步已经通过的代码和测试上。

概念校准\n\n在开始操作代码前,先把本章涉及的概念放回正确的工程边界。\n\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
#

STAGE 01

验证能力包必须包含入口说明、资源目录和可审查的相对路径

先看一个具体问题#

创建 forge/skill_layout.py 并让结构验证测试通过#

当前 forge/skill_layout.py 不存在,tests/test_stage.py 在导入 validate_layout 时直接触发 ModuleNotFoundError,因此学习者需要从空文件开始创建该模块。

本阶段要实现的 validate_layout 接收路径字符串列表,返回错误字符串列表;它必须拒绝缺少 SKILL.md 的目录,并拒绝绝对路径或包含 .. 的父目录遍历路径。

测试 test_skill_requires_entry_instructions 和 test_parent_traversal_is_rejected 分别锁定这两个结构约束,学习者需要让这两个测试从失败变为通过。

先做判断#

如果 validate_layout([“references/guide.md”]) 的实现只是 return [],运行 test_skill_requires_entry_instructions 会发生什么?

  • 测试通过,因为 references/guide.md 是合法路径
  • 测试失败,因为断言期望错误列表包含 missing SKILL.md
  • 测试失败,因为路径包含父目录遍历
  • 测试通过,因为 references 是允许的根目录

判断依据: 正确选项是第二个:测试断言 “missing SKILL.md” in validate_layout(…),空列表不包含该错误,因此失败。这暴露了入口文件合同没有被执行。

它是什么#

Skill 结构验证器是一个纯函数合同检查器#

validate_layout 接收路径字符串列表,返回错误字符串列表;它不读取文件内容,只根据路径集合判断是否满足 SKILL.md 入口、允许根目录和相对路径安全。

该函数使用 PurePosixPath 把每个路径字符串规范化为可检查的路径对象,然后通过集合成员关系和 parts 属性执行静态判断。

它不是什么#

它不是文件系统扫描器或安装器#

validate_layout 不会检查文件是否真实存在,也不会创建目录、复制资源或执行脚本;它只对传入的路径字符串做结构合同验证。

它也不负责平台权限策略或工具调用隔离,这些边界由后续的 Harness 和适配器处理,本阶段只验证 Skill 包的结构元素。

它与相邻概念的关系#

入口集合、根目录集合与路径对象之间的关系#

REQUIRED 集合定义必须存在的入口文件名 SKILL.md,ALLOWED_ROOTS 集合定义允许的第一级目录,如 references、templates、scripts、tests。

PurePosixPath 的 parts 属性把路径拆成组件,使代码能检查第一级根目录和 .. 组件;先检查入口缺失,再检查路径安全,最后检查根目录,形成分层验证顺序。

本阶段的边界决定: 当路径列表缺少 SKILL.md 时返回 missing SKILL.md;当路径为绝对路径或包含 .. 时返回 unsafe path;当第一级目录不在 ALLOWED_ROOTS 时返回 unknown root。

STAGE 02

区分可读取资源与可执行脚本,并默认禁止安装或运行脚本

先看一个具体问题#

Skill 包中的脚本为什么不能像资源一样被自动安装#

在 Skill 包的安装过程中,学习者面对的核心决策是区分可读取的静态资源与可能产生副作用的可执行脚本。当前的 forge/package_policy.py 文件尚未创建,测试在收集阶段就会因为 ModuleNotFoundError 而中断。

故障夹具中的 decide_file 函数将所有路径一视同仁地标记为可安装,并且将 scripts/ 目录下的文件标记为可执行。这导致 scripts/setup.ps1 在未经用户显式批准的情况下被直接接受并赋予执行权限,违反了安全策略。

学习者需要创建 forge/package_policy.py,实现 decide_file 函数,使得脚本在默认情况下被拒绝安装,且即使经过批准也必须以不可执行的状态存储,而普通资源文件则可以安全安装。

先做判断#

当 decide_file("scripts/setup.ps1") 被调用且未传入 allow_scripts=True 时,函数应该返回什么样的决策结果?

  • install=True, executable=True,因为脚本也是包的一部分
  • install=False, executable=False,因为脚本需要显式批准
  • install=True, executable=False,因为脚本可以安装但不能执行
  • install=False, executable=True,因为脚本需要批准但可以执行

判断依据: 正确答案是 install=False 且 executable=False。脚本具有执行副作用的风险,默认策略必须拒绝其安装,并在 reason 中说明需要显式批准。即使后续获得批准,脚本也必须以不可执行的状态存储,执行权限由 Harness 和权限策略另行控制。

它是什么#

资源与脚本的本质区别#

资源是可读取的静态内容,例如 Markdown 文档、JSON 配置或图片,它们在被安装和读取时不会产生系统副作用。脚本则是包含可执行代码的文件,例如 .ps1 或 .py,一旦被执行就可能修改文件系统、发起网络请求或访问敏感数据。

decide_file 函数的职责是根据路径前缀和调用参数,为每个文件输出一个包含 install、executable 和 reason 的 FileDecision。这个决策模型将安全策略从运行时提前到安装时,确保平台不会无意中引入可执行副作用。

它不是什么#

策略边界的常见误解#

策略不是仅仅检查文件扩展名,因为 .py 文件既可能是可执行脚本也可能是只读文档片段,因此本阶段使用目录前缀 scripts/ 作为脚本标识。

批准脚本安装并不意味着赋予执行权限,allow_scripts=True 只允许脚本文件被复制到安装目录,但 executable 字段必须始终为 False,实际执行由 Harness 在独立的权限检查中决定。

它与相邻概念的关系#

决策与平台执行的依赖关系#

decide_file 返回的 FileDecision 是安装器决定是否将文件写入磁盘的依据,而 executable 字段则指导安装器在写入时剥离或保留文件的可执行位。

Harness 负责在安装完成后根据平台策略和用户权限决定是否真正运行某个脚本,因此 package_policy 模块只需关注安装边界的静态属性,不需要也不应该尝试预测运行时上下文。

本阶段的边界决定: 当路径以 scripts/ 开头且 allow_scripts 为 False 时,必须返回 install=False;当路径包含绝对路径或 .. 时,必须因不安全而拒绝;所有通过检查的文件,其 executable 字段必须为 False。

STAGE 03

用声明变量渲染 Prompt,并拒绝缺失或未声明变量以避免平台隐式依赖

先看一个具体问题#

缺失变量被静默替换为空字符串#

在当前起点状态下,forge/prompt_template.py 文件尚不存在,测试文件 tests/test_stage.py 在导入 from forge.prompt_template import render 时会直接抛出 ModuleNotFoundError,导致收集阶段就中断。

本阶段需要新建 forge/prompt_template.py 并实现 render 函数,使其能够从模板中提取变量、校验声明集合与提供的值字典,最终安全地完成字符串替换。

故障树中的 faultySource 展示了一个存在缺陷的初始实现:它在替换时使用 values.get(match.group(1), ""),当模板声明了 goal 变量但值字典中没有提供对应键时,goal 会被静默替换为空字符串,导致渲染后的指令丢失关键信息。

test_missing_declared_value_is_not_silently_blank 测试用例期望在这种情况下抛出包含 missing variables 的 ValueError,而不是静默返回残缺的字符串。

先做判断#

当模板为 Review {{workspace}} for {{goal}},声明集合为 {workspace, goal},但值字典只提供 {workspace: 'repo'} 时,使用 values.get(name, "") 的实现会产生什么结果?

  • 抛出 ValueError,因为 goal 缺失
  • 返回 ‘Review repo for ‘,goal 变成空字符串
  • 返回 ‘Review repo for {{goal}}‘,原样保留占位符
  • 抛出 KeyError,因为字典中没有 goal 键

判断依据: values.get(name, "") 会在键不存在时返回默认值空字符串,因此不会抛出异常,而是把 goal 替换为空,返回 ‘Review repo for ‘。这正是需要避免的静默丢失行为。

它是什么#

声明式模板渲染的三集合校验模型#

render 函数围绕三个集合运作:模板中实际使用的变量集合 used、外部声明的合法变量集合 declared、以及调用方提供的值字典键集合。

渲染前必须依次检查:used 是否为 declared 的子集以拒绝未声明变量,used 是否被 values 完全覆盖以拒绝缺失值,以及 values 是否包含 declared 之外的额外键以拒绝未知值。

只有这三层校验全部通过后,才执行 VARIABLE.sub 替换,且替换时使用 values[match.group(1)] 直接索引而非 .get,确保不会回退到默认值。

它不是什么#

模板渲染不是简单的字符串替换#

模板渲染不等同于无条件的 str.replace 或带默认值的 dict.get,因为这两种方式都会在变量缺失时静默产生不完整结果而非报错。

声明集合不是可选的装饰性参数,它是平台间可移植性的合同:如果模板使用了未在 declared 中声明的变量,说明该模板依赖了目标平台可能不支持的隐式上下文。

extra 检查不是多余的严格性,它防止调用方误以为某些值会被模板使用但实际上从未出现在模板中,这种不匹配往往意味着调用方与模板版本不一致。

它与相邻概念的关系#

render 与 Skill 可移植性的关系#

render 函数是 Skill 包中 Prompt 模板跨平台渲染的基础设施:不同平台加载同一个 Skill 时,只要值字典满足声明合同,渲染结果就一致。

forge/package_policy.py 中的 decide_file 决定了文件层面的策略,而 render 在内容层面保证了 Prompt 变量的完整性,两者共同构成 Skill 包的可靠性边界。

VARIABLE 正则表达式 r"{{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}" 定义了模板语法,它允许变量名两侧有空白字符如 {{ workspace }},这与 test_declared_template_renders_portably 中测试的带空格模板一致。

本阶段的边界决定: 当 used - declared 非空时必须抛出 ValueError 而非忽略未声明变量,因为未声明变量意味着模板依赖了平台合同之外的隐式上下文,静默接受会破坏跨平台可移植性。

STAGE 04

解析包含 ID、版本、入口、文件哈希和权限声明的严格 Manifest

先看一个具体问题#

Manifest 不是字典,而是信任边界#

在 Stage 04 中,forge/manifest.py 尚不存在,测试文件 tests/test_stage.py 在导入时直接抛出 ModuleNotFoundError: No module named 'forge.manifest',导致收集阶段就失败。

本阶段的起点代码为空,学习者需要从零创建 forge/manifest.py,实现 Manifest.parse 方法,使其在入口文件不在 files 哈希映射中时抛出包含 entrypoint 的 ValueError。

核心决策在于:Manifest 绝不能被当作宽松的元数据字典来解析,它必须作为 Skill 包的信任边界,拒绝任何未经验证的入口或文件哈希。

先做判断#

当 Manifest.parse 收到一个 entrypoint 为 missing.md 但 files 中只有 SKILL.md 的字典时,正确的严格解析器应该做什么?

  • 直接接受并返回 Manifest 对象,因为字段都存在
  • 抛出 ValueError 且消息中包含 entrypoint
  • 自动将 entrypoint 改为 files 中的第一个文件
  • 忽略 entrypoint 字段,使用默认值 SKILL.md

判断依据: 正确答案是抛出 ValueError 且消息中包含 entrypoint。信任边界要求入口必须存在于文件哈希映射中,否则该入口未被内容寻址验证,不能被信任。

它是什么#

Manifest 是内容寻址的信任契约#

Manifest 是一个不可变数据契约,通过 Manifest.parse 类方法将外部字典转化为受信任的 Manifest 对象,其核心在于解析过程本身就是一次完整的验证。

它要求字段集合精确匹配 id、version、entrypoint、files、permissions 五个键,不多不少,ID 必须匹配小写连字符模式,文件哈希必须是 64 位十六进制字符串。

入口文件必须存在于 files 映射中,这意味着入口本身也是内容寻址的,攻击者无法注入一个未被哈希验证的入口文件。

它不是什么#

Manifest 不是宽松的元数据容器#

Manifest 不是简单的字典包装器,不能使用 payload.get 提供默认值来跳过缺失字段,否则攻击者可以省略关键字段并让解析器静默填充。

它也不是一个通用的配置解析器,不会容忍多余字段或格式不规范的哈希值,任何偏差都应导致 ValueError 而非静默修正。

Manifest 的验证不依赖于外部工具链的后续步骤,解析本身必须是原子性的信任决策点。

它与相邻概念的关系#

字段验证与入口存在性检查的因果关系#

字段集合检查是第一道防线,确保没有多余或缺失的键,这防止了攻击者通过注入未知字段来绕过后续验证逻辑。

哈希格式验证与入口存在性检查形成因果链:只有当 files 中的每个值都是合法的 64 位十六进制哈希时,入口存在性检查才有意义,因为内容寻址依赖于哈希的完整性。

entrypoint 必须是 files 映射的键,这建立了入口文件与其内容哈希之间的强绑定,使得 Harness 在安装时可以验证入口文件内容是否与声明的哈希匹配。

本阶段的边界决定: 当 payload 的键集合不等于 {'id','version','entrypoint','files','permissions'} 时,必须立即抛出 ValueError,绝不能使用默认值填充缺失字段或忽略多余字段。

STAGE 05

解析 SemVer 并根据主版本判断升级是否需要迁移

先看一个具体问题#

Skill 升级时主版本变化未被识别为需要迁移#

在当前的 forge/versioning.py 故障实现中,upgrade_kind 函数无条件返回 compatible-upgrade,导致从 1.4.2 升级到 2.0.0 时未触发迁移要求。

测试用例 test_major_upgrade_requires_migration 期望主版本号变化时返回 migration-required,但实际返回了 compatible-upgrade,断言失败。

根本原因在于版本元数据虽然被解析并存储在 Version 数据类中,但并未与兼容性语义建立因果联系,主版本号的变化没有驱动分类逻辑。

先做判断#

当 upgrade_kind("1.4.2", "2.0.0") 被调用时,故障代码返回 compatible-upgrade。你认为要修复此问题,核心逻辑应基于哪个版本号分量的比较?

  • 仅比较补丁号是否变化
  • 比较主版本号是否不同
  • 比较次版本号是否递增
  • 比较完整版本字符串的字典序

判断依据: 正确思路是比较主版本号。根据 SemVer 规范,主版本号变化代表不兼容的 API 变更,必须返回 migration-required,而次版本号和补丁号变化属于兼容升级。

它是什么#

语义版本号(SemVer)的兼容性判定模型#

语义版本号由主版本、次版本和补丁三个数字分量组成,主版本号变化表示不兼容的 API 修改,次版本号变化表示向后兼容的新功能,补丁号变化表示向后兼容的缺陷修复。

upgrade_kind 函数通过 parse_version 将版本字符串解析为 Version 数据类对象,然后比较左右两端的主版本号来判定升级类型,主版本号不同则返回 migration-required,否则返回 compatible-upgrade。

它不是什么#

SemVer 判定模型的边界#

版本兼容性判定不是简单的字符串比较或字典序比较,因为 2.0.0 在字典序上大于 1.9.9 但这并不直接表达 API 兼容性语义。

upgrade_kind 不是对任意版本格式生效的通用解析器,它严格依赖正则表达式 PATTERN 匹配标准 SemVer 格式,无法处理非数字前缀或缺失分量的字符串。

它与相邻概念的关系#

版本解析与升级分类的因果链#

parse_version 函数使用正则表达式 PATTERN 提取数字分量并构造 Version 对象,Version 使用 @dataclass(frozen=True, order=True) 装饰器从而支持基于元组的自然大小比较。

upgrade_kind 依赖 parse_version 的输出,先通过 right <= left 拦截非递增目标版本,再通过 right.major != left.major 将主版本差异映射为 migration-required,形成从字符串到分类结果的完整因果链。

本阶段的边界决定: 当目标版本的主版本号与当前版本不同时,无论次版本或补丁号如何变化,都必须判定为需要迁移,因为主版本号变化在 SemVer 中是破坏性 API 变更的唯一标志。

STAGE 06

根据平台能力、所需功能和最低客户端版本给出兼容或缺口报告

先看一个具体问题#

Skill 在目标平台上总是被误判为兼容#

在当前代码变更之前,项目中不存在 forge/compatibility.py 文件,因此测试在收集阶段就会因为 ModuleNotFoundError: No module named 'forge.compatibility' 而中断。

为了通过测试,我们需要创建该文件并实现 compatibility 函数。然而,如果仅仅让该函数无条件返回 True, [],就会产生一个更隐蔽的工程缺陷:平台即使缺少所需功能,也会被错误地标记为兼容。

test_missing_platform_feature_is_reported 测试用例明确要求,当平台 Platform("codex", "1.2.0", frozenset({"prompts"})) 缺少 resources 功能时,函数必须返回 ok=False 且 gaps 包含 "missing feature: resources"。

本阶段的核心任务是建立一种机制,通过比较平台实际拥有的功能集与所需功能集,以及比较客户端版本与最低版本要求,来产生精确的缺口报告。

先做判断#

当 compatibility 函数接收到一个客户端版本满足要求但功能集不完整的平台时,如果函数内部完全没有执行任何集合比较逻辑,测试断言 ok is False and gaps == ["missing feature: resources"] 会发生什么?

  • 断言成功,因为版本满足要求就默认功能兼容
  • 断言失败,因为 ok 的实际值是 True 而不是 False
  • 抛出 KeyError,因为 resources 不在平台功能集中
  • 抛出 TypeError,因为无法对 frozenset 和 set 进行减法运算

判断依据: 正确答案是断言失败。如果函数无条件返回 True, [],那么 ok 的值就是 True,而断言期望它是 False,因此 assert (True is False) 会触发 AssertionError。这说明兼容性不能仅凭存在性判断,必须进行实质性的集合差异比较。

它是什么#

兼容性评估模型#

兼容性评估是一个基于集合差异和版本比较的确定性计算过程。它接收目标平台的当前状态(包括功能集和客户端版本号)以及 Skill 的硬性需求(所需功能和最低版本),输出一个布尔值和一个缺口列表。

在这个模型中,required_features - platform.features 的差集直接暴露了平台缺失的功能,而 parse_version 函数将版本字符串转换为可比较的元组,从而确定客户端版本是否低于最低要求。

它不是什么#

兼容性评估的边界#

兼容性评估不是简单地检查平台名称是否存在,也不是一种基于启发式的模糊猜测。它不负责自动安装缺失的功能或升级客户端版本。

该机制不涉及对平台运行时性能的评估,也不检查 Skill 中附带脚本是否被平台信任执行;这些属于 Harness 和权限策略的范畴,超出了本阶段纯数据比较的边界。

它与相邻概念的关系#

组件间的依赖关系#

compatibility 函数依赖于 forge.versioning 模块中的 parse_version 函数来将版本字符串(如 “1.2.0”)解析为可比较的数值结构。

Platform 数据类作为输入数据的载体,其 features 字段必须是 frozenset 类型,以便进行高效的集合运算,而 required_features 作为 set 传入,两者相减产生缺失功能的迭代器。

缺口列表的生成顺序由 sorted() 函数保证,这使得测试断言可以精确匹配列表内容,避免了集合无序性导致的测试不稳定问题。

本阶段的边界决定: 当平台功能集是 required_features 的真子集时,必须立即判定为不兼容并报告缺失的具体功能项,即使客户端版本满足要求也不能改变这一结论。

STAGE 07

为三场景生成排序稳定、哈希可复核且不自动执行脚本的发布清单

先看一个具体问题#

发布清单缺失场景完整性校验导致部分场景被接受#

在当前的代码起点中,forge/packaging.py 文件尚未创建,测试在收集阶段就会因为 ModuleNotFoundError: No module named 'forge.packaging' 而中断。作为开发者,你需要从零创建该模块,并实现 build_inventory 函数以生成确定性的发布清单。

在构建故障实验室的缺陷代码时,build_inventory 函数完全忽略了传入的 scenarios 参数,直接对文件字典进行遍历并返回清单。这导致当调用者仅传入 {'software', 'office'} 时,系统仍然会生成发布清单,从而接受了不完整的场景包。

根据本模块的里程碑要求,发布清单必须强制覆盖 software、office 和 music 这三个场景。如果场景集合与要求不符,函数必须抛出包含特定错误信息的 ValueError 异常,以阻止不完整包的发布。

先做判断#

当 build_inventory 函数接收到 {'software', 'office'} 作为场景参数时,为了保证发布清单的完整性,该函数应当采取什么行为?

  • 直接返回空列表以跳过不完整的场景
  • 抛出包含 ‘software, office, and music’ 的 ValueError 异常
  • 自动补全缺失的 music 场景并继续生成清单
  • 仅打印警告信息并正常返回清单

判断依据: 正确的做法是立即中断执行并抛出 ValueError 异常,因为发布清单的契约要求恰好包含三个场景,任何缺失都意味着包不完整,不能通过校验。

它是什么#

确定性发布清单的构建契约#

确定性发布清单是 Skill 包中所有文件的有序记录,每个条目包含文件路径、基于文件内容计算的 SHA-256 哈希值以及可执行标志。它通过固定的排序规则和哈希校验,确保相同的输入文件集合在任何时间、任何环境下都能生成完全相同的清单输出。

在本阶段中,确定性还体现在场景完整性约束上:build_inventory 必须要求传入的 scenarios 参数恰好等于 {'software', 'office', 'music'},并在生成清单前进行严格校验,从而在打包阶段拦截不完整的场景配置。

它不是什么#

发布清单不是简单的文件列表拷贝#

发布清单不是对输入文件字典的直接遍历输出,因为字典的插入顺序在不同运行中可能不同,这会破坏确定性。同时,它也不是一个可以自动执行附带脚本的机制,所有文件的 executable 标志必须被硬编码为 False。

此外,场景校验不是一种软性建议或可忽略的警告,而是强制性的前置条件。函数不能在场景不匹配时静默补全或返回降级结果,必须通过抛出异常来明确拒绝不符合契约的输入。

它与相邻概念的关系#

场景校验、路径排序与哈希计算的协同#

场景校验是生成清单的第一道防线,它决定了函数是否继续执行;路径排序是确定性的核心机制,通过对文件名进行字典序排序,保证了输出列表的顺序稳定;哈希计算则为每个文件提供了内容级别的可复核性。

这三者协同工作:场景校验拦截不完整的包,路径排序保证输出顺序的确定性,而哈希值和 executable 标志则确保了文件内容的可追溯性和安全性。如果缺少场景校验,即使排序和哈希正确,也会产生不完整的清单。

本阶段的边界决定: 当 scenarios 参数不等于 {'software', 'office', 'music'} 时,函数必须立即抛出 ValueError 异常,绝不能进入文件遍历和清单生成阶段。

完成本章#

把 Forge 的需求抽取能力封装为可安装 Skill。

FORGE / LOCAL CHECK

本地实验自检

  1. 未开始
  2. 2阅读中
  3. 3实验已下载
  4. 4测试结果已读取
  5. 5本地自检通过

verification.json 只在当前浏览器中解析,不会上传。这里验证的是实验合同,不是服务器认证或第三方背书。

概念校准与一周复习

三道题检查你是否掌握了本章边界、交付证据和恢复方法。答案只保存在当前浏览器。

  1. 1哪一项最能证明你真正完成了「Skill、Prompt 与 Agent 能力包」?
  2. 2关于「Skill」,哪一种理解最准确?
  3. 3实验卡住时,哪个动作是本章建议的最小恢复点?