Skills系统:可扩展AI能力设计

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调用
持久性 会话级别 单次调用
复杂度 简单(配置) 复杂(开发)
灵活性 高(声明式) 低(编程式)

系统设计

架构

Skills系统架构

SKILL.md规范

文件格式

# [技能名称] ([技能代码])

## 描述
技能的简要描述,说明这个Skill的用途和能力。

## 能力
- 能力1:详细描述
- 能力2:详细描述
- 能力3:详细描述

## 适用场景
- 场景1:描述
- 场景2:描述

## 系统提示

你是一位[专业角色]。你的任务是[任务描述]。

请遵循以下原则:

  1. 原则1
  2. 原则2
  3. 原则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年以上软件开发经验。请对提供的代码进行全面审查。

审查维度:

  1. 代码质量

    • 命名规范(变量、函数、类)
    • 代码可读性
    • 注释完整性
    • 代码复杂度
  2. 潜在问题

    • 逻辑错误
    • 边界条件处理
    • 异常处理
    • 资源泄漏
  3. 性能优化

    • 算法复杂度
    • 内存使用
    • 不必要的计算
    • 缓存机会
  4. 安全漏洞

    • 注入攻击风险
    • XSS漏洞
    • 敏感信息泄露
    • 权限控制
  5. 最佳实践

    • 设计模式应用
    • 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;
}

输出:

总体评价

良好,代码逻辑清晰,但存在一些可以改进的地方。

详细审查

🔴 严重问题

  1. 使用var声明变量
    • 问题:var存在变量提升和作用域问题
    • 建议:使用const或let

🟡 警告

  1. 使用==比较
    • 问题:==会进行类型转换,可能导致意外结果
    • 建议:使用===进行严格比较

改进后的代码

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系统的设计要点:

  1. 声明式定义:SKILL.md文件声明能力
  2. 解析器:自动解析Markdown格式的Skill定义
  3. 管理器:统一管理Skills的加载、配置、激活
  4. 集成聊天:Skill系统与LLM聊天无缝集成
  5. 参数化:支持参数配置,灵活适配场景
  6. 优先级:多Skill时按优先级组合提示词
  7. 热更新:支持动态加载新的Skills

Skills系统让AI助手具备了专业领域能力,是AI-Native架构的重要组成部分。

阅读更多

插件化架构设计模式

插件化架构设计模式

概述 插件化架构是一种将核心功能与扩展功能分离的设计模式,允许系统在运行时动态加载和卸载功能模块。本文将介绍如何在微服务平台中设计和实现插件化架构。 为什么需要插件化 插件化优势 1. 模块化:功能独立,边界清晰 2. 可扩展:按需加载,动态增删 3. 隔离性:插件间互不干扰 4. 可维护:独立开发、测试、部署 5. 可定制:用户按需选择功能 核心设计 架构概览 核心组件实现 1. 插件接口定义 // core/plugin.interface.ts // 插件接口 export interface IPlugin { // 插件名称 readonly name: string // 插件版本 readonly version: string // 插件配置 getConfig(): PluginConfig // 插件清单

By 菱角
gRPC服务通信设计与实践

gRPC服务通信设计与实践

概述 在微服务架构中,服务间通信是关键环节。相比REST API,gRPC提供了更高的性能和更强的类型安全。本文将介绍如何在微服务平台中设计和实现gRPC服务通信。 为什么选择gRPC gRPC vs REST对比 特性 gRPC REST 协议 HTTP/2 HTTP/1.1 序列化 Protocol Buffers (二进制) JSON (文本) 性能 高(二进制+压缩) 中(文本开销) 类型安全 强(代码生成) 弱(运行时检查) 流式通信 原生支持(双向流) 需额外实现(SSE/WebSocket) 代码生成 自动生成 手动编写 浏览器支持 需gRPC-Web 原生支持 调试难度

By 菱角
多语言微服务架构:Node.js与Python协作

多语言微服务架构:Node.js与Python协作

概述 在微服务架构中,根据场景选择最适合的编程语言是最佳实践。本文将介绍如何在微服务平台中实现Node.js与Python的协作,发挥各自技术优势。 技术选型策略 为什么混合使用 服务划分 Node.js服务(7个) 服务 功能 选择Node.js的原因 llm.api 大模型服务 高并发SSE流式响应 ucenter.api 用户中心 RESTful API标准实践 doc.api 文件服务 流式上传下载处理 resource.api 资源管理 gRPC高性能通信 rag.api 知识库服务 MongoDB集成便利 statistic.api 统计分析 事件驱动架构 pptonline.api PPT服务 与前端技术栈统一 Python服务(1个) 服务 功能 选择Python的原因

By 菱角
CI/CD流水线:Jenkins + APISIX微服务部署实践

CI/CD流水线:Jenkins + APISIX微服务部署实践

概述 微服务架构的CI/CD流水线设计是DevOps实践的核心。本文将介绍如何使用Jenkins Pipeline、Docker和APISIX构建完整的微服务持续集成与持续部署系统。 架构概览 CI/CD流程 Jenkins Pipeline设计 流水线流程 ┌─────────────────────────────────────────────────────────────────────────────┐ │ Jenkins Pipeline 标准流程 │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────┐ ┌─────────

By 菱角