Chuyển đến nội dung chính

レッスン 14: スキル システム — 動的なツール構成

スキル システムの構築:defineSkill()、SkillSelector、セッションごとのアクティブ化。 RL ベースのランキング、getRankedTools()、コンテキスト認識ツールの選択。スキルとツールの区別。

🧠 AI と ML — レッスン 13 レッスン 14: スキル システム — ダイナミック ツール 構成

AIエージェントプラットフォームをゼロから構築 — xClawとの実戦

パート 5: スキル、ドメイン、プラグイン

xdev.asia

はじめに

ツール レジストリは個々のツールを管理します。しかし実際には、ツールはグループで提供されることが多く、「Web リサーチ」のニーズに応えます。 web_search + url_reader + summarize。スキル システムは、コンテキスト、アクティブ化ルール、ランキングを使用してツールを スキル にグループ化します。


1. スキルとツール

側面ツールスキル
範囲単機能関連ツール群
コンテキスト無国籍システム プロンプトとコンテキストがある
アクティベーションいつでも利用可能セッション/ドメインごとにアクティブ化
ランキング等しい優先度RL ベースのランキング
例web_search「Webリサーチ」スキル(探す+読む+まとめる)

2. スキルの定義

// packages/core/src/skills/skill-manager.ts
export interface SkillDefinition {
  id: string;
  name: string;
  description: string;
  version: string;
  tools: AdditionalTool[];           // Tools provided by this skill
  systemPromptAddition?: string;     // Injected into agent's system prompt
  activationKeywords?: string[];     // Auto-activate when user mentions these
  requiredPermissions?: string[];    // RBAC permissions needed
  config?: Record<string, unknown>;  // Skill-specific config
}

export function defineSkill(def: SkillDefinition): SkillDefinition {
  // Validate skill definition
  if (!def.id || !def.name) throw new Error('Skill must have id and name');
  if (!def.tools || def.tools.length === 0) throw new Error('Skill must have at least one tool');

  return Object.freeze(def);
}

例: ウェブリサーチスキル

const webResearchSkill = defineSkill({
  id: 'web-research',
  name: 'Web Research',
  description: 'Search the web and analyze content',
  version: '1.0.0',
  systemPromptAddition: 'You can search the web and read URLs to find information.',
  activationKeywords: ['search', 'find', 'research', 'look up'],
  tools: [
    {
      definition: {
        name: 'web_search',
        description: 'Search the web',
        parameters: {
          type: 'object',
          properties: {
            query: { type: 'string', description: 'Search query' },
          },
          required: ['query'],
        },
      },
      handler: webSearchHandler,
    },
    {
      definition: {
        name: 'read_url',
        description: 'Read and extract content from a URL',
        parameters: {
          type: 'object',
          properties: {
            url: { type: 'string', description: 'URL to read' },
          },
          required: ['url'],
        },
      },
      handler: readUrlHandler,
    },
  ],
});

3. スキルマネージャー

export class SkillManager {
  private skills = new Map<string, SkillDefinition>();
  private activeSkills = new Map<string, Set<string>>(); // sessionId → skill IDs
  private selector?: SkillSelector;

  register(skill: SkillDefinition) {
    this.skills.set(skill.id, skill);
  }

  activate(sessionId: string, skillId: string) {
    if (!this.activeSkills.has(sessionId)) {
      this.activeSkills.set(sessionId, new Set());
    }
    this.activeSkills.get(sessionId)!.add(skillId);
  }

  deactivate(sessionId: string, skillId: string) {
    this.activeSkills.get(sessionId)?.delete(skillId);
  }

  // Get all tools from active skills for a session
  getActiveTools(sessionId: string): AdditionalTool[] {
    const activeIds = this.activeSkills.get(sessionId);
    if (!activeIds) return [];

    const tools: AdditionalTool[] = [];
    for (const id of activeIds) {
      const skill = this.skills.get(id);
      if (skill) tools.push(...skill.tools);
    }
    return tools;
  }

  // Get system prompt additions from active skills
  getSystemPromptAdditions(sessionId: string): string {
    const activeIds = this.activeSkills.get(sessionId);
    if (!activeIds) return '';

    const additions: string[] = [];
    for (const id of activeIds) {
      const skill = this.skills.get(id);
      if (skill?.systemPromptAddition) {
        additions.push(skill.systemPromptAddition);
      }
    }
    return additions.join('\n\n');
  }
}

4. RL ベースのスキルランキング

export interface SkillSelector {
  rank(skills: SkillDefinition[], context: string): SkillDefinition[];
  recordFeedback(skillId: string, score: number): void;
}

// Reinforcement Learning selector
export class RLSkillSelector implements SkillSelector {
  private scores = new Map<string, { total: number; count: number }>();

  rank(skills: SkillDefinition[], userMessage: string): SkillDefinition[] {
    return skills
      .map(skill => ({
        skill,
        score: this.calculateScore(skill, userMessage),
      }))
      .sort((a, b) => b.score - a.score)
      .map(s => s.skill);
  }

  private calculateScore(skill: SkillDefinition, message: string): number {
    let score = 0;

    // Historical performance
    const history = this.scores.get(skill.id);
    if (history) {
      score += (history.total / history.count) * 0.6;
    }

    // Keyword match
    if (skill.activationKeywords) {
      const messageLower = message.toLowerCase();
      const matches = skill.activationKeywords.filter(k =>
        messageLower.includes(k.toLowerCase())
      );
      score += (matches.length / skill.activationKeywords.length) * 0.4;
    }

    return score;
  }

  recordFeedback(skillId: string, score: number) {
    const current = this.scores.get(skillId) || { total: 0, count: 0 };
    current.total += score;
    current.count += 1;
    this.scores.set(skillId, current);
  }
}

5. まとめ

  • スキル グループ関連ツール + コンテキスト + アクティベーション ルール
  • SkillManager はライフサイクルを管理します: セッションごとに登録、アクティブ化、非アクティブ化
  • RL ランキング はフィードバックから学習し、適切なスキルに優先順位を付けます
  • 統合 — エージェントはアクティブなスキルからツールとシステム プロンプトを受け取ります

次の記事: ドメイン パック — 業界向けの事前構築済みスキル バンドル。