Skip to content

3. 自动检测机制

3.1 检测流程

┌─────────────────────────────────────────────────────────────────┐
│                    Skill Detection Pipeline                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. User Input                                                   │
│     "Deploy the app to production"                               │
│                                                                  │
│  2. Extract Intent                                               │
│     ├── Keywords: [deploy, production]                           │
│     ├── Action: deployment                                       │
│     └── Context: production environment                          │
│                                                                  │
│  3. Match Skills                                                 │
│     ├── Keyword Match                                            │
│     │   deployment-skill: triggers=[deploy, production] ✓        │
│     │   testing-skill: triggers=[test, qa] ✗                     │
│     │                                                             │
│     ├── Semantic Match                                           │
│     │   deployment-skill: similarity=0.92 ✓                      │
│     │   monitoring-skill: similarity=0.45 ✗                      │
│     │                                                             │
│     └── Context Match                                            │
│         deployment-skill: context=production ✓                   │
│                                                                  │
│  4. Rank Candidates                                              │
│     1. deployment-skill (score: 0.95)                            │
│     2. docker-skill (score: 0.72)                                │
│     3. k8s-skill (score: 0.68)                                   │
│                                                                  │
│  5. Load Top Skill                                               │
│     Load: deployment-skill                                       │
│                                                                  │
│  6. Inject into Context                                          │
│     System Prompt += deployment-skill content                    │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

3.2 实现代码

typescript
class SkillDetector {
  private skills: Map<string, SkillMetadata> = new Map();
  private embeddings: Map<string, number[]> = new Map();

  // 初始化:扫描并加载所有 skill 元数据
  async initialize(skillsDir: string) {
    const skillDirs = await fs.readdir(skillsDir);

    for (const dir of skillDirs) {
      const skillPath = path.join(skillsDir, dir, 'SKILL.md');

      if (await fs.exists(skillPath)) {
        const metadata = await this.parseSkillMetadata(skillPath);
        this.skills.set(metadata.name, metadata);

        // 预计算嵌入向量
        const embedding = await this.embed(
          `${metadata.name}: ${metadata.description}`
        );
        this.embeddings.set(metadata.name, embedding);
      }
    }

    console.log(`Loaded ${this.skills.size} skills`);
  }

  // 检测相关 skills
  async detect(userInput: string, limit = 3): Promise<Skill[]> {
    const candidates: SkillCandidate[] = [];

    for (const [name, metadata] of this.skills) {
      const score = await this.calculateScore(userInput, metadata);

      if (score > 0.5) {  // 阈值
        candidates.push({ name, metadata, score });
      }
    }

    // 排序并返回 top N
    const topSkills = candidates
      .sort((a, b) => b.score - a.score)
      .slice(0, limit);

    // 加载完整 skill 内容
    return await Promise.all(
      topSkills.map(c => this.loadSkill(c.name))
    );
  }

  // 计算匹配分数
  private async calculateScore(
    userInput: string,
    metadata: SkillMetadata
  ): Promise<number> {
    let score = 0;

    // 1. 关键词匹配 (40%)
    const keywordScore = this.keywordMatch(userInput, metadata.triggers);
    score += keywordScore * 0.4;

    // 2. 语义匹配 (40%)
    const semanticScore = await this.semanticMatch(userInput, metadata.name);
    score += semanticScore * 0.4;

    // 3. 上下文匹配 (20%)
    const contextScore = this.contextMatch(metadata);
    score += contextScore * 0.2;

    return score;
  }

  // 关键词匹配
  private keywordMatch(input: string, triggers: string[]): number {
    const inputLower = input.toLowerCase();
    const matches = triggers.filter(t =>
      inputLower.includes(t.toLowerCase())
    );

    return matches.length / triggers.length;
  }

  // 语义匹配
  private async semanticMatch(input: string, skillName: string): Promise<number> {
    const inputEmbedding = await this.embed(input);
    const skillEmbedding = this.embeddings.get(skillName)!;

    return this.cosineSimilarity(inputEmbedding, skillEmbedding);
  }

  // 上下文匹配
  private contextMatch(metadata: SkillMetadata): number {
    // 检查当前项目是否匹配 skill 的上下文
    // 例如:检查是否有 Dockerfile, k8s 配置等

    let score = 0;

    if (metadata.name === 'deployment-skill') {
      if (fs.existsSync('Dockerfile')) score += 0.5;
      if (fs.existsSync('k8s/')) score += 0.5;
    }

    return score;
  }

  // 解析 SKILL.md frontmatter
  private async parseSkillMetadata(skillPath: string): Promise<SkillMetadata> {
    const content = await fs.readFile(skillPath, 'utf-8');

    // 提取 YAML frontmatter
    const match = content.match(/^---\n([\s\S]*?)\n---/);
    if (!match) {
      throw new Error(`Invalid SKILL.md: ${skillPath}`);
    }

    const frontmatter = yaml.parse(match[1]);

    return {
      name: frontmatter.name,
      description: frontmatter.description,
      triggers: frontmatter.triggers || [],
      version: frontmatter.version,
      author: frontmatter.author,
      tags: frontmatter.tags || [],
      path: skillPath
    };
  }

  // 加载完整 skill
  private async loadSkill(name: string): Promise<Skill> {
    const metadata = this.skills.get(name)!;
    const content = await fs.readFile(metadata.path, 'utf-8');

    // 移除 frontmatter,只保留 markdown 内容
    const body = content.replace(/^---\n[\s\S]*?\n---\n/, '');

    return {
      ...metadata,
      content: body
    };
  }
}

前端面试知识库