fischer-agentkit/docs/plans/2026-06-24-002-feat-skill-h...

317 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: "feat: Skill 激活前置条件 + 来源标记 + 风险守卫学习"
status: active
date: 2026-06-24
type: feat
origin: "SkillHarness (arXiv:2606.20636) + Agent Skills survey (arXiv:2602.12430) 对比分析"
---
## Summary
借鉴 SkillHarness 论文Macro/Micro Skill 分离、风险守卫 R、监督偏差与 Agent Skills 综述4 层门控权限模型、渐进式披露、26.1% 社区 skill 漏洞率)的观点,为 AgentKit 的 Skill 子系统补齐三个当前缺失的能力:
1. **激活前置条件preconditions+ 来源标记provenance** 作为 `SkillConfig` 基础设施preconditions 通过 system_prompt 注入实现软检查。
2. **16 个存量 Skill YAML 的 preconditions 全量审查与补充**(引擎模板除外)。
3. **RiskGuardLearner** 从失败轨迹学习风险守卫建议,强制人工审查后应用(不自动应用)。
明确**不**做基于轨迹的 skill 创建或边界细化L2/L3——只做 L1 风险守卫学习——因为 AgentKit 的 skill 是人工编写的 YAML论文核心问题轨迹学习导致的监督偏差在此不存在。
## Problem Frame
SkillHarness 论文的核心贡献是 Macro/Micro Skill 分离 + 风险守卫 R实验显示自动从轨迹学习的 skill 有 75% 不安全,引入风险守卫后不安全 skill 减少 57.1%。Agent Skills 综述指出 26.1% 的社区 skill 存在漏洞,并提出 4 层门控权限模型与 Artifacts vs In-use 区分。
对照 AgentKit 现状:
| 论文观点 | AgentKit 现状 | 差距 |
|---------|--------------|------|
| Macro Skill 激活前置条件preconditions | SkillConfig 无 preconditions 字段;`@skill:xxx` 命中即无条件执行 | **缺失** |
| Skill 来源标记provenance / Artifacts vs In-use | SkillLoader 三种加载路径YAML / SKILL.md / entry_points均不记录来源 | **缺失** |
| 危险能力告警 | entry_points 加载第三方 Skill 时无危险能力 warning | **缺失** |
| 风险守卫 R从失败轨迹学习 | EvolutionMixin 只优化 promptreflect→optimize→AB test不学习 skill 级风险守卫 | **缺失** |
| 4 层门控权限模型 | 已有 alignment 守卫v5+ quality_gate部分覆盖 | 部分实现 |
| 渐进式披露 | 已有 disclosure_levelv3 | 已实现 |
| 监督偏差(轨迹学习 skill | skill 是人工编写 YAML不从轨迹学习 | **不适用**(问题不存在) |
关键洞察:论文的监督偏差问题在 AgentKit 不存在(人工编写 skill因此**不引入** L2skill 边界细化)和 L3从轨迹创建新 skill。只引入 L1从失败轨迹学习风险守卫建议且必须人工审查。
## Requirements
- **R1**`SkillConfig` 新增 `preconditions: list[str] | None``provenance: str` 字段,完全向后兼容(旧 YAML 无字段时取默认值),`from_dict` / `to_dict` 正确序列化。
- **R2**`build_skill_system_prompt` 在拼装基础 prompt 后追加 preconditions 段落(软检查,不增加额外 LLM 调用preconditions 为空时不改变现有 prompt 输出。
- **R3**`SkillLoader` 三条加载路径记录 provenance`"yaml:<path>"` / `"skill_md:<path>"` / `"entry_point:<ep_name>"`entry_points 加载时若 Skill 声明了危险能力terminal / code_execution / file_write / shell / system_admin发出 `logger.warning`
- **R4**10 个业务 Skill YAML 审查并补充 preconditions 字段6 个引擎模板react/direct/rewoo/reflexion/plan_exec/goal_driven不需要 preconditions。
- **R5**`RiskGuardLearner` 从 `ExperienceStore` 检索失败轨迹,经 LLM 分析生成 `RiskGuardSuggestion`preconditions 候选 + 理由 + 置信度),**不自动应用**,输出供人工审查。
- **R6**CLI 新增 `agentkit skill learn-risk-guards` 命令,触发 RiskGuardLearner 并以 Rich 表格打印建议清单,明确标注"待人工审查"。
## Key Technical Decisions
### KTD1preconditions 通过 system_prompt 注入(软检查),不做硬 LLM 调用
**决策**preconditions 作为提示词约束注入 system_prompt由 LLM 在执行时自行判断是否满足,而非在 skill 激活前发起一次额外 LLM 调用做硬校验。
**理由**:硬校验会在每次 skill 激活时增加一次 LLM 调用延迟(~500ms-2s与 token 成本。AgentKit 的 `@skill:xxx` 路由追求零成本显式匹配(见 `RequestPreprocessor` Layer 0。软检查符合"显式调用即信任用户意图"的现有设计哲学preconditions 更多是引导 LLM 在条件不满足时拒绝或澄清,而非阻断路由。
**代价**preconditions 不是强保证——LLM 可能忽略。可接受的边界preconditions 是"激活后行为约束",不是"激活前权限门控"(后者由 alignment 守卫 v5 负责)。
### KTD2RiskGuardLearner 不自动应用,强制人工审查
**决策**`RiskGuardLearner` 只生成 `RiskGuardSuggestion`,不写入 SkillConfig必须由人工审查后手动编辑 YAML 应用。
**理由**SkillHarness 论文实验显示自动从轨迹学习的 skill 有 75% 不安全。AgentKit 虽然是"学习风险守卫建议"而非"学习新 skill",但自动写入 preconditions 仍可能引入错误约束(误判失败原因 → 错误 precondition → 阻断合法调用。human-in-the-loop 是最低成本的安全保证。
**代价**:无法闭环自动化。可接受:风险守卫学习是低频离线操作,不是实时路径。
### KTD3provenance 是轻量字符串,不做 hash/签名
**决策**`provenance` 为简单字符串(如 `"yaml:configs/skills/code_reviewer.yaml"`、`"entry_point:my_rag_skill"`),不做内容 hash 或签名校验。
**理由**AgentKit 当前无供应链合规需求provenance 的用途仅是"在日志和 `skill info` 中区分来源",便于排查"哪个 skill 来自第三方 entry_point"。引入 hash/签名会增加加载路径复杂度且当前无消费者。
**代价**:无法检测第三方 skill 被篡改。升级路径:未来若有合规需求,可在 provenance 字符串中追加 `:sha256=<hash>` 后缀,向后兼容。
---
## Scope Boundaries
### In scope
- `SkillConfig` 新增 `preconditions` / `provenance` 字段及序列化
- `build_skill_system_prompt` 注入 preconditions
- `SkillLoader` 三路径记录 provenance + entry_points 危险能力 warning
- 10 个业务 Skill YAML 补充 preconditions
- `RiskGuardLearner` 新模块(仅生成建议,不自动应用)
- `agentkit skill learn-risk-guards` CLI 命令
### Out of scope
- 从轨迹学习创建新 skillL3——论文监督偏差问题在 AgentKit 不存在
- 从轨迹细化 skill 边界L2——同上
- preconditions 的硬校验 LLM 调用——见 KTD1
- provenance 的 hash/签名——见 KTD3
- 4 层门控权限模型的完整实现——alignment 守卫 v5 已部分覆盖,本次不扩展
- RiskGuardLearner 自动应用闭环——见 KTD2
### Deferred to follow-up work
- `skill info` CLI 展示 preconditions / provenance 字段U6 之外的小增强,可后续补)
- RiskGuardSuggestion 的持久化存储(当前只打印,未来可存入 ExperienceStore
- 第三方 skill 的内容签名校验(见 KTD3 升级路径)
---
## Implementation Units
### U1. SkillConfig preconditions + provenance 字段基础设施
**Goal**:为 `SkillConfig` 新增 `preconditions``provenance` 字段,完成 `__init__` / `from_dict` / `to_dict` 三处改造,向后兼容。
**Requirements**R1
**Dependencies**:无(基础设施单元,后续 U2/U3/U4 依赖此单元)
**Files**
- Modify: `src/agentkit/skills/base.py`
- Test: `tests/unit/test_skill_config_preconditions.py`
**Approach**
-`SkillConfig.__init__` 签名末尾新增 `preconditions: list[str] | None = None``provenance: str = ""` 两个参数(放在 v6 `fallback_strategies` 之后,作为 v7 字段)。
- `__init__` 体内赋值 `self.preconditions = preconditions``self.provenance = provenance`
- `from_dict` 增加 `preconditions=data.get("preconditions")``provenance=data.get("provenance", "")`
- `to_dict` 增加 `d["preconditions"] = self.preconditions``d["provenance"] = self.provenance`
- 不新增校验逻辑preconditions 是字符串列表provenance 是字符串,无合法值约束)。
**Patterns to follow**v6 `fallback_strategies` 字段的添加方式(`src/agentkit/skills/base.py` 的 `__init__` 签名、`from_dict`、`to_dict` 三处对称改造)。
**Test scenarios**
- *Happy path*`SkillConfig(name="x", agent_type="y", preconditions=["用户已登录"], provenance="yaml:test.yaml")` 构造成功,字段可读。
- *Happy path*`SkillConfig.from_dict({"name":"x","agent_type":"y"})` 不传新字段时,`preconditions` 为 None、`provenance` 为 `""`(向后兼容)。
- *Happy path*`from_dict` 传入 preconditions 列表与 provenance 字符串时正确解析。
- *Edge case*`to_dict()` 输出包含 `preconditions``provenance` 键,值与构造时一致。
- *Edge case*`preconditions=[]`(空列表)与 `preconditions=None``to_dict` 中区分保留。
**Verification**`python3 -m pytest tests/unit/test_skill_config_preconditions.py -x -q` 通过;现有 `tests/unit/` 中涉及 SkillConfig 的测试不回归。
---
### U2. build_skill_system_prompt 注入 preconditions
**Goal**`build_skill_system_prompt` 在拼装基础 prompt 后,若 `skill_config.preconditions` 非空,追加 preconditions 段落,引导 LLM 在条件不满足时拒绝或澄清。
**Requirements**R2
**Dependencies**U1
**Files**
- Modify: `src/agentkit/chat/skill_routing.py`
- Test: `tests/unit/test_skill_system_prompt_preconditions.py`
**Approach**
-`build_skill_system_prompt` 现有 `"\n\n".join(prompt_parts)` 之后,检查 `skill_config.preconditions`
- 若非空列表,追加一段格式化文本(标题如 `## Activation Preconditions`,逐条列出 preconditions并附一句"若任一条件不满足,请拒绝执行或向用户澄清")。
- preconditions 为空或 None 时,返回值与现状完全一致(不改变现有行为)。
**Patterns to follow**`build_skill_system_prompt` 现有的 `prompt_parts.append` + `"\n\n".join` 模式(`src/agentkit/chat/skill_routing.py`)。
**Test scenarios**
- *Happy path*skill_config 有 preconditions=`["需要代码仓库访问权限", "当前分支非 main"]` 时,输出 prompt 包含 `## Activation Preconditions` 段落与两条条件文本。
- *Happy path*skill_config.preconditions 为 None 时,输出 prompt 与不传 preconditions 时完全一致(字节级)。
- *Edge case*skill_config.preconditions 为空列表 `[]` 时,不追加 preconditions 段落。
- *Edge case*skill_config 无 prompt 字段时,函数返回 None现有行为不变
- *Integration*preconditions 段落出现在 identity/context/instructions 等基础段落之后。
**Verification**`python3 -m pytest tests/unit/test_skill_system_prompt_preconditions.py -x -q` 通过。
---
### U3. SkillLoader 三路径 provenance 记录 + entry_points 危险能力 warning
**Goal**`SkillLoader` 的三条加载路径(`_load_skill_from_file` / `load_from_skill_md` / `load_from_entry_points`)在加载后设置 `config.provenance`entry_points 路径额外检查危险能力并 `logger.warning`
**Requirements**R3
**Dependencies**U1
**Files**
- Modify: `src/agentkit/skills/loader.py`
- Test: `tests/unit/test_skill_loader_provenance.py`
**Approach**
- 在模块顶部定义 `_DANGEROUS_CAPABILITIES = frozenset({"terminal", "code_execution", "file_write", "shell", "system_admin"})`
- `_load_skill_from_file``SkillConfig.from_yaml(path)` 后设置 `config.provenance = f"yaml:{path}"`
- `load_from_skill_md``SkillMdParser.to_skill_config(...)` 后设置 `config.provenance = f"skill_md:{path}"`
- `load_from_entry_points`:每个 Skill 加载后设置 `skill.config.provenance = f"entry_point:{ep.name}"`,并检查 `skill.config.capabilities`CapabilityTag 列表)中是否有 tag 命中 `_DANGEROUS_CAPABILITIES`,命中则 `logger.warning`
- provenance 设置在 `register` 之前,确保注册到 registry 的 config 已带 provenance。
**Patterns to follow**`load_from_entry_points` 现有的 `logger.info` 日志模式(`src/agentkit/skills/loader.py``CapabilityTag` 的 `tag` 字段访问方式(`src/agentkit/skills/schema.py`)。
**Test scenarios**
- *Happy path*`_load_skill_from_file` 加载 YAML 后,`skill.config.provenance` 为 `"yaml:<path>"`
- *Happy path*`load_from_skill_md` 加载后,`skill.config.provenance` 为 `"skill_md:<path>"`
- *Happy path*`load_from_entry_points` 加载后,`skill.config.provenance` 为 `"entry_point:<ep.name>"`
- *Error path*entry_points 加载的 Skill 声明了 `capabilities: [{tag: "shell"}]` 时,`logger.warning` 被调用且包含 skill 名与危险能力名。
- *Edge case*entry_points 加载的 Skill 无 capabilities 或 capabilities 为空时,不触发 warning。
- *Edge case*YAML 中已有 `provenance` 字段时,加载路径的设置覆盖它(加载路径是权威来源)。
**Verification**`python3 -m pytest tests/unit/test_skill_loader_provenance.py -x -q` 通过。
---
### U4. 10 个业务 Skill YAML 审查并补充 preconditions
**Goal**:审查 10 个业务 Skill YAML根据每个 skill 的实际语义补充 `preconditions` 字段6 个引擎模板不补充。
**Requirements**R4
**Dependencies**U1字段必须先存在
**Files**
- Modify: `configs/skills/code_reviewer.yaml`
- Modify: `configs/skills/geo_optimizer.yaml`
- Modify: `configs/skills/content_generator.yaml`
- Modify: `configs/skills/competitor_analyzer.yaml`
- Modify: `configs/skills/benchmark_runner.yaml`
- Modify: `configs/skills/trend_agent.yaml`
- Modify: `configs/skills/monitor.yaml`
- Modify: `configs/skills/citation_detector.yaml`
- Modify: `configs/skills/schema_advisor.yaml`
- Modify: `configs/skills/deai_agent.yaml`
**Approach**
- 逐个审查每个业务 skill 的 identity / instructions / tools / capabilities提炼出"激活此 skill 的前置条件"(如"需要可访问的代码仓库"、"需要网络连接"、"输入必须包含待审查的代码片段")。
- preconditions 用中文短句2-4 条为宜,聚焦"条件不满足会导致 skill 无法正常工作或产生误导"的场景。
- 引擎模板(`react_agent` / `direct_agent` / `rewoo_agent` / `reflexion_agent` / `plan_exec_agent` / `goal_driven_agent`)是通用执行模板,不补充 preconditions。
- 不修改 YAML 的其他字段,只新增 `preconditions` 键。
**Patterns to follow**:现有 YAML 的字段缩进与风格(如 `configs/skills/code_reviewer.yaml` 的 2 空格缩进、字符串引号风格)。
**Test scenarios**
- *Test expectation: none -- 纯配置变更,无行为代码*。验证方式:`SkillConfig.from_yaml` 对每个修改后的 YAML 加载成功且 `preconditions` 字段非空(引擎模板为 None
**Verification**`agentkit skill list` 正常加载全部 16 个 skill 无报错10 个业务 skill 的 `preconditions` 字段非空。
---
### U5. RiskGuardLearner 从失败轨迹学习风险守卫建议
**Goal**:新建 `RiskGuardLearner` 模块,从 `ExperienceStore` 检索失败轨迹,经 LLM 分析生成 `RiskGuardSuggestion` 列表preconditions 候选 + 理由 + 置信度),不自动应用。
**Requirements**R5
**Dependencies**U1preconditions 字段概念)、`ExperienceStore`(已存在)
**Files**
- Create: `src/agentkit/evolution/risk_guard_learner.py`
- Test: `tests/unit/test_risk_guard_learner.py`
**Approach**
- 定义 `RiskGuardSuggestion` dataclass`skill_name: str`、`precondition: str`、`reason: str`、`confidence: float`、`source_experience_ids: list[str]`。
- `RiskGuardLearner` 类:`__init__(experience_store, llm_gateway, model="default")`。
- `async def learn(self, skill_name: str | None = None, top_k: int = 20) -> list[RiskGuardSuggestion]`
-`ExperienceStore.search(query="failure", top_k=top_k, task_type=None)` 检索失败轨迹(`outcome == "failure"`)。
-`skill_name` 指定,过滤属于该 skill 的轨迹。
- 构建 LLM prompt输入失败轨迹摘要goal / steps_summary / failure_reasons / optimization_tips要求 LLM 输出"该 skill 应补充的 preconditions 候选"JSON。
- 解析 LLM 响应为 `RiskGuardSuggestion` 列表。
- LLM 失败时返回空列表并 `logger.warning`(不抛异常)。
- 明确不做:不写入 SkillConfig、不修改 YAML、不调用任何"应用"方法。
**Patterns to follow**`LLMReflector``src/agentkit/evolution/llm_reflector.py`)的 `__init__(llm_gateway, model)` 签名、`_sanitize_for_prompt` 提示词安全处理、LLM 失败时返回默认值的容错模式。
**Test scenarios**
- *Happy path*ExperienceStore 返回 3 条失败轨迹LLM 返回合法 JSON`learn()` 返回 3 条 `RiskGuardSuggestion`,字段完整。
- *Happy path*`skill_name` 过滤生效——只返回该 skill 的建议。
- *Error path*LLM 调用抛异常时,`learn()` 返回空列表且不抛异常。
- *Error path*LLM 返回非法 JSON 时,`learn()` 返回空列表并 `logger.warning`
- *Edge case*ExperienceStore 返回空列表时,`learn()` 返回空列表(不调用 LLM
- *Edge case*`confidence` 字段被 clamp 到 [0.0, 1.0] 区间。
**Verification**`python3 -m pytest tests/unit/test_risk_guard_learner.py -x -q` 通过;模块不导入任何"写入 SkillConfig"的路径。
---
### U6. CLI 命令 learn-risk-guards
**Goal**:新增 `agentkit skill learn-risk-guards` 命令,触发 `RiskGuardLearner`,以 Rich 表格打印建议清单,明确标注"待人工审查"。
**Requirements**R6
**Dependencies**U5
**Files**
- Modify: `src/agentkit/cli/skill.py`
- Test: `tests/unit/test_cli_skill_learn_risk_guards.py`
**Approach**
-`skill_app` 下新增 `@skill_app.command("learn-risk-guards")` 命令。
- 参数:`--skill`(可选,指定 skill 名)、`--top-k`(默认 20、`--server-url`(可选,远程模式预留,本地模式优先)。
- 本地模式:构造 `ExperienceStore`(需 PostgreSQL若无则提示"需要 PostgreSQL"并退出)+ `LLMGateway`,实例化 `RiskGuardLearner`,调用 `learn()`
- 用 Rich `Table` 打印建议:列含 Skill / Precondition / Confidence / Reason。
- 表格上方打印醒目提示:"以下为自动生成的风险守卫建议,**必须人工审查后手动编辑 YAML 应用**,不会自动生效。"
- 无建议时打印"未从失败轨迹中学习到风险守卫建议"。
**Patterns to follow**`skill list` 命令的 Rich `Table` 构造与 `rprint` 模式(`src/agentkit/cli/skill.py``skill list` 的本地/远程双模式结构。
**Test scenarios**
- *Happy path*`RiskGuardLearner.learn()` 返回 2 条建议时,命令输出包含 Rich 表格与 2 行建议,且包含"人工审查"提示文本。
- *Happy path*`learn()` 返回空列表时,命令输出"未从失败轨迹中学习到风险守卫建议"。
- *Error path*PostgreSQL 不可用时,命令打印明确错误信息并以非零码退出。
- *Edge case*`--skill` 参数透传给 `learn(skill_name=...)`
**Verification**`python3 -m pytest tests/unit/test_cli_skill_learn_risk_guards.py -x -q` 通过;`agentkit skill learn-risk-guards --help` 正常显示帮助。
---
## Risks & Dependencies
- **依赖 PostgreSQL**U5/U6 依赖 `ExperienceStore`PostgreSQL + pgvector。单元测试需 mock ExperienceStore不依赖真实数据库。
- **LLM 成本**U5 的 `learn()` 会发起一次 LLM 调用,但属低频离线操作,风险可控。
- **向后兼容**U1 新增字段必须不破坏现有 16 个 YAML 加载与现有 SkillConfig 测试——通过默认值保证。
- **preconditions 软检查的局限性**KTD1 明确 preconditions 不是强保证;若未来需要硬保证,需在 `RequestPreprocessor._resolve_explicit_skill` 中增加校验逻辑(本次不做)。
- **YAML 审查的主观性**U4 的 preconditions 内容依赖人工语义判断,需逐个 skill 阅读后提炼,无法自动化。
## Sources & Research
- **SkillHarness 论文**arXiv:2606.20636Macro/Micro Skill 分离、风险守卫 R、监督偏差、57.1% 不安全 skill 减少。核心借鉴preconditions 概念 + 风险守卫从失败学习 + 不自动应用。
- **Agent Skills 综述**arXiv:2602.124304 层门控权限模型、渐进式披露、26.1% 社区 skill 漏洞率、Artifacts vs In-use 区分。核心借鉴provenance 来源标记 + 危险能力告警。
- **AgentKit 现状代码**`src/agentkit/skills/base.py`SkillConfig v1-v6 字段演进)、`src/agentkit/chat/skill_routing.py`build_skill_system_prompt、`src/agentkit/skills/loader.py`(三路径加载)、`src/agentkit/evolution/llm_reflector.py`LLM 分析器模式)、`src/agentkit/evolution/experience_store.py`(失败轨迹检索)。
- **外部研究未运行**:本计划基于论文观点与代码现状的直接对照,未发起额外外部研究(论文已在上一轮对话中深度学习)。