Skills系统:可扩展AI能力设计
概述
Skills系统是AI-Native架构中的重要组件,它允许通过声明式配置扩展AI的能力。本文将介绍Skills系统的设计与实现,让大模型能够像人类专家一样具备特定领域的能力。
什么是Skills系统
概念
Skills(技能)是一种声明式的AI能力扩展机制,类似于人类的"专业技能":
通用AI助手 专业AI助手(带Skills)
┌──────────────────────┐ ┌──────────────────────────────┐
│ │ │ │
│ 用户:请帮我写代码 │ │ 用户:请帮我审查这段代码 │
│ │ │ │
│ AI:我是一个AI助手 │ │ AI:[激活代码审查Skill] │
│ 可以帮你生成代码... │ → │ │
│ │ │ 1. 代码质量分析 │
│ │ │ 2. 潜在Bug识别 │
│ │ │ 3. 性能优化建议 │
│ │ │ 4. 安全漏洞检查 │
│ │ │ 5. 最佳实践推荐 │
│ │ │ │
└──────────────────────┘ └──────────────────────────────┘
特点: 特点:
• 通用回答,泛泛而谈 • 基于专业知识深入分析
• 缺乏领域深度 • 结构化输出,专业建议
• 难以保证质量 • 可复用的专业能力
与Function Calling的区别
| 特性 | Skills | Function Calling |
|---|---|---|
| 目的 | 改变AI的"思维"方式 | 扩展AI的"工具"能力 |
| 实现 | 系统提示词注入 | API调用 |
| 持久性 | 会话级别 | 单次调用 |
| 复杂度 | 简单(配置) | 复杂(开发) |
| 灵活性 | 高(声明式) | 低(编程式) |
系统设计
架构

SKILL.md规范
文件格式
# [技能名称] ([技能代码])
## 描述
技能的简要描述,说明这个Skill的用途和能力。
## 能力
- 能力1:详细描述
- 能力2:详细描述
- 能力3:详细描述
## 适用场景
- 场景1:描述
- 场景2:描述
## 系统提示
你是一位[专业角色]。你的任务是[任务描述]。
请遵循以下原则:
- 原则1
- 原则2
- 原则3
输出格式:
- 使用Markdown格式
- 结构化输出
- 包含具体示例
[其他特定指令...]
## 参数
| 参数名 | 类型 | 描述 | 默认值 |
|--------|------|------|--------|
| param1 | string | 参数1描述 | "default" |
| param2 | number | 参数2描述 | 100 |
## 示例
### 示例1:场景描述
输入:...
输出:...
### 示例2:场景描述
输入:...
输出:...
## 元数据
- 版本: 1.0.0
- 作者: xxx
- 更新日期: 2024-01-01
- 标签: tag1, tag2, tag3
代码审查Skill示例
# 代码审查专家 (code-reviewer)
## 描述
专业的代码审查专家,擅长发现代码中的潜在问题、性能瓶颈和安全漏洞,并提供改进建议。
## 能力
- 代码质量分析:评估代码可读性、可维护性、命名规范
- Bug检测:识别潜在的逻辑错误、边界条件问题
- 性能优化:发现性能瓶颈,提供优化建议
- 安全检查:识别安全漏洞、注入风险、敏感信息泄露
- 最佳实践:推荐设计模式、架构优化
## 适用场景
- Code Review流程
- 代码提交前的自检
- 学习和改进代码质量
- 团队协作规范
## 系统提示
你是一位资深代码审查专家,拥有10年以上软件开发经验。请对提供的代码进行全面审查。
审查维度:
-
代码质量
- 命名规范(变量、函数、类)
- 代码可读性
- 注释完整性
- 代码复杂度
-
潜在问题
- 逻辑错误
- 边界条件处理
- 异常处理
- 资源泄漏
-
性能优化
- 算法复杂度
- 内存使用
- 不必要的计算
- 缓存机会
-
安全漏洞
- 注入攻击风险
- XSS漏洞
- 敏感信息泄露
- 权限控制
-
最佳实践
- 设计模式应用
- SOLID原则
- DRY原则
- 测试覆盖
输出格式:
使用Markdown格式,按以下结构输出:
总体评价
简要评价代码质量等级(优秀/良好/一般/需改进)
详细审查
[文件名]
🔴 严重问题
- 问题描述
- 建议修改
- 参考代码
🟡 警告
- 问题描述
- 改进建议
🟢 建议
- 改进点
- 原因说明
改进后的代码
提供优化后的完整代码
学习资源
推荐相关的学习资料和最佳实践链接
## 参数
| 参数名 | 类型 | 描述 | 默认值 |
|--------|------|------|--------|
| language | string | 编程语言 | "auto" |
| strictness | string | 严格程度(strict/normal) | "normal" |
| focus | array | 重点审查维度 | ["quality", "security"] |
## 示例
### 示例1:JavaScript代码审查
输入:
```javascript
function processData(data) {
var result = [];
for (var i = 0; i < data.length; i++) {
if (data[i].status == 'active') {
result.push(data[i]);
}
}
return result;
}
输出:
总体评价
良好,代码逻辑清晰,但存在一些可以改进的地方。
详细审查
🔴 严重问题
- 使用var声明变量
- 问题:var存在变量提升和作用域问题
- 建议:使用const或let
🟡 警告
- 使用==比较
- 问题:==会进行类型转换,可能导致意外结果
- 建议:使用===进行严格比较
改进后的代码
function processData(data) {
const result = [];
for (let i = 0; i < data.length; i++) {
if (data[i].status === 'active') {
result.push(data[i]);
}
}
return result;
}
// 或使用更现代的写法
const processData = (data) => data.filter(item => item.status === 'active');
元数据
- 版本: 1.0.0
- 作者: AI Team
- 更新日期: 2024-01-01
- 标签: code-review, quality, security
## 核心实现
### Skill模型
```javascript
// skill/types.js
// Skill数据结构
export interface Skill {
code: string // 技能代码
name: string // 技能名称
description: string // 描述
capabilities: string[] // 能力列表
systemPrompt: string // 系统提示词
parameters: SkillParameter[] // 参数定义
examples: SkillExample[] // 示例
metadata: SkillMetadata // 元数据
}
// 参数定义
export interface SkillParameter {
name: string
type: 'string' | 'number' | 'boolean' | 'array' | 'object'
description: string
required: boolean
default?: any
}
// 示例
export interface SkillExample {
title: string
input: string
output: string
}
// 元数据
export interface SkillMetadata {
version: string
author: string
updatedAt: string
tags: string[]
}
// Skill配置(用户启用时)
export interface SkillConfig {
skillCode: string
enabled: boolean
priority: number // 优先级(多个Skill时)
parameters: Record<string, any> // 参数值
contextWindow?: number // 上下文窗口大小
}
Skill解析器
// skill/skillParser.js
import matter from 'gray-matter'
import MarkdownIt from 'markdown-it'
export class SkillParser {
private md: MarkdownIt
constructor() {
this.md = new MarkdownIt()
}
// 解析SKILL.md文件
parse(content: string): Skill {
// 解析Frontmatter
const { data, content: markdownContent } = matter(content)
// 提取标题(技能代码)
const titleMatch = markdownContent.match(/^# (.+?) \((.+?)\)/m)
if (!titleMatch) {
throw new Error('Invalid SKILL.md format: missing title')
}
const name = titleMatch[1].trim()
const code = titleMatch[2].trim()
// 解析各部分
const sections = this.parseSections(markdownContent)
// 提取系统提示
const systemPrompt = this.extractSystemPrompt(sections)
// 提取能力列表
const capabilities = this.parseCapabilities(sections['能力'] || '')
// 提取参数
const parameters = this.parseParameters(sections['参数'] || '')
// 提取示例
const examples = this.parseExamples(sections['示例'] || '')
return {
code,
name,
description: sections['描述']?.trim() || '',
capabilities,
systemPrompt,
parameters,
examples,
metadata: {
version: data.version || '1.0.0',
author: data.author || 'unknown',
updatedAt: data.updatedAt || new Date().toISOString(),
tags: data.tags || [],
},
}
}
// 解析Markdown章节
private parseSections(content: string): Record<string, string> {
const sections: Record<string, string> = {}
const regex = /^## (.+?)\n(.*?)(?=^## |\Z)/gms
let match
while ((match = regex.exec(content)) !== null) {
const title = match[1].trim()
const sectionContent = match[2].trim()
sections[title] = sectionContent
}
return sections
}
// 提取系统提示
private extractSystemPrompt(sections: Record<string, string>): string {
const systemPromptSection = sections['系统提示']
if (!systemPromptSection) return ''
// 提取代码块内容
const codeBlockMatch = systemPromptSection.match(/```[\s\S]*?\n([\s\S]*?)```/)
if (codeBlockMatch) {
return codeBlockMatch[1].trim()
}
return systemPromptSection.trim()
}
// 解析能力列表
private parseCapabilities(content: string): string[] {
const capabilities: string[] = []
const lines = content.split('\n')
for (const line of lines) {
const match = line.match(/^- (.+?)(?::|$)/)
if (match) {
capabilities.push(match[1].trim())
}
}
return capabilities
}
// 解析参数表格
private parseParameters(content: string): SkillParameter[] {
const parameters: SkillParameter[] = []
// 简单解析Markdown表格
const lines = content.split('\n')
let inTable = false
for (const line of lines) {
if (line.startsWith('| 参数名')) {
inTable = true
continue
}
if (inTable && line.startsWith('|')) {
const cells = line.split('|').map(c => c.trim()).filter(Boolean)
if (cells.length >= 4 && cells[0] !== '参数名') {
parameters.push({
name: cells[0],
type: cells[1] as any,
description: cells[2],
required: !cells[3] || cells[3] === '-',
default: cells[3] !== '-' ? cells[3] : undefined,
})
}
}
}
return parameters
}
// 解析示例
private parseExamples(content: string): SkillExample[] {
const examples: SkillExample[] = []
const sections = content.split('###')
for (const section of sections.slice(1)) {
const lines = section.trim().split('\n')
const title = lines[0].trim()
// 提取输入和输出
const inputMatch = section.match(/输入:\s*```[\s\S]*?\n([\s\S]*?)```/)
const outputMatch = section.match(/输出:\s*```[\s\S]*?\n([\s\S]*?)```/)
examples.push({
title,
input: inputMatch ? inputMatch[1].trim() : '',
output: outputMatch ? outputMatch[1].trim() : '',
})
}
return examples
}
}
Skill管理器
// skill/skillManager.js
import { SkillParser } from './skillParser.js'
import fs from 'fs/promises'
import path from 'path'
export class SkillManager {
private skills: Map<string, Skill> = new Map()
private configs: Map<string, SkillConfig> = new Map()
private parser: SkillParser
private skillsDir: string
constructor(skillsDir: string) {
this.skillsDir = skillsDir
this.parser = new SkillParser()
}
// 加载所有Skills
async loadSkills(): Promise<void> {
const skillDirs = await fs.readdir(this.skillsDir)
for (const dir of skillDirs) {
const skillPath = path.join(this.skillsDir, dir, 'SKILL.md')
try {
const content = await fs.readFile(skillPath, 'utf-8')
const skill = this.parser.parse(content)
this.skills.set(skill.code, skill)
console.log(`Loaded skill: ${skill.code} - ${skill.name}`)
} catch (error) {
console.warn(`Failed to load skill from ${skillPath}:`, error.message)
}
}
}
// 获取Skill
getSkill(code: string): Skill | undefined {
return this.skills.get(code)
}
// 获取所有Skills
getAllSkills(): Skill[] {
return Array.from(this.skills.values())
}
// 配置Skill(用户启用)
configureSkill(config: SkillConfig): void {
// 验证Skill存在
if (!this.skills.has(config.skillCode)) {
throw new Error(`Skill ${config.skillCode} not found`)
}
// 验证参数
const skill = this.skills.get(config.skillCode)
if (skill) {
this.validateParameters(skill, config.parameters)
}
this.configs.set(config.skillCode, config)
}
// 获取频道的Skills配置
getChannelSkills(channelCode: string): SkillConfig[] {
// 从频道配置中获取启用的Skills
// 实际实现需查询数据库
return Array.from(this.configs.values())
.filter(config => config.enabled)
.sort((a, b) => b.priority - a.priority)
}
// 构建系统提示词
buildSystemPrompt(skillCodes: string[]): string {
const prompts: string[] = []
for (const code of skillCodes) {
const skill = this.skills.get(code)
if (!skill) continue
const config = this.configs.get(code)
// 替换参数
let prompt = skill.systemPrompt
if (config?.parameters) {
for (const [key, value] of Object.entries(config.parameters)) {
prompt = prompt.replace(new RegExp(`{${key}}`, 'g'), String(value))
}
}
prompts.push(`[${skill.name}]\n${prompt}`)
}
return prompts.join('\n\n---\n\n')
}
// 验证参数
private validateParameters(skill: Skill, parameters: Record<string, any>): void {
for (const param of skill.parameters) {
if (param.required && !(param.name in parameters)) {
throw new Error(`Required parameter ${param.name} not provided`)
}
if (param.name in parameters) {
const value = parameters[param.name]
if (!this.validateType(value, param.type)) {
throw new Error(`Parameter ${param.name} should be of type ${param.type}`)
}
}
}
}
// 验证类型
private validateType(value: any, type: string): boolean {
switch (type) {
case 'string': return typeof value === 'string'
case 'number': return typeof value === 'number'
case 'boolean': return typeof value === 'boolean'
case 'array': return Array.isArray(value)
case 'object': return typeof value === 'object' && !Array.isArray(value)
default: return true
}
}
}
SkillChat集成
// skill/skillChat.js
import { OpenAI } from 'openai'
export class SkillChat {
private openai: OpenAI
private skillManager: SkillManager
constructor(openai: OpenAI, skillManager: SkillManager) {
this.openai = openai
this.skillManager = skillManager
}
// 带Skills的聊天
async chat(
channelCode: string,
message: string,
history: ChatMessage[] = []
): Promise<SkillChatResponse> {
// 1. 获取频道启用的Skills
const skillConfigs = this.skillManager.getChannelSkills(channelCode)
const skillCodes = skillConfigs.map(c => c.skillCode)
// 2. 构建系统提示词
let systemPrompt = '你是一位 helpful assistant.'
if (skillCodes.length > 0) {
const skillPrompt = this.skillManager.buildSystemPrompt(skillCodes)
systemPrompt += '\n\n' + skillPrompt
}
// 3. 构建消息
const messages: ChatMessage[] = [
{ role: 'system', content: systemPrompt },
...history,
{ role: 'user', content: message },
]
// 4. 选择模型参数
const modelConfig = this.selectModelConfig(skillConfigs)
// 5. 调用LLM
const response = await this.openai.chat.completions.create({
model: modelConfig.model,
messages,
temperature: modelConfig.temperature,
max_tokens: modelConfig.maxTokens,
stream: false,
})
// 6. 返回结果
return {
content: response.choices[0].message.content,
usedSkills: skillCodes,
tokens: {
prompt: response.usage?.prompt_tokens || 0,
completion: response.usage?.completion_tokens || 0,
total: response.usage?.total_tokens || 0,
},
}
}
// 流式聊天
async *streamChat(
channelCode: string,
message: string,
history: ChatMessage[] = []
): AsyncGenerator<StreamChunk, void, unknown> {
// 类似chat,但使用stream模式
const skillConfigs = this.skillManager.getChannelSkills(channelCode)
const skillCodes = skillConfigs.map(c => c.skillCode)
let systemPrompt = '你是一位 helpful assistant.'
if (skillCodes.length > 0) {
const skillPrompt = this.skillManager.buildSystemPrompt(skillCodes)
systemPrompt += '\n\n' + skillPrompt
}
const messages: ChatMessage[] = [
{ role: 'system', content: systemPrompt },
...history,
{ role: 'user', content: message },
]
const modelConfig = this.selectModelConfig(skillConfigs)
const stream = await this.openai.chat.completions.create({
model: modelConfig.model,
messages,
temperature: modelConfig.temperature,
max_tokens: modelConfig.maxTokens,
stream: true,
})
let fullContent = ''
for await (const chunk of stream) {
const content = chunk.choices[0]?.delta?.content || ''
fullContent += content
yield {
content,
fullContent,
finished: false,
}
}
yield {
content: '',
fullContent,
finished: true,
usedSkills: skillCodes,
}
}
// 根据Skills选择模型配置
private selectModelConfig(skillConfigs: SkillConfig[]): ModelConfig {
// 如果有代码审查等复杂Skill,使用更强的模型
const hasComplexSkill = skillConfigs.some(c =>
['code-reviewer', 'refactoring-expert'].includes(c.skillCode)
)
if (hasComplexSkill) {
return {
model: 'gpt-4',
temperature: 0.3, // 代码任务使用较低温度
maxTokens: 4096,
}
}
return {
model: 'gpt-4',
temperature: 0.7,
maxTokens: 2048,
}
}
}
使用示例
配置频道Skills
// 配置频道的Skills
const skillConfigs = [
{
skillCode: 'code-reviewer',
enabled: true,
priority: 100,
parameters: {
language: 'javascript',
strictness: 'strict',
focus: ['quality', 'security', 'performance'],
},
},
{
skillCode: 'document-extractor',
enabled: true,
priority: 50,
parameters: {
format: 'markdown',
includeMetadata: true,
},
},
]
// 保存到频道配置
await channelService.updateChannelSkills('my-channel', skillConfigs)
使用Skill聊天
// 初始化
const skillManager = new SkillManager('./skill/skills')
await skillManager.loadSkills()
const skillChat = new SkillChat(openai, skillManager)
// 普通对话(无Skill)
const response1 = await skillChat.chat('general-channel', '你好!')
// 代码审查对话(启用code-reviewer Skill)
const code = `
function add(a, b) {
return a + b
}
`
const response2 = await skillChat.chat('code-channel',
`请审查以下代码:\n\n${code}`,
[]
)
console.log(response2.content)
// 输出详细的代码审查报告
总结
Skills系统的设计要点:
- 声明式定义:SKILL.md文件声明能力
- 解析器:自动解析Markdown格式的Skill定义
- 管理器:统一管理Skills的加载、配置、激活
- 集成聊天:Skill系统与LLM聊天无缝集成
- 参数化:支持参数配置,灵活适配场景
- 优先级:多Skill时按优先级组合提示词
- 热更新:支持动态加载新的Skills
Skills系统让AI助手具备了专业领域能力,是AI-Native架构的重要组成部分。