跳转到内容

Skills 技能系统

Gemini CLI — Skills 技能系统

学习目标

  • 理解四层技能发现机制与优先级覆盖规则
  • 掌握运行时技能激活(activate-skill)的工作方式
  • 分析后台自动技能提取的触发条件和节流策略

前置知识

本章涉及 Skill 系统的通用原理,建议先阅读:

下文假设你已理解三层渐进式披露和触发短语设计,直接聚焦 Gemini CLI 的发现、激活和提取机制。


项目实践

四层技能发现与优先级

SkillManager.discoverSkills() 按以下顺序加载技能,后加载的覆盖先加载的同名技能

加载代码packages/core/src/skills/skillManager.ts):

discoverSkills():
1. discoverBuiltinSkills() // 内置
2. for ext in extensions: // 扩展
if ext.isActive && ext.skills:
addSkillsWithPrecedence(ext.skills)
3. loadSkillsFromDir(userSkillsDir) // 用户
loadSkillsFromDir(userAgentSkillsDir) // 用户别名
4. if isTrusted: // 工作区(需信任)
loadSkillsFromDir(projectSkillsDir)
loadSkillsFromDir(projectAgentSkillsDir)

工作区技能仅在文件夹被信任时加载isTrusted 检查),防止恶意工作区通过技能注入不当行为。

技能文件格式

技能文件使用 Markdown frontmatter 格式:

---
name: my-skill
description: 当用户需要 X 时激活此技能
---
# My Skill
当检测到用户的意图涉及 X 时,执行以下步骤...

加载解析skillLoader.ts):

  • 使用 FRONTMATTER_REGEX 匹配 --- 包裹的 YAML frontmatter
  • parseFrontmatter 提取 namedescription
  • 其余 Markdown 内容作为技能指令注入到系统提示中

运行时技能激活

Gemini CLI 提供了一个 activate-skill 工具,允许 Agent 在执行过程中主动激活某个技能:

LLM: functionCall("activate-skill", { name: "code-review" })
ActivateSkillTool.execute():
├── 从 SkillManager 查找技能定义
├── 将技能指令注入当前会话的上下文
└── 返回激活确认消息

与普通触发的区别

  • 普通触发:技能在 turn 开始前被动加载(通过 PromptProvider)
  • 运行时激活:Agent 在 turn 中间主动请求加载技能,适用于 Agent 在任务中途发现自己需要新知识的情况

Agent 技能别名

.agents/skills 目录下的技能作为 Agent 能力的扩展。这些技能与常规技能并行加载,但来源不同:

来源目录用途
常规技能skills/通用工作流(如代码审查、文档生成)
Agent 别名.agents/skills为特定 Agent 定制的能力扩展

这种分离允许用户为不同的 Agent 配置不同的技能集,而不影响全局技能。

自动技能提取

MemoryService 中的后台任务自动从历史会话中提取新技能:

后台提取流程:
1. 检测空闲: 空闲时间 ≥ 3 小时
2. 批量加载: 每次处理最多 10 个未索引会话
3. SkillExtractionAgent: 使用专用 Agent 分析对话模式
4. 注册技能: 提取结果保存为新 Skill 文件
5. 节流: 两次提取间隔 ≥ 30 分钟

提取阈值memoryService.ts):

  • MIN_USER_MESSAGES = 10:至少 10 条用户消息才考虑提取
  • MIN_IDLE_MS = 3 * 60 * 60 * 1000:空闲 3 小时才触发
  • MAX_SESSION_INDEX_SIZE = 50:最多索引 50 个会话
  • MAX_NEW_SESSION_BATCH_SIZE = 10:每批最多处理 10 个新会话

并发协调

  • 使用 .extraction.lock 文件防止多个 CLI 实例同时运行提取
  • 锁超时时间 35 分钟(超过 Agent 的 30 分钟最大执行时间,确保过期锁可被清理)
  • 使用 .extraction-state.json 记录已处理的会话,避免重复提取

技能与记忆系统的联动

技能提取与记忆系统紧密协作:

  1. 会话历史先被 MemoryService 索引
  2. SkillExtractionAgent 分析索引后的会话
  3. 提取的技能注册到 SkillManager
  4. 下次会话启动时,新技能可被自动发现和激活

这种”对话 → 记忆 → 技能提取 → 技能注册 → 未来对话使用”的闭环使得 Agent 可以从经验中学习。


问题与规避

技能覆盖的调试难度

问题:四层优先级下,工作区技能覆盖内置技能的行为可能让开发者困惑。

对策SkillManager 在加载时记录调试日志(debugLogger.debug),显示每个技能的来源和覆盖关系。用户可通过日志追踪同名技能的实际生效版本。

自动提取的技能质量

问题:SkillExtractionAgent 提取的技能可能过于泛化或不准确。

对策

  • 最低对话轮次阈值(10 条用户消息)过滤掉太短的对话
  • 3 小时间隔确保只提取经过充分讨论的对话模式
  • 提取的技能仍受标准技能格式约束(name + description + 指令)

工作区技能的安全风险

问题:恶意工作区可能通过技能注入不当行为。

对策:工作区技能仅在文件夹被信任时加载。未信任的文件夹跳过步骤 4,仅加载内置、扩展和用户技能。


设计取舍

四层优先级 vs 显式引用

方案优势代价
优先级覆盖用户/项目可无缝覆盖默认行为,无需修改配置覆盖是隐式的,调试困难
显式引用清晰可见哪个技能来自哪里需要用户手动配置引用关系

Gemini CLI 的选择:优先级覆盖让用户和项目可以”静默定制”Agent 行为,降低配置负担。调试信息通过日志补充。

自动提取 vs 手动创建

方案优势代价
自动提取从经验中学习,无需手动编写质量不可控,可能提取不需要的模式
手动创建精确控制技能内容和触发条件用户需要学习格式,创建成本高

Gemini CLI 的选择:两者共存。手动创建是主要方式(用户编写 SKILL.md),自动提取是辅助方式(从历史中学习新技能)。


参考来源