# Skill 形式化设计：`Skill : Term → Term`

> 状态：草案 v0.1（待评审）
> 作者：lambdagent 团队
> 日期：2026-06-14
> 关联：[MCP_SKILL_DESIGN.md](MCP_SKILL_DESIGN.md)（现状 P2 prompt skill）、[PRODUCT_GATE_DESIGN.md](PRODUCT_GATE_DESIGN.md)（gate=Guard 形式化）、[harmonyos-agent-dsl-comparison.md](harmonyos-agent-dsl-comparison.md)（鸿蒙 DSL 对照）、Paper III（λA 类型化 Lambda 演算）

---

## 0. 一句话

把 skill 从「编译前被溶解进 systemPrompt 的 prompt 宏」升级为「term graph 里的一等组合子」——注入型 `Skill : Term → Term`（加能力）与对偶的约束型 `Restrict : Term → Term`（封顶能力），二者在效果格上构成伽罗瓦连接。由此让现有的 effect / cost grade / 类型 / Guard 四套形式化机器**免费**作用到 skill 上，并以此契约作为向鸿蒙 Agent DSL 移植的运行时无关 IR。

---

## 1. 背景与动机

### 1.1 现状：skill 是形式化盲区

当前 prompt skill 的生命周期（[skill_registry.py:188](../agentpaas/src/agentpaas/engine/skill_registry.py)）：

```
skill.yml (prompt + requires.tools)
   └─ mount_into_config(config)          # agentpaas 层，编译前
        ├─ prompt 追加到 systemPrompt（带受限边界包裹 + 注入扫描）
        └─ requires.tools 并进 mcp.localTools（高风险工具进权限 diff）
             └─ lambdagent.from_config(config) 才编译成 term graph
```

关键事实：**skill 在进入 term graph 之前就被内联溶解了**。等到 `Lam` / `Compose` / `Guard` / `estimate_cost` / `infer_effect_for_term` 开始工作时，skill 已不是独立对象。后果：

- `estimate_cost(term)`（[cost_grade.py:234](../lambdagent/src/lambdagent/cost_grade.py)）算不出"挂这个 skill 多花多少 token / 钱"；
- `infer_effect_for_term(term)`（[effects.py:309](../lambdagent/src/lambdagent/effects.py)）看不到 skill 引入的 IO/STATE 效果；
- skill 没有输入/输出类型，无法做 `is_subtype` 组合检查；
- skill 的"产出是否合格"（assignment-grader 必须产出含 `review_report` 标记的报告）没有被 `Guard` 强制，只能靠 prompt 自觉。

### 1.2 机会：四套机器都现成

一个 skill 现在携带的数据 `(prompt, requires.tools, requires.mcp_scopes)` 恰好够派生一个完整形式契约：

| 已有机器 | 文件 | 给 skill 派生 |
|---|---|---|
| `infer_effect_for_term` + 效果格 `pure ≤ state ≤ io ≤ llm` | [effects.py:309](../lambdagent/src/lambdagent/effects.py) | 效果上界 ε_s |
| `CostGrade` + `grade_*` + `estimate_cost` | [cost_grade.py:37](../lambdagent/src/lambdagent/cost_grade.py) | 成本天花板 g_s |
| `AgentType (τ₁ →^ε τ₂)` + `is_subtype` | `lam_types.py` | 输入/输出契约 |
| `Guard(agent, validator, retry, on_fail)` | [extensions.py:146](../lambdagent/src/lambdagent/extensions.py) | 验收谓词 P_s |

本设计就是把这四样东西显式地挂到每个 skill 上。

### 1.3 表征：skill 形态谱系（覆盖性证据）

为确保 `interface:` 设计能表征真实生态里的 skill（而非纸上谈兵），下面用本机磁盘上 6 类真实样例（项目自有 + gstack + Anthropic 生态，共 535 个 SKILL.md 中取跨度最大者）刻画 skill 的形态：

| 类 | 真实样例 | 形态 | 载体 | 实际是什么 | 对效果的作用 |
|---|---|---|---|---|---|
| **A 声明 prompt** | `assignment-grader`（本项目 [skill.yml](../agentexample/skills/assignment-grader/skill.yml)） | 纯方法论 prompt | 单 yml，无 frontmatter | prompt + `requires.tools` | 注入（↑） |
| **B 约束/拦截** | gstack `careful`/`freeze`/`guard` | 工具调用拦截器 | frontmatter `hooks.PreToolUse` + `bin/*.sh` | 危险命令拦截钩子（非 prompt） | **约束（↓）** |
| **C 工具+代码** | gstack `make-pdf` | prompt 指挥捆绑脚本 | 25KB prompt + `src/ dist/ test/` | prompt + 转换脚本 | 注入（↑） |
| **D 多步工作流** | gstack `qa` | test→fix→verify 循环 | 65KB prompt + `references/ templates/` | 多步骤、原子提交 | 注入（↑） |
| **E 外部桥接** | gstack `codex` | 包装外部 CLI | 61KB prompt，三模式 | 调 `codex` CLI 作第二意见 | 注入（↑，io） |
| **F 代码库** | Anthropic `pdf`/`docx`/`pptx`/`xlsx` | SKILL.md + 捆绑 Python | SKILL.md + 脚本 + 参考文档 | 可执行库 + 用法说明 | 注入（↑） |

**表征轴**（skill 在这些维度上连续变化）：

```
载体:    单 yml ─────────────────────────────▶ SKILL.md + 脚本目录 + 资源
能力:    纯 prompt ── +工具 ── +捆绑代码 ── +外部进程 ── +运行时钩子
对效果:  注入能力(↑) ◀──────────────────────────────▶ 约束能力(↓)   ← 双向！
执行:    改变 LLM 行为(声明式) ──────────────▶ 真正跑代码(命令式)
验收:    靠 prompt 自觉 ── 产物标记 ── pass/fail gate ── 脚本退出码
```

**关键发现（驱动 §3.5）**：A/C/D/E/F 都是**往 agent 里加能力**（`Skill : Term → Term`，效果只增），可统一归约到 §3.2 的 `Guard(Memory(...))`。但 **B 类（约束/拦截）方向相反**——它不注入，而是**砍效果上界 / 缩工具面 / 在工具调用前拦截**。因此单一的能力注入组合子覆盖不全 skill 谱系，需引入一个对偶的 `Restrict` 组合子（§3.5）。这两者构成效果格上的一对伽罗瓦连接，是本设计安全论证最硬的一块。

> 覆盖性结论：`{Skill, Restrict}` 两个组合子 + §4 `interface:`/`restrict:` schema 即可表征上表全部 6 类形态（脚本型 C/F 经 §7.3 normalize 后落到 `Tool`/`Compose`）。

---

## 2. 目标与非目标

### 2.1 目标

- **G1** 给 skill 一个形式语义 `Skill : Term → Term`，使其成为 term graph 的一等公民。
- **G2** 扩展 skill.yml 增加可选 `interface:` 段（类型 / 效果上界 / 验收谓词），向后兼容现有 prompt-only skill。
- **G3** skill 进入 term graph 后，自动获得 effect / cost / type 的派生与组合（整 agent 的界由 skill 的界按现有 `grade_serial/parallel` 合成）。
- **G4** 以 `interface:` 契约为运行时无关 IR，给出到鸿蒙 Agent DSL（`@agent`/`@prompt[pattern]`/`@tool`）的映射规则与可行性检查。
- **G5** 保持现有安全不变量：系统规则永远在前、注入扫描、高风险工具不自动授予。
- **G6** 覆盖 skill 全谱系（§1.3 六类）：注入型归 `Skill`、约束型归对偶组合子 `Restrict`（§3.5），二者构成效果格伽罗瓦连接，使 agent 最终能力有可证的上下界。

### 2.2 非目标

- **N1** 不追求"零不确定性"。本设计提供的是**显式且可校验的概率界**（`p`、`p_fatal`），不是确定性消除。
- **N2** 不改 lambdagent 编译器对 agentpaas 的依赖方向（编译器仍无 agentpaas 依赖）。
- **N3** 不在本期实现端侧（鸿蒙）codegen 落地，仅给出映射规格；落地见 §8 P3。
- **N4** 不把可执行脚本型 skill（OpenClaw/Claude SKILL.md + scripts）纳入本期；本期只覆盖 prompt skill + 工具授予型 skill，脚本型的 normalize 见 §7.3 与后续设计。

---

## 3. 核心语义：`Skill : Term → Term`

### 3.1 直觉

skill 不是一个 term（不是独立 agent），而是一个**作用在 agent 上的变换**：它给一个已有 agent 注入能力（prompt + 工具）并约束其产出。因此把 skill 建模为 term 上的组合子：

```
Skill_s : Term → Term
```

### 3.2 指称语义

设 skill `s = (prompt_s, tools_s, mcp_s, iface_s)`，作用在 agent term `A` 上：

```
Skill_s(A)  ≜  Guard(
                  Memory( A ⊕ prompt_s ⊕ tools_s ),   -- 注入能力（复用现有 mount 逻辑）
                  validator = P_s,                      -- iface_s.guard 派生的验收谓词
                  retry     = iface_s.retry,
                  on_fail   = iface_s.on_fail
               )
```

其中：

- `A ⊕ prompt_s`：把 `prompt_s` 以受限边界包裹追加到 `A` 的 systemPrompt（**沿用** [skill_registry.py:223-226](../agentpaas/src/agentpaas/engine/skill_registry.py) 的包裹文案与注入扫描，语义不变）。
- `A ⊕ tools_s`：把 `tools_s` 并进 `A` 的 localTools（**沿用** [skill_registry.py:228-235](../agentpaas/src/agentpaas/engine/skill_registry.py) 的高风险工具不自动授予规则）。
- `Memory(·)`：复用 [extensions.py:109](../lambdagent/src/lambdagent/extensions.py)，若 skill 不需要持久态可退化为恒等包裹（无 STATE 效果）。
- `Guard(·, P_s, ...)`：复用 [extensions.py:146](../lambdagent/src/lambdagent/extensions.py)。**若 `iface_s` 未声明 guard，则 `P_s = λ_. True`**（恒真谓词，retry=0）——退化为「不验收」，与今天行为等价。

> **要点**：当 `iface_s` 缺省（旧 skill）时，`Skill_s(A)` 在可观测行为上**严格等价**于今天的 `mount_into_config`——只是现在它是 term graph 里一个具名节点，能被 cost/effect 看见。这是 G2 向后兼容的形式保证。

### 3.3 组合律

多个 skill 挂同一 agent，定义为组合子的函数复合：

```
[s₁, s₂](A)  ≜  Skill_{s₂}(Skill_{s₁}(A))
```

即 `mount` 的 skill 列表顺序 = 组合子从内到外的顺序。Prompt 追加顺序、工具并入顺序与今天一致（列表序）。

**注意非交换性**：`Skill_{s₁} ∘ Skill_{s₂} ≠ Skill_{s₂} ∘ Skill_{s₁}`，因为 Guard 嵌套层次不同（外层 Guard 看到的是内层 Guard 之后的结果）。评审点 R1：是否需要把多 skill 的 Guard 合并成单层合取 `P_{s₁} ∧ P_{s₂}` 而非嵌套？见 §10。

### 3.4 与 PRODUCT_GATE 的一致性

PRODUCT_GATE_DESIGN.md 已确立 `gate = Guard`。本设计把 skill 也归约到 `Guard`，二者同源：pipeline 的 gate 验收 stage 产物，skill 的 Guard 验收单 agent 产物。两者都走 `grade_guard`（[cost_grade.py:164](../lambdagent/src/lambdagent/cost_grade.py)）计成本。

### 3.5 对偶组合子：`Restrict : Term → Term`（约束型 skill）

§1.3 的 B 类（`careful`/`freeze`/`guard`）方向与 `Skill_s` 相反：它**不注入能力，而是封顶能力**。建模为 `Skill_s` 的对偶：

```
Restrict_r(A)  ≜  A   但施加三层约束：
                    ε'      = effect_meet(ε_A, ε_allow)        -- 效果取下确界（砍，非取并）
                    tools'  = tools_A ∩ tools_allow            -- 工具面取交（缩，非并）
                    每次 Tool 调用前过守卫谓词 H_r             -- PreToolUse 拦截，命中则 ask/deny
```

其中：

- `effect_meet`：效果格 `pure ≤ state ≤ io ≤ llm` 上的**下确界**（与 §5.1 `max_effect` 的上确界对偶）。例：`Restrict` 把 `io` 砍成 `pure` ⟹ agent 不再能调外部工具。需在 [effects.py](../lambdagent/src/lambdagent/effects.py) 补 `min_effect`（对偶于 `max_effect`，[effects.py:244](../lambdagent/src/lambdagent/effects.py)）。
- `tools_allow`：允许工具白名单；`A` 原有但不在白名单的工具被移出 localTools。
- `H_r`：工具调用守卫，对应 careful 的 `hooks.PreToolUse`（[careful/bin/check-careful.sh](../../../.claude/skills/gstack/careful/bin/check-careful.sh) 那样的危险命令拦截）。形式上是 Tool 层的 `Guard`：`Tool_guarded = λx. if H_r(call) then tool(x) else REFUSE`。复用 builtin 的 `dangerousCommandBlock`/`highRiskConfirmation` guard 机制（记忆「AgentEdit 加 Guard tab」），不需新拦截器。

**安全代数（核心论证）**：`Skill`（注入，`effect_leq(ε_A, ε_after)`，单调增）与 `Restrict`（约束，`effect_leq(ε_after, ε_A)`，单调减）在效果格上构成一对**伽罗瓦连接**。对任意 agent `A` 与 skill 集，最终能力可证地被夹在区间内：

```
effect_leq( Restrict 施加的下界 ,  ε_最终 )  ∧  effect_leq( ε_最终 ,  Skill 累积的上界 )
```

这把"形式化压住不确定性"从单纯的**输出验收**（Guard）推进到**工具调用层的静态能力封顶**——agent 能做什么有了可证的上下界，而非运行时撞墙。

**组合顺序约定**：`Restrict` 总在最外层（`Restrict_r(Skill_{s₂}(Skill_{s₁}(A)))`），即先注入能力再统一封顶，保证约束不被后续 skill 突破。`mount` 时若同时声明 `skills` 与约束型 skill，约束型一律后挂（最外）。评审点 R6（§10）：是否允许约束型嵌在中间以做分层授权？

### 3.6 三组合子全景

| 组合子 | 文件/状态 | 语义 | 对效果 | 覆盖 §1.3 类 |
|---|---|---|---|---|
| `Skill : Term→Term` | 新（归约到 Guard∘Memory） | 注入 prompt+工具+验收 | `max`（↑） | A/C/D/E/F |
| `Restrict : Term→Term` | 新（归约到 effect_meet + Tool-Guard） | 砍效果+缩工具+调用守卫 | `meet`（↓） | B |
| `Guard`（已有） | [extensions.py:146](../lambdagent/src/lambdagent/extensions.py) | 输出验收+重试 | 不变 | 二者复用 |

---

## 4. skill.yml 的 `interface:` 扩展

### 4.1 新 schema（增量、可选）

在现有字段（`name/version/description/prompt/requires/examples/enabled`，见 [assignment-grader/skill.yml](../agentexample/skills/assignment-grader/skill.yml)）之上，**新增可选** `interface:` 段：

```yaml
name: assignment-grader
version: 0.2.0
description: ...
prompt: |
  ...
requires:
  tools: [ReadFile, WriteFile, ListFiles, SearchContent, KBSearch, KBList]
  mcp_scopes: []

# ── 新增：形式契约（全部可选；缺省则退化为今天行为）──
interface:
  input_type:  Str                  # τ_in，默认 Str
  output_type: Json                 # τ_out，默认 Str；可为 Str/Int/Float/Bool/Json/Any
  effect_hint: io                   # 效果上界声明 pure|state|io|llm，默认由 requires 推断
  state_keys: []                    # STATE 效果涉及的 memory key（用于 effect 精化）
  guard:                            # 验收谓词（缺省 = 恒真，不验收）
    kind: rule                      # rule | judge | none
    predicate: "'review_report' in x or 'verdict' in x or '建议总评' in x"
    retry: 1                        # 失败重试次数（k = 1 + retry）
    on_fail: last                   # last（返回末次结果）| error（抛 ValidationError）
  cost_hint:                        # 成本先验（可选，用于无历史数据时的估算锚点）
    tokens: 1200
    latency: 6.0
    p_useful: 0.85                  # 单次产出通过验收的概率
    p_fatal: 0.02                   # 单步致命失败率

  # ── 约束型 skill（§3.5 Restrict 组合子）；与上面注入型字段互斥使用 ──
  restrict:                         # 存在则该 skill 编译为 Restrict 而非 Skill
    effect_ceiling: io              # 效果上界封顶 pure|state|io|llm（effect_meet）
    allow_tools: [ReadFile, ListFiles]   # 工具白名单（与 agent 原工具取交）；缺省=不缩
    deny_patterns:                  # 工具调用守卫 H_r：命中则 ask/deny
      - regex: 'rm\s+-rf'
        action: ask                 # ask | deny
      - regex: 'DROP\s+TABLE'
        action: deny

examples: [...]
enabled: true
```

> **互斥约定**：一个 skill 要么是注入型（有 `prompt`/`requires`/`interface.guard`），要么是约束型（有 `interface.restrict`）。同时出现 → `validate_skill` 报错。约束型 skill 的 `prompt` 可为空。

### 4.2 字段语义与默认

| 字段 | 类型 | 默认 | 含义 |
|---|---|---|---|
| `interface.input_type` | LamType 名 | `Str` | 编译为 `AgentType.input_type`，参与 `is_subtype` 组合检查 |
| `interface.output_type` | LamType 名 | `Str` | 同上，输出类型 |
| `interface.effect_hint` | `pure\|state\|io\|llm` | 由 `requires` 推断 | 声明的效果上界；实际效果取 `max(声明, 推断)` |
| `interface.state_keys` | `[str]` | `[]` | 非空时引入 `STATE(keys)` 效果，触发 `Memory` 包裹 |
| `interface.guard.kind` | `rule\|judge\|none` | `none` | rule=受限 DSL 谓词；judge=LLM-judge（复用 pipeline 的 default_judge）；none=不验收 |
| `interface.guard.predicate` | str | — | `kind=rule` 时的谓词表达式（**受限 DSL，见 §4.3**） |
| `interface.guard.retry` | int | `0` | Guard 重试次数 |
| `interface.guard.on_fail` | `last\|error` | `last` | 验收始终失败时的兜底 |
| `interface.cost_hint.*` | num | 见 §5.3 | 无历史数据时 `estimate_cost` 的锚点 |
| `interface.restrict.effect_ceiling` | `pure\|state\|io\|llm` | 无 | 约束型：效果上界封顶（`effect_meet`，§3.5） |
| `interface.restrict.allow_tools` | `[str]` | 无（不缩） | 约束型：工具白名单，与 agent 原工具取交 |
| `interface.restrict.deny_patterns` | `[{regex, action}]` | `[]` | 约束型：工具调用守卫 H_r，`action ∈ {ask, deny}` |

### 4.3 guard.predicate 的受限 DSL

**不得使用 Python `eval`**。复用 PRODUCT_GATE_DESIGN.md 的受限 gate DSL 求值器（AST 白名单 + `has/count/contains/regex/json_len`），变量 `x` 绑定到 agent 输出。安全要求与现有 gate 一致：

- 仅允许白名单 AST 节点（比较、布尔、成员、`in`、字符串方法子集）；
- regex 带长度上限 + 窗口缓解（防 ReDoS）；
- 求值异常 → 判 `False`（验收不通过），不得吞成 `True`。

`kind=judge` 时复用 pipeline 的 `default_judge(JudgeRequest)`（本地 ollama 默认），阈值默认 0.7，可在 `guard` 下加 `threshold` 覆盖。

### 4.4 校验扩展

`validate_skill`（[skill_registry.py:101](../agentpaas/src/agentpaas/engine/skill_registry.py)）增量校验：

- `input_type/output_type` ∈ {Str,Int,Float,Bool,Json,Any,None,Tuple,Union}（解析失败 → 报错）；
- `effect_hint` ∈ {pure,state,io,llm}；
- `guard.predicate` 静态校验（AST 白名单 + regex 长度），不通过 → 报错（同 gate 静态校验）；
- `state_keys` 非空但 `effect_hint=pure` → 报错（自相矛盾）。
- **互斥**：`interface.restrict` 与（`prompt` 非空 / `requires.tools` 非空 / `interface.guard`）同时出现 → 报错（§3.5/§4.1 互斥约定）。
- `restrict.deny_patterns[].regex` 同 §4.3 受限 DSL 的 regex 约束（长度上限 + ReDoS 缓解）；`action ∈ {ask, deny}`。

现有注入扫描、name 正则、builtin 工具白名单**保持不变**。

---

## 5. 形式属性的派生规则

### 5.1 效果派生 ε_s

skill `s` 的效果上界：

```
ε_s = max_effect(
        LLM(model_A),                              -- skill 注入的 prompt 被 A 的模型消费
        io   if tools_s ∩ {IO 类工具} ≠ ∅ else pure,
        STATE(state_keys) if state_keys ≠ ∅ else pure,
        effect_hint                                -- 声明上界，取并
      )
```

实现：把 `Skill_s(A)` 当作普通 term 交给 `infer_effect_for_term`（[effects.py:309](../lambdagent/src/lambdagent/effects.py)）即可——因为 `Skill_s(A)` 展开后就是 `Guard(Memory(A'))`，三者的 effect 规则（[effects.py:330-370](../lambdagent/src/lambdagent/effects.py)）已覆盖。`effect_hint` 仅作为 `max_effect` 的额外下界，确保**声明的能力上界不被低估**。

**不变量 I-EFF**：`Skill_s(A)` 的效果 ⊒ `A` 的效果（skill 只增不减能力），用 `effect_leq(ε_A, ε_{Skill_s(A)})` 断言。对偶地，`Restrict_r(A)` 的效果 ⊑ `A` 的效果（**不变量 I-RES**，§3.5），用 `effect_leq(ε_{Restrict_r(A)}, ε_A)` 断言——二者即伽罗瓦连接的上下界。

### 5.2 成本派生 g_s

`Skill_s(A) = Guard(Memory(A'), P_s, retry)`，故：

```
g_{Skill_s(A)} = grade_guard( g_{A'}, retries = iface_s.retry )
```

其中 `g_{A'}` = `A` 注入 prompt/tools 后的 grade。`grade_guard`（[cost_grade.py:164](../lambdagent/src/lambdagent/cost_grade.py)）已实现：

```
p       = 1 - (1 - p_useful)^k,  k = 1 + retry      -- 至少一次通过验收
t,l,m   = k × (t,l,m)                               -- 重试线性放大
p_fatal 不变                                         -- Guard 不修致命错（关键诚实点）
```

整 agent 仍由 `estimate_cost(term)`（[cost_grade.py:234](../lambdagent/src/lambdagent/cost_grade.py)）一次递归得到——因为 skill 现在就是 term graph 的节点，不需要额外的"skill 成本表"。

### 5.3 cost_hint 的作用域

`estimate_cost` 对 `Lam` 的基线是固定的（800 token / 2.0s）。skill 注入的 prompt 会让真实 token 偏离基线。处理：

- 若 `interface.cost_hint` 存在 → 用它覆盖该 skill 包裹层的基线（`t=cost_hint.tokens` 等）；
- 否则用基线 + prompt 长度的线性修正 `t += len(prompt_s) // 4`（CJK 低估问题见记忆「审稿67 三连修复」，沿用 chars//4 但**仅作上界估算**）；
- `validate_cost(predicted, actual)`（[cost_grade.py:364](../lambdagent/src/lambdagent/cost_grade.py)）跑完后回填真实值，迭代收敛 cost_hint（见 §8 P2：从 trace 学习 hint）。

### 5.4 类型派生

`Skill_s(A)` 的 `AgentType`：

```
input_type  = iface_s.input_type   (默认继承 A.input_type)
output_type = iface_s.output_type  (默认继承 A.output_type)
effect      = ε_s   (§5.1)
```

组合 `[s₁, s₂](A)` 时，编译期检查 `output_type(Skill_{s₁}(A)) <: input_type(s₂)`（`is_subtype`），不满足 → 编译告警（非致命，因 skill 多为 Str→Str，宽松）。

---

## 6. 与鸿蒙 Agent DSL 的契约映射

> 目标：`interface:` 契约作为运行时无关 IR，使同一 skill 既能编译成 lambdagent term，也能编译成鸿蒙 Agent DSL（CangjieMagic `@agent`/`@prompt`/`@tool`）。映射依据见 [harmonyos-agent-dsl-comparison.md](harmonyos-agent-dsl-comparison.md)。

### 6.1 结构映射表

| lambdagent skill `interface` | 鸿蒙 Agent DSL / CangjieMagic | 说明 |
|---|---|---|
| `prompt_s`（受限边界包裹） | `@prompt[pattern: APE]( action/purpose/expectation )` | skill prompt → 结构化 prompt 模式；自由文本需切成 action/purpose/expectation 三元组 |
| `requires.tools`（builtin 白名单） | `@tool[description](func ...)` | 每个 builtin 工具映射到一个端侧 `@tool` 方法；端侧无对应实现 → 标记 unsupported |
| `interface.input_type/output_type` | `func skill(input: τ_in): τ_out` | LamType → 仓颉类型（Str→String, Json→自定义 struct/JSON） |
| `interface.effect_hint` | 端侧能力清单（网络/文件/IoT 权限） | `io` → 需端侧 ability 权限；`state` → 端侧 KV/Preferences |
| `interface.guard`（rule/judge） | 端侧后处理校验函数 / 端侧小模型 judge | rule → 仓颉纯函数校验；judge → 端侧盘古小模型 |
| `interface.cost_hint` | 端侧推理预算（token/延迟） | 用于端侧模型选择（小模型 vs 云端回退） |
| `@agent[model]` | `model_A` | skill 本身无 model，继承宿主 agent 的 model |

### 6.2 可行性检查（编译期判定）

把"这个 skill 能不能在端侧跑"变成可判定问题：给端侧一个**能力配置** `Caps_device = (ε_device_max, g_device_budget, tools_device)`，判定：

```
portable(s, device) ⟺
    effect_leq(ε_s, ε_device_max)                 -- skill 效果不超过端侧允许
  ∧ tools_s ⊆ tools_device                         -- 所需工具端侧都有实现
  ∧ grade_leq(g_s, g_device_budget)                -- 成本不超端侧预算（token/延迟/钱）
```

`grade_leq` 定义为逐分量 ≤（`t≤t' ∧ l≤l' ∧ m≤m'`，且 `p≥p'_min`）。任一条不满足 → **编译期报告"端侧不可移植"及具体缺口**，而非端侧运行时炸。这是形式化相对于"直翻 DSL"的核心价值。

### 6.3 降级策略

`portable(s, device)` 为假时的自动降级建议（codegen 产出告警 + 选项）：

- 工具缺失 → 端侧调云端 MCP 回退（牺牲端侧自治，`effect_hint` 升至 `io` 远程）；
- 成本超预算 → 换更小端侧模型 + 收紧 `guard.retry`；
- 效果超限 → 拒绝移植，要求人工裁剪 skill 能力。

### 6.4 与 CangjieMagic 的对齐点

CangjieMagic（仓颉原生 Agent 框架，MCP 原生）已有 `@agent/@prompt/@tool` 三件套但**无形式化安全保障**（[harmonyos-agent-dsl-comparison.md:118](harmonyos-agent-dsl-comparison.md)）。本设计的 `interface:` 契约正是它缺的那层——可作为「给 CangjieMagic skill 加类型/效果/成本契约」的上游 IR，是差异化的研究/工程卖点。

---

## 7. 兼容与迁移

### 7.1 向后兼容

- 无 `interface:` 段的旧 skill：编译为 `Skill_s(A) = Memory(A')`（无 Guard，恒等验收），行为等价今天的 `mount_into_config`（§3.2 形式保证）。
- API（`/skills` CRUD）、存储路径（`{data_dir}/skills/<name>/skill.yml`）、注入扫描、权限 diff **全部不变**。

### 7.2 迁移路径

1. P0：`mount_into_config` 内部改为构造 `Skill_s` 组合子节点，但**输出仍是等价的 config**（保证零行为变化），同时返回 skill 的 term 句柄供 cost/effect 查询。
2. P1：开放 `interface:` 段，老 skill 逐个补 guard/类型。
3. 给现有 11 个内置 skill 补 `interface:`（assignment-grader 的 guard 已经隐含在 prompt 里，抽出来即可）。

### 7.3 脚本型 skill（OpenClaw/Claude）normalize（后续）

OpenClaw/Claude 的 SKILL.md + scripts + workflow 比 prompt skill 重。normalize 思路（不在本期实现，仅记录）：

- SKILL.md frontmatter → `name/description/examples`；
- SKILL.md 正文 → `prompt`；
- 脚本调用 → 包装成 builtin Tool 或 MCP scope，进 `requires`；
- workflow 步骤 → 若线性则 `Compose`，若带验收则每步 `Guard`。
- 由此脚本型 skill 也获得 `interface:` 契约 → 可走 §6 的鸿蒙移植判定。

---

## 8. 实施分期与验收

| 期 | 范围 | 验收 |
|---|---|---|
| **P0** 内核抬升 | `Skill : Term → Term` 组合子；`mount` 改造为构造组合子但输出等价 config | 回归：所有现有 skill 挂载后 config 字节级等价；新增 `estimate_cost(Skill_s(A))` / `infer_effect_for_term(Skill_s(A))` 返回非平凡值的单测 |
| **P1** interface 段 | skill.yml `interface:` 解析 + 校验 + Guard 编译；受限 DSL 谓词复用 gate 求值器 | 单测：guard 谓词命中/不命中、retry 计数、on_fail；`validate_skill` 拒绝非法类型/注入谓词/注入-约束互斥 |
| **P1b** Restrict 组合子 | `Restrict : Term → Term` + `min_effect`/`effect_meet`；`restrict:` 解析；工具调用守卫复用 dangerousCommandBlock | 单测：I-RES 不变量（`effect_leq(ε_Restrict, ε_A)`）、工具面取交、deny_patterns 命中 ask/deny；把 gstack `careful` normalize 成 `restrict:` 并验证拦截 |
| **P2** 成本闭环 | `cost_hint` 估算 + `validate_cost` 回填学习 | live 测：assignment-grader 实跑，predicted vs actual 在 2× 阈值内 |
| **P3** 鸿蒙映射 | `interface → 鸿蒙 Agent DSL` codegen + `portable(s,device)` 检查 | 给定一个 device caps，对内置 skill 输出可移植/不可移植报告 + Cangjie 代码骨架 |

### 8.1 回归基线（P0 硬门槛）

- `test_skill_mount_equivalence`：对每个内置 skill，`mount_into_config` 新旧实现的 config 输出 deep-equal。
- `test_skill_term_cost`：`estimate_cost` 对挂 skill 与不挂 skill 的 agent 给出 `g_with ≥ g_without`（单调）。
- `test_skill_effect_monotone`：`effect_leq(ε_without, ε_with)`（I-EFF）。

---

## 9. 形式化对齐（论文角度）

- skill = term 组合子 `Term → Term`，是 Paper III §Guard/§Effect 的应用扩展，几乎不引入新原语（复用 `Guard`/`Memory`/`max_effect`/`grade_guard`，仅补对偶的 `min_effect`）。
- **注入 `Skill` 与约束 `Restrict` 构成效果格上的伽罗瓦连接**：agent 最终能力 `ε_final` 被可证地夹在 `[Restrict 下界, Skill 上界]`。这是一条干净的"能力代数"结果，把"压住 LLM 不确定性"从输出验收推进到工具调用层的静态能力封顶——是本工作相对纯 prompt 工程最硬的形式化卖点。
- 可移植性判定 `portable(s, device)` 是 effect 子类型 + grade 偏序的联合判定，属于"带资源语义的能力移植"——契合 AgentOS 定位（见记忆「AgentOS 定位策略」）。`Restrict` 通过降低效果上界反而**增大**可移植性（R7），是端侧降级的形式化手段。
- 候选论文角度：**"Portable Agent Skills with Cost/Effect Contracts"**——skill 作为带契约的可移植能力，端侧/云端统一 IR。可作 Paper34（OOPSLA 全文）的一个实证案例，或独立 vision。

---

## 10. 开放问题（评审点）

- **R1（§3.3）** 多 skill 的 Guard：嵌套 `Guard(Guard(...))` vs 合并单层合取 `P₁ ∧ P₂`？嵌套语义更直观（每 skill 独立验收+重试），但 retry 成本相乘可能爆炸（`k₁ × k₂`）。倾向：默认嵌套，提供 `guard.merge: and` 选项合并。
- **R2（§5.1）** `effect_hint` 取 `max(声明, 推断)` 是否过严？若 skill 声明 `io` 但实际从不调工具，会高估效果，导致 §6.2 误判不可移植。缓解：以推断为主，`effect_hint` 仅作上界校验（声明 < 推断时报错）。
- **R3（§5.3）** CJK token 估算 `chars//4` 已知低估（记忆「审稿67 三连修复」）。cost_hint 缺省时是否要求 skill 作者必填，避免 Guard 重试把低估放大 k 倍？
- **R4（§4.3）** guard `kind=judge` 引入 LLM 调用 → skill 自身有了 LLM 效果与成本，需计入 `g_s`（`grade_guard` 当前假设 validator 零成本）。是否复用 PRODUCT_GATE 的 `build_judge_term` 把 judge 也折进 term 图计成本？
- **R5（§6.1）** 自由文本 prompt → 鸿蒙 `@prompt[pattern]` 三元组（action/purpose/expectation）需要结构化，非平凡。是否要求新 skill 的 prompt 也写成三元组结构以利移植？
- **R6（§3.5）** `Restrict` 组合顺序：默认最外层（注入后统一封顶）能保证约束不被突破，但牺牲了分层授权（"先严后宽"的中间放开）。是否需要允许 `Restrict` 嵌在中间，代价是失去"最终能力 ≤ 全局下界"的简单不变量？
- **R7（§3.5/§6.2）** `Restrict.effect_ceiling` 砍效果后，§6.2 的 `portable(s, device)` 判定如何与之交互？约束型 skill 反而**增大**可移植性（效果更低 → 更易满足端侧上界），可作端侧降级（§6.3）的形式化手段——值得在鸿蒙映射里专门利用。

---

## 附：受影响文件清单（预估）

| 文件 | 改动 |
|---|---|
| `agentpaas/src/agentpaas/engine/skill_registry.py` | `mount_into_config` 构造组合子；`validate_skill` 增 interface 校验 |
| `lambdagent/src/lambdagent/skills.py` 或新 `skill_term.py` | `Skill` 与 `Restrict` 两个 `Term → Term` 组合子实现 |
| `lambdagent/src/lambdagent/effects.py` | `infer_effect_for_term` 覆盖 Skill/Restrict 组合子；新增 `min_effect`（对偶于 `max_effect`，供 `Restrict` 的 `effect_meet`） |
| `lambdagent/src/lambdagent/cost_grade.py` | `estimate_cost` 覆盖 Skill 组合子；cost_hint 注入 |
| `agentexample/skills/*/skill.yml` | 内置 skill 补 `interface:` 段 |
| `docs/MCP_SKILL_DESIGN.md` | §3 形式化对齐回链本文档 |
| 新 `tests/test_skill_formalization.py` | §8.1 回归 |
