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-skilldescription: 当用户需要 X 时激活此技能---
# My Skill
当检测到用户的意图涉及 X 时,执行以下步骤...加载解析(skillLoader.ts):
- 使用
FRONTMATTER_REGEX匹配---包裹的 YAML frontmatter parseFrontmatter提取name和description- 其余 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记录已处理的会话,避免重复提取
技能与记忆系统的联动
技能提取与记忆系统紧密协作:
- 会话历史先被
MemoryService索引 SkillExtractionAgent分析索引后的会话- 提取的技能注册到
SkillManager - 下次会话启动时,新技能可被自动发现和激活
这种”对话 → 记忆 → 技能提取 → 技能注册 → 未来对话使用”的闭环使得 Agent 可以从经验中学习。
问题与规避
技能覆盖的调试难度
问题:四层优先级下,工作区技能覆盖内置技能的行为可能让开发者困惑。
对策:SkillManager 在加载时记录调试日志(debugLogger.debug),显示每个技能的来源和覆盖关系。用户可通过日志追踪同名技能的实际生效版本。
自动提取的技能质量
问题:SkillExtractionAgent 提取的技能可能过于泛化或不准确。
对策:
- 最低对话轮次阈值(10 条用户消息)过滤掉太短的对话
- 3 小时间隔确保只提取经过充分讨论的对话模式
- 提取的技能仍受标准技能格式约束(name + description + 指令)
工作区技能的安全风险
问题:恶意工作区可能通过技能注入不当行为。
对策:工作区技能仅在文件夹被信任时加载。未信任的文件夹跳过步骤 4,仅加载内置、扩展和用户技能。
设计取舍
四层优先级 vs 显式引用
| 方案 | 优势 | 代价 |
|---|---|---|
| 优先级覆盖 | 用户/项目可无缝覆盖默认行为,无需修改配置 | 覆盖是隐式的,调试困难 |
| 显式引用 | 清晰可见哪个技能来自哪里 | 需要用户手动配置引用关系 |
Gemini CLI 的选择:优先级覆盖让用户和项目可以”静默定制”Agent 行为,降低配置负担。调试信息通过日志补充。
自动提取 vs 手动创建
| 方案 | 优势 | 代价 |
|---|---|---|
| 自动提取 | 从经验中学习,无需手动编写 | 质量不可控,可能提取不需要的模式 |
| 手动创建 | 精确控制技能内容和触发条件 | 用户需要学习格式,创建成本高 |
Gemini CLI 的选择:两者共存。手动创建是主要方式(用户编写 SKILL.md),自动提取是辅助方式(从历史中学习新技能)。