エージェント のスキル

エージェント スキル は、エージェントに特化した機能とドメインの専門知識を提供する、命令、スクリプト、リソースの移植可能なパッケージです。 スキルはオープン仕様に従い、段階的な開示パターンを実装して、エージェントが必要なときに必要なコンテキストのみを読み込みます。

次の状況では、エージェント スキルを使用します。

  • パッケージ ドメインの専門知識 - 再利用可能なポータブル パッケージとして、専門的な知識 (経費ポリシー、法的ワークフロー、データ分析パイプライン) をキャプチャします。
  • エージェントの機能を拡張する - コア命令を変更することなく、エージェントに新しい機能を提供します。
  • 一貫性を確保 する - 複数ステップのタスクを反復可能で監査可能なワークフローに変換します。
  • 相互運用性を有効にする - 異なるエージェント スキル互換製品間で同じスキルを再利用します。

スキルの構造

スキルは、SKILL.md ファイルや、リソースのためのオプションのサブディレクトリを含むディレクトリです。

expense-report/
├── SKILL.md                          # Required - frontmatter + instructions
├── scripts/
│   └── validate.py                   # Executable code agents can run
├── references/
│   └── POLICY_FAQ.md                 # Reference documents loaded on demand
└── assets/
    └── expense-report-template.md    # Templates and static resources

SKILL.md 形式

SKILL.md ファイルには、YAML frontmatter とそれに続くマークダウン コンテンツが含まれている必要があります。

---
name: expense-report
description: File and validate employee expense reports according to company policy. Use when asked about expense submissions, reimbursement rules, or spending limits.
license: Apache-2.0
compatibility: Requires python3
metadata:
  author: contoso-finance
  version: "2.1"
---
Field 必須 [説明]
name イエス 最大 64 文字。 小文字、数字、ハイフンのみ。 先頭または末尾にハイフンを使用したり、連続するハイフンを含めたりすることはできません。 親ディレクトリ名と一致する必要があります。
description イエス スキルが実行する内容と、それを使用するタイミング。 最大 1024 文字。 エージェントが関連するタスクを識別するのに役立つキーワードを含める必要があります。
license いいえ バンドルされたライセンス ファイルへのライセンス名または参照。
compatibility いいえ 最大 500 文字。 環境要件 (目的の製品、システム パッケージ、ネットワーク アクセスなど) を示します。
metadata いいえ 追加のメタデータに対するキーと値の任意のマッピング。
allowed-tools いいえ スキルが使用する可能性がある、事前に承認されたツールのスペースで区切られた一覧。 試験段階 - サポートは、エージェントの実装によって異なる場合があります。

フロントマッターの後のマークダウン本文には、スキル命令 (ステップ バイ ステップ ガイダンス、入力と出力の例、一般的なエッジ ケース、エージェントがタスクを実行するのに役立つコンテンツ) が含まれています。 SKILL.md 500 行以下にし、詳細な参照マテリアルを別のファイルに移動します。

段階的な開示

エージェント スキルは、コンテキストの使用を最小限に抑えるために、4 段階のプログレッシブ開示パターンを使用します。

  1. アドバタイズ (スキルあたり最大 100 トークン) - スキル名と説明は、各実行の開始時にシステム プロンプトに挿入されるため、エージェントは使用可能なスキルを認識します。
  2. 読み込み (< 5,000 トークンを推奨) - タスクがスキルのドメインと一致すると、エージェントは load_skill ツールを呼び出して、詳細な手順で完全な SKILL.md 本文を取得します。
  3. リソースの読み取り (必要に応じて) - エージェントは read_skill_resource ツールを呼び出して、必要な場合にのみ補助ファイル (参照、テンプレート、資産) をフェッチします。
  4. スクリプトの実行 (必要に応じて) - エージェントは run_skill_script ツールを呼び出して、スキルにバンドルされたスクリプトを実行します。

必要に応じてドメインの深い知識にアクセスできるようにしつつ、このパターンはエージェントのコンテキスト ウィンドウを簡素に保ちます。

load_skill は常にアドバタイズされます。 read_skill_resource は、少なくとも 1 つのスキルにリソースがある場合にのみアドバタイズされます。 run_skill_script は、少なくとも 1 つのスキルにスクリプトがある場合にのみアドバタイズされます。

エージェントにスキルを提供する

スキルの操作には、次の 3 つの構成要素が含まれます。

  • プロバイダー - AgentSkillsProvider(C#) または SkillsProvider (Python) は、エージェントにスキルを公開するコンテキスト プロバイダーです。 システム プロンプトで使用可能なスキルをアドバタイズし、エージェントがスキルの読み込み、リソースの読み取り、スクリプトの実行に使用するツールを登録します。
  • ソース - ソースは、プロバイダーにスキルを提供します。 スキルは、いくつかのソースの種類から取得できます。
    • ファイル ベース - ファイル システム ディレクトリ内の SKILL.md ファイルから検出されたスキル。
    • コード定義 - AgentInlineSkill (C#) またはInlineSkill (Python) を使用してコード内でインラインで定義されたスキル。
    • クラス ベース - AgentClassSkill<T> (C#) またはClassSkill (Python) から派生するクラスにカプセル化されたスキル。
    • UseMcpSkills (C#) または MCPSkillsSource (Python) を介して MCP (モデル コンテキスト プロトコル) サーバーから検出されたスキル。
  • ビルダー - AgentSkillsProviderBuilder (C#) 複数のソースを 1 つのプロバイダーにアセンブルし、集計、重複除去、キャッシュ、およびオプションのフィルター処理を適用します。 Pythonでは、AggregatingSkillsSourceFilteringSkillsSourceDeduplicatingSkillsSourceなどのソース クラスを直接作成します。

次のセクションでは、各ソースの種類のスキルを作成する方法と、ソースを結合してそこからプロバイダーを構築する方法について説明します。

ファイル ベースのスキル

スキルを含むディレクトリを指す AgentSkillsProvider を作成し、エージェントのコンテキスト プロバイダーに追加します。 スクリプト ランナーを渡して、スキル ディレクトリにあるファイル ベースのスクリプトの実行を有効にします。

using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Agents.AI;
using OpenAI.Responses;

string endpoint = Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT")!;
string deploymentName = Environment.GetEnvironmentVariable("AZURE_OPENAI_DEPLOYMENT_NAME") ?? "gpt-4o-mini";

// Discover skills from the 'skills' directory
var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"));

// Create an agent with the skills provider
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetResponsesClient()
    .AsAIAgent(new ChatClientAgentOptions
    {
        Name = "SkillsAgent",
        ChatOptions = new()
        {
            Instructions = "You are a helpful assistant.",
        },
        AIContextProviders = [skillsProvider],
    },
    model: deploymentName);

Warnung

DefaultAzureCredential は開発には便利ですが、運用環境では慎重に考慮する必要があります。 運用環境では、待機時間の問題、意図しない資格情報のプローブ、フォールバック メカニズムによる潜在的なセキュリティ リスクを回避するために、特定の資格情報 ( ManagedIdentityCredential など) を使用することを検討してください。

複数のスキル ディレクトリ

プロバイダーを単一の親ディレクトリにポイントできます。 SKILL.md を含む各サブディレクトリは、スキルとして自動的に検出されます。

var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "all-skills"));

または、パスの一覧を渡して、複数のルート ディレクトリを検索します。

var skillsProvider = new AgentSkillsProvider(
    [
        Path.Combine(AppContext.BaseDirectory, "company-skills"),
        Path.Combine(AppContext.BaseDirectory, "team-skills"),
    ]);

プロバイダーは、最大 2 つのレベルの深さまで検索します。

リソースとスクリプトの検出のカスタマイズ

既定では、プロバイダーは、拡張機能 .md.json.yaml.yml.csv.xml、および .txt と、拡張機能の .py.js.sh.ps1.cs、および .csxを持つリソースを認識します。 各スキル ディレクトリ内で最大 2 つのレベルを検索します。 AgentFileSkillsSourceOptionsを使用して、次の既定値を変更します。

var fileOptions = new AgentFileSkillsSourceOptions
{
    AllowedResourceExtensions = [".md", ".txt"],
    AllowedScriptExtensions = [".py"],
    SearchDepth = 3, // Search up to 3 levels deep (default is 2)
    ResourceFilter = context => context.RelativeFilePath.StartsWith("references/"),
    ScriptFilter = context => context.RelativeFilePath.StartsWith("scripts/")
                           || context.RelativeFilePath.StartsWith("tools/"),
};

// Via constructor
var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    fileOptions: fileOptions);

// Via builder
var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"), options: fileOptions)
    .Build();

ResourceFilter ScriptFilter、スキル名とファイルの相対パスを持つAgentFileSkillFilterContextを受け取り、場所、名前付け規則、またはカスタム ロジックによってファイルを制限できます。

スクリプトの実行

SubprocessScriptRunner.RunAsyncをスクリプト ランナーとして渡して、ファイル ベースのスクリプトの実行を有効にします。

var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    SubprocessScriptRunner.RunAsync);

SubprocessScriptRunner.RunAsync は、次とほぼ同じです。

// Simplified equivalent of what SubprocessScriptRunner.RunAsync does internally
using System.Diagnostics;
using System.Text.Json;

static async Task<object?> RunAsync(
    AgentFileSkill skill,
    AgentFileSkillScript script,
    JsonElement? args,
    IServiceProvider? serviceProvider,
    CancellationToken cancellationToken)
{
    var psi = new ProcessStartInfo("python3")
    {
        RedirectStandardOutput = true,
        UseShellExecute = false,
    };
    psi.ArgumentList.Add(script.FullPath);
    if (args is { ValueKind: JsonValueKind.Array } json)
    {
        foreach (var element in json.EnumerateArray())
        {
            psi.ArgumentList.Add(element.GetString()!);
        }
    }
    using var process = Process.Start(psi)!;
    string output = await process.StandardOutput.ReadToEndAsync(cancellationToken);
    await process.WaitForExitAsync(cancellationToken);
    return output.Trim();
}

ランナーは、検出された各スクリプトをローカル サブプロセスとして実行します。 ファイル ベースのスクリプトでは、引数が文字列の JSON 配列として想定されます。各配列要素は位置指定コマンド ライン引数になります。

Warnung

SubprocessScriptRunner はデモ目的 でのみ提供されます。 運用環境で使用する場合は、次の追加を検討してください。

  • サンドボックス (コンテナーや分離された実行環境など)
  • リソース制限 (CPU、メモリ、ウォール クロック タイムアウト)
  • 実行可能スクリプトの入力検証と許可リスト
  • 構造化されたログ記録と監査証跡

ファイル ベースのスキル

SkillsProvider.from_paths() ファクトリを使用して、SKILL.md ファイルを含むディレクトリからスキルを検出し、エージェントのコンテキスト プロバイダーにプロバイダーを追加します。

import os
from pathlib import Path
from agent_framework import Agent, SkillsProvider
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential

# Discover skills from the 'skills' directory
skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
)

# Create an agent with the skills provider
endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
deployment = os.environ.get("FOUNDRY_MODEL", "gpt-4o-mini")

client = FoundryChatClient(
    project_endpoint=endpoint,
    model=deployment,
    credential=AzureCliCredential(),
)

agent = Agent(
    client=client,
    instructions="You are a helpful assistant.",
    context_providers=[skills_provider],
)

複数のスキル ディレクトリ

プロバイダーを単一の親ディレクトリにポイントできます。 SKILL.md を含む各サブディレクトリは、スキルとして自動的に検出されます。

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "all-skills"
)

または、パスの一覧を渡して、複数のルート ディレクトリを検索します。

skills_provider = SkillsProvider.from_paths(
    skill_paths=[
        Path(__file__).parent / "company-skills",
        Path(__file__).parent / "team-skills",
    ]
)

プロバイダーは、最大 2 つのレベルの深さまで検索します。

リソースとスクリプトの検出のカスタマイズ

既定では、リソースは agentskills.io 仕様に従って、 references/ および assets/ サブディレクトリから検出され、 scripts/からスクリプトが検出 されます。 認識されるリソース拡張機能は、 .md.json.yaml.yml.csv.xml、および .txtです。 検出をカスタマイズするには、 resource_extensionsscript_extensionsresource_filter、および script_filter を使用します。

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    resource_extensions=(".md", ".txt"),
    script_extensions=(".py", ".sh"),
    resource_filter=lambda skill_name, path: path.startswith("references/"),
    script_filter=lambda skill_name, path: path.startswith("scripts/"),
)

"."を使用して、サブディレクトリに加えてスキル ルート レベルでファイルを含めます。

スクリプトの実行

ファイル ベースのスクリプトの実行を有効にするには、 script_runnerSkillsProvider.from_paths()に渡します。 SkillScriptRunner プロトコルを満たす任意の同期または非同期呼び出し可能な呼び出しを使用できます。

from pathlib import Path
from agent_framework import FileSkill, FileSkillScript, SkillsProvider

def my_runner(
    skill: FileSkill,
    script: FileSkillScript,
    args: dict | list[str] | None = None,
) -> str:
    """Run a file-based script as a subprocess."""
    import subprocess, sys
    script_path = Path(script.full_path)
    cmd = [sys.executable, str(script_path)]
    if isinstance(args, list):
        cmd.extend(args)
    result = subprocess.run(
        cmd, capture_output=True, text=True, timeout=30, cwd=str(script_path.parent)
    )
    return result.stdout.strip()

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    script_runner=my_runner,
)

ランナーは、解決された FileSkillFileSkillScript、および省略可能な args 引数を受け取ります。 ファイル ベースのスクリプトでは、引数が文字列の JSON 配列として想定されます。各配列要素は位置指定コマンド ライン引数になります。 スクリプトは、各スキル ディレクトリの .py サブディレクトリ内のscripts/ ファイルから自動的に検出されます。

Warnung

上記のランナーはデモンストレーション目的 でのみ提供されます。 運用環境で使用する場合は、次の追加を検討してください。

  • サンドボックス (コンテナー、 seccompfirejailなど)
  • リソース制限 (CPU、メモリ、ウォール クロック タイムアウト)
  • 実行可能スクリプトの入力検証と許可リスト
  • 構造化されたログ記録と監査証跡

スクリプトを含むファイル ベースのスキルが提供されていても、 script_runner が設定されていない場合、 SkillsProvider スクリプトの実行が試行されたときにエラーが発生します。

コード定義スキル

SKILL.md ファイルから検出されたファイル ベースのスキルに加えて、AgentInlineSkillを使用してコード内でスキルを完全に定義できます。 コード定義スキルは、次の場合に役立ちます。

  • スキル コンテンツは動的に生成されます (データベースや環境からの読み取りなど)。
  • スキル定義は、それらを使用するアプリケーション コードと共に保持する必要があります。
  • 静的ファイルを提供するのではなく、読み取り時にロジックを実行するリソースが必要です。
  • スキル定義は、実行時に データから構築 する必要があります。たとえば、ロールやアクセス許可に基づいてユーザー セッションごとにパーソナライズされたスキルを作成します。
  • スキルは、DI コンテナーからサービスを解決するのではなく、 呼び出しサイトの状態 (ローカル変数、クロージャ) を閉じる必要があります。

基本的なコード スキル

名前、説明、および手順を含む AgentInlineSkill を作成します。 .AddResource()を使用してリソースをアタッチします。

using Microsoft.Agents.AI;

var codeStyleSkill = new AgentInlineSkill(
    name: "code-style",
    description: "Coding style guidelines and conventions for the team",
    instructions: """
        Use this skill when answering questions about coding style, conventions, or best practices for the team.
        1. Read the style-guide resource for the full set of rules.
        2. Answer based on those rules, quoting the relevant guideline where helpful.
        """)
    .AddResource(
        "style-guide",
        """
        # Team Coding Style Guide
        - Use 4-space indentation (no tabs)
        - Maximum line length: 120 characters
        - Use type annotations on all public methods
        """);

var skillsProvider = new AgentSkillsProvider(codeStyleSkill);

動的リソース

ファクトリ デリゲートを .AddResource() に渡して、実行時にコンテンツを計算します。 デリゲートは、エージェントがリソースを読み取るたびに呼び出されます。

var projectInfoSkill = new AgentInlineSkill(
    name: "project-info",
    description: "Project status and configuration information",
    instructions: """
        Use this skill for questions about the current project.
        1. Read the environment resource for deployment configuration details.
        2. Read the team-roster resource for information about team members.
        """)
    .AddResource("environment", () =>
    {
        string env = Environment.GetEnvironmentVariable("APP_ENV") ?? "development";
        string region = Environment.GetEnvironmentVariable("APP_REGION") ?? "us-east-1";
        return $"Environment: {env}, Region: {region}";
    })
    .AddResource(
        "team-roster",
        "Alice Chen (Tech Lead), Bob Smith (Backend Engineer)");

コード定義スクリプト

.AddScript()を使用して、実行可能スクリプトとしてデリゲートを登録します。 コード定義スクリプトは、直接デリゲート呼び出しとして インプロセスで 実行されます。 スクリプト ランナーは必要ありません。 デリゲートの型指定されたパラメーターは、エージェントが引数を渡すために使用する JSON スキーマに自動的に変換されます。

using System.Text.Json;

var unitConverterSkill = new AgentInlineSkill(
    name: "unit-converter",
    description: "Convert between common units using a conversion factor",
    instructions: """
        Use this skill when the user asks to convert between units.
        1. Review the conversion-table resource to find the correct factor.
        2. Use the convert script, passing the value and factor from the table.
        3. Present the result clearly with both units.
        """)
    .AddResource(
        "conversion-table",
        """
        # Conversion Tables
        Formula: **result = value × factor**
        | From       | To         | Factor   |
        |------------|------------|----------|
        | miles      | kilometers | 1.60934  |
        | kilometers | miles      | 0.621371 |
        | pounds     | kilograms  | 0.453592 |
        | kilograms  | pounds     | 2.20462  |
        """)
    .AddScript("convert", (double value, double factor) =>
    {
        double result = Math.Round(value * factor, 4);
        return JsonSerializer.Serialize(new { value, factor, result });
    });

var skillsProvider = new AgentSkillsProvider(unitConverterSkill);

コード定義スキルとファイル ベースまたはクラスベースのスキルを 1 つのプロバイダーで組み合わせるには、 AgentSkillsProviderBuilder を使用します。プロバイダーの 構築に関するセクションを参照してください。

SKILL.md ファイルから検出されたファイル ベースのスキルに加えて、InlineSkill を使用して、Python コードでスキルを完全に定義できます。 コード定義スキルは、次の場合に役立ちます。

  • スキル コンテンツは動的に生成されます (データベースや環境からの読み取りなど)。
  • スキル定義は、それらを使用するアプリケーション コードと共に保持する必要があります。
  • 静的ファイルを提供するのではなく、読み取り時にロジックを実行するリソースが必要です。
  • スキル定義は、実行時に データから構築 する必要があります。たとえば、ロールやアクセス許可に基づいてユーザー セッションごとにパーソナライズされたスキルを作成します。
  • スキルは、呼び出し元の状態 (ローカル変数、クロージャ) を閉じ込める必要があり、**kwargs を介してサービスを解決してはなりません。

基本的なコード スキル

InlineSkill (名前と説明を含む) と命令コンテンツを含むSkillFrontmatter インスタンスを作成します。 必要に応じて、静的コンテンツ InlineSkillResource インスタンスをアタッチします。

from textwrap import dedent
from agent_framework import InlineSkill, InlineSkillResource, SkillFrontmatter, SkillsProvider

code_style_skill = InlineSkill(
    frontmatter=SkillFrontmatter(
        name="code-style",
        description="Coding style guidelines and conventions for the team",
    ),
    instructions=dedent("""\
        Use this skill when answering questions about coding style,
        conventions, or best practices for the team.
    """),
    resources=[
        InlineSkillResource(
            name="style-guide",
            content=dedent("""\
                # Team Coding Style Guide
                - Use 4-space indentation (no tabs)
                - Maximum line length: 120 characters
                - Use type annotations on all public functions
            """),
        ),
    ],
)

skills_provider = SkillsProvider(code_style_skill)

動的リソース

@skill.resource デコレーターを使用して、関数をリソースとして登録します。 この関数は、エージェントがリソースを読み取るたびに呼び出されるため、最新のデータを返すことができます。 同期関数と非同期関数の両方がサポートされています。

import os
from agent_framework import InlineSkill, SkillFrontmatter

project_info_skill = InlineSkill(
    frontmatter=SkillFrontmatter(
        name="project-info",
        description="Project status and configuration information",
    ),
    instructions="Use this skill for questions about the current project.",
)

@project_info_skill.resource
def environment() -> str:
    """Get current environment configuration."""
    env = os.environ.get("APP_ENV", "development")
    region = os.environ.get("APP_REGION", "us-east-1")
    return f"Environment: {env}, Region: {region}"

@project_info_skill.resource(name="team-roster", description="Current team members")
def get_team_roster() -> str:
    """Return the team roster."""
    return "Alice Chen (Tech Lead), Bob Smith (Backend Engineer)"

引数 (@skill.resource) なしでデコレーターを使用すると、関数名がリソース名になり、docstring が説明になります。 @skill.resource(name="...", description="...")を使用して明示的に設定します。

コード定義スクリプト

@skill.scriptデコレーターを使用して、スキルの実行可能スクリプトとして関数を登録します。 コード定義スクリプトは インプロセスで 実行され、スクリプト ランナーは必要ありません。 同期関数と非同期関数の両方がサポートされています。

from agent_framework import InlineSkill, SkillFrontmatter

unit_converter_skill = InlineSkill(
    frontmatter=SkillFrontmatter(
        name="unit-converter",
        description="Convert between common units using a conversion factor",
    ),
    instructions="Use the convert script to perform unit conversions.",
)

@unit_converter_skill.script(name="convert", description="Convert a value: result = value × factor")
def convert_units(value: float, factor: float) -> str:
    """Convert a value using a multiplication factor."""
    import json
    result = round(value * factor, 4)
    return json.dumps({"value": value, "factor": factor, "result": result})

引数 (@skill.script) なしでデコレーターを使用すると、関数名がスクリプト名になり、docstring が説明になります。 関数の型指定されたパラメーターは、エージェントが引数を渡すために使用する JSON スキーマに自動的に変換されます。

クラスベースのスキル

クラスベースのスキルを使用すると、すべてのスキル コンポーネント (名前、説明、命令、リソース、スクリプト) を 1 つの C# クラスにバンドルできます。 これにより、NuGet パッケージとしてのパッケージ化と配布が簡単になります。チームは個別にスキルを作成して出荷することができ、コンシューマーは dotnet add package と 1 回の .UseSkill() 呼び出しでスキルを追加できます。 AgentClassSkill<T> (クラスT) から派生し、自動検出のためにプロパティに [AgentSkillResource] とメソッドに [AgentSkillScript] 注釈を付けます。

using System.ComponentModel;
using System.Text.Json;
using Microsoft.Agents.AI;

internal sealed class UnitConverterSkill : AgentClassSkill<UnitConverterSkill>
{
    public override AgentSkillFrontmatter Frontmatter { get; } = new(
        "unit-converter",
        "Convert between common units using a multiplication factor. Use when asked to convert miles, kilometers, pounds, or kilograms.");

    protected override string Instructions => """
        Use this skill when the user asks to convert between units.

        1. Review the conversion-table resource to find the correct factor.
        2. Use the convert script, passing the value and factor from the table.
        3. Present the result clearly with both units.
        """;

    [AgentSkillResource("conversion-table")]
    [Description("Lookup table of multiplication factors for common unit conversions.")]
    public string ConversionTable => """
        # Conversion Tables
        Formula: **result = value × factor**
        | From       | To         | Factor   |
        |------------|------------|----------|
        | miles      | kilometers | 1.60934  |
        | kilometers | miles      | 0.621371 |
        | pounds     | kilograms  | 0.453592 |
        | kilograms  | pounds     | 2.20462  |
        """;

    [AgentSkillScript("convert")]
    [Description("Multiplies a value by a conversion factor and returns the result as JSON.")]
    private static string ConvertUnits(double value, double factor)
    {
        double result = Math.Round(value * factor, 4);
        return JsonSerializer.Serialize(new { value, factor, result });
    }
}

クラスベースのスキルを AgentSkillsProviderに登録します。

var skill = new UnitConverterSkill();
var skillsProvider = new AgentSkillsProvider(skill);

[AgentSkillResource]属性がプロパティまたはメソッドに適用されている場合、その戻り値は、エージェントがリソースを読み取るときにリソース コンテンツとして使用されます。読み取り時にコンテンツを計算する必要がある場合はメソッドを使用します。 [AgentSkillScript]がメソッドに適用されると、エージェントがスクリプトを呼び出すときにメソッドが呼び出されます。 [Description]System.ComponentModelを使用して、エージェントの各リソースとスクリプトを記述します。

AgentClassSkill<T> では、属性ベースの検出が適合しないシナリオのコレクションとして、 ResourcesScripts のオーバーライドもサポートされています。

クラスベースのスキル

クラスベースのスキルを使用すると、名前、説明、命令、リソース、スクリプトなど、すべてのスキル コンポーネントを 1 つのPython クラスにバンドルできます。 これにより、PyPI パッケージとしてのパッケージ化と配布が簡単になります。チームは、スキルを個別に作成して出荷することができ、コンシューマーは pip install と 1 回の SkillsProvider() 呼び出しでスキルを追加できます。 ClassSkillサブクラス化し、自動検出に@ClassSkill.resourceおよび@ClassSkill.scriptデコレーターを使用します。

import json
from textwrap import dedent
from agent_framework import ClassSkill, SkillFrontmatter

class UnitConverterSkill(ClassSkill):
    """A unit-converter skill defined as a Python class."""

    def __init__(self) -> None:
        super().__init__(
            frontmatter=SkillFrontmatter(
                name="unit-converter",
                description=(
                    "Convert between common units using a multiplication factor. "
                    "Use when asked to convert miles, kilometers, pounds, or kilograms."
                ),
            ),
        )

    @property
    def instructions(self) -> str:
        return dedent("""\
            Use this skill when the user asks to convert between units.

            1. Review the conversion-table resource to find the correct factor.
            2. Use the convert script, passing the value and factor from the table.
            3. Present the result clearly with both units.
        """)

    @property
    @ClassSkill.resource
    def conversion_table(self) -> str:
        """Lookup table of multiplication factors for common unit conversions."""
        return dedent("""\
            # Conversion Tables
            Formula: **result = value × factor**
            | From       | To         | Factor   |
            |------------|------------|----------|
            | miles      | kilometers | 1.60934  |
            | kilometers | miles      | 0.621371 |
            | pounds     | kilograms  | 0.453592 |
            | kilograms  | pounds     | 2.20462  |
        """)

    @ClassSkill.script(name="convert", description="Multiplies a value by a conversion factor.")
    def convert_units(self, value: float, factor: float) -> str:
        """Convert a value using a multiplication factor."""
        result = round(value * factor, 4)
        return json.dumps({"value": value, "factor": factor, "result": result})

クラスベースのスキルを SkillsProviderに登録します。

from agent_framework import SkillsProvider

skill = UnitConverterSkill()
skills_provider = SkillsProvider(skill)

@ClassSkill.resourceがベア デコレーター (引数なし) として適用されている場合、メソッド名はリソース名になり (アンダースコアはハイフンに変換されます)、docstring は説明になります。 @ClassSkill.resource(name="...", description="...")を使用して明示的に設定します。 @ClassSkill.scriptにも同じパターンが適用されます。

リソースは、通常のメソッドまたは @property 記述子として定義できます。 @propertyを使用する場合は、@propertyを最初に配置し、@ClassSkill.resourceを2番目に配置します。 リソースの戻り値は、最初のアクセス後にキャッシュされます。

ClassSkillまた、デコレータベースの検出が適合しないシナリオでは、resourcesおよびscriptsインスタンスを直接返すために、InlineSkillResourceプロパティとInlineSkillScriptプロパティを明示的にオーバーライドすることもできます。

MCP ベースのスキル

MCP ベースのスキルには、 Microsoft.Agents.AI.Mcp NuGet パッケージが必要です。 MCP スキル API は試験段階であり、今後のリリースで変更される可能性があります。

スキルは、 skill:// URI スキームでスキル リソースを公開する MCP (モデル コンテキスト プロトコル) サーバーから検出できます。 MCP サーバーは、 skill://index.json 検出ドキュメントを介してスキルをアドバタイズし、フレームワークは必要に応じてスキル コンテンツをフェッチします。

MCP ベースのスキルでは、次の 2 種類のインデックス エントリがサポートされます。

  • skill-md - スキルの SKILL.md と関連リソースは、MCP サーバーからオンデマンドで取得されます。
  • archive - スキルは、ローカルにダウンロードおよびアンパックされる 1 つのパッケージアーカイブ (ZIP、TAR、または gzip 圧縮 TAR) として配布されます。

基本的な使用方法

UseMcpSkillsAgentSkillsProviderBuilder 拡張メソッドを使用して、MCP スキル ソースを追加します。

using Microsoft.Agents.AI;
using ModelContextProtocol.Client;

// Connect to the MCP server
await using McpClient client = await McpClient.CreateAsync(
    new StdioClientTransport(new()
    {
        Name = "skills-server",
        Command = "dotnet",
        Arguments = [skillsServerPath, "--server"],
    }));

// Build a skills provider that discovers skills over MCP
var skillsProvider = new AgentSkillsProviderBuilder()
    .UseMcpSkills(client)
    .Build();

// Create an agent with the MCP skills
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetResponsesClient()
    .AsAIAgent(new ChatClientAgentOptions
    {
        Name = "SkillsAgent",
        ChatOptions = new()
        {
            Instructions = "You are a helpful assistant. Use available skills to answer the user.",
        },
        AIContextProviders = [skillsProvider],
    },
    model: deploymentName);

アーカイブ型のスキル

アーカイブの種類のスキルの場合は、(AgentMcpSkillsSourceOptions パッケージから) Microsoft.Agents.AI.Mcpを使用して抽出動作を構成します。

var skillsProvider = new AgentSkillsProviderBuilder()
    .UseMcpSkills(client, new AgentMcpSkillsSourceOptions
    {
        ArchiveSkillsDirectory = Path.Combine(AppContext.BaseDirectory, "extracted-skills"),
        ArchiveMaxFileCount = 50,
        ArchiveMaxSizeBytes = 2 * 1024 * 1024, // 2 MB
    })
    .Build();

AgentMcpSkillsSourceOptions は、アーカイブの抽出を制御するために次のプロパティを公開します。

  • ArchiveSkillsDirectory - 抽出されたアーカイブのベース ディレクトリ。 複数のソース間の競合を防ぐためにソース インスタンスごとに生成される、現在の作業ディレクトリの下にある一意のサブディレクトリが既定で設定されます。
  • ArchiveResourceExtensions - 抽出されたアーカイブ内のリソースに対して許可される拡張機能。 既定値は、 .md.json.yaml.yml.csv.xml.txtです。
  • ArchiveResourceSearchDepth - 抽出された各スキル ディレクトリ内のリソースを検索する深さ。 既定値は 2 です。
  • ArchiveMaxFileCount - アーカイブあたりの最大ファイル数。 この制限を超えるアーカイブはスキップされます。 既定値は 20 です。
  • ArchiveMaxSizeBytes - アーカイブあたりの最大ダウンロード サイズ。 既定値は 1 MB です。
  • ArchiveMaxUncompressedSizeBytes - アーカイブあたりの圧縮されていない最大サイズ。 既定値は 1 MB です。

Important

アーカイブ型スキルにバンドルされているスクリプトは 実行されません。 これは意図的なセキュリティ対策です。リモート MCP サーバーからの実行可能コンテンツには明示的な信頼が必要です。

スキルの提供元

AgentSkillsProviderは、1 つ以上のソース (AgentSkillsSourceを実装するオブジェクト) からスキルを取得します。 ソースは、スキルを検出または保持する リーフ ソース (ファイル ベースのスキルの AgentFileSkillsSource など) と、別のソースの出力を変換する デコレーター (集計、重複除去、キャッシュ、フィルター処理) の 2 つのカテゴリに分類されます。 カスタム ソースを作成することもできます。

すべてのソースは、1 つのメソッド ( GetSkillsAsync(AgentSkillsSourceContext context, CancellationToken cancellationToken = default)) を実装します。 AgentSkillsSourceContextには、現在の要求に関する情報が含まれます。

  • Agent - スキルを要求する AIAgent インスタンス。
  • Session - 呼び出しに関連付けられた AgentSession 。セッションがない場合は null

このコンテキストはソース パイプライン全体で使用できるため、 FilteringAgentSkillsSource 述語またはカスタム ソースはそのロジックに基づいて、要求元エージェントに応じて異なるスキル セットを返すことができます。

リーフソース

AgentFileSkillsSource

ディスク上の SKILL.md ファイルからスキルを検出します。 1 つ以上のディレクトリ パス、オプションのスクリプト ランナー、およびオプションの AgentFileSkillsSourceOptions を受け入れます ( ファイル ベースのスキルに関する記事を参照)。

var source = new AgentFileSkillsSource(
    [Path.Combine(AppContext.BaseDirectory, "skills")],
    scriptRunner: SubprocessScriptRunner.RunAsync,
    options: new AgentFileSkillsSourceOptions { SearchDepth = 3 });

AgentInMemorySkillsSource

メモリ内 AgentSkill インスタンス (コード定義またはクラスベース) をラップします。

var source = new AgentInMemorySkillsSource([volumeConverterSkill, temperatureConverter]);

コンビネータ

AggregatingAgentSkillsSource

複数のソースを 1 つに結合します。 スキルは登録順に返され、重複除去やフィルター処理は適用されません。

var aggregated = new AggregatingAgentSkillsSource([fileSource, inMemorySource]);

デコレータ

デコレーターは内部のソースを包み込み、その出力を変換します。 これらは、パイプラインを構築するためにチェーンできます。

DeduplicatingAgentSkillsSource

重複するスキル名を削除します (大文字と小文字が区別されず、最初の出現が優先されます)。 重複は警告レベルでログに記録されます。

var deduplicated = new DeduplicatingAgentSkillsSource(innerSource);

CachingAgentSkillsSource

内部ソースによって返されたスキル リストをキャッシュします。 同時呼び出し元はキャッシュ キーごとにシリアル化されるため、一度に実行されるフェッチは 1 つだけです。 省略可能な CachingAgentSkillsSourceOptionsを受け入れます。

  • RefreshInterval (TimeSpan?) - 設定すると、キャッシュされた結果はこの間隔の後に期限切れになり、内部ソースが再呼び出されます。 null (既定値) の場合、キャッシュされた結果は期限切れになりません。
  • CacheIsolationKeySelector (Func<AgentSkillsSourceContext, string?>?) - キャッシュされた結果をコンテキスト (テナントごとなど) で分離するためのキャッシュ キーを返します。 nullすると、すべての呼び出し元が 1 つのキャッシュ バケットを共有します。
var cached = new CachingAgentSkillsSource(innerSource, new CachingAgentSkillsSourceOptions
{
    RefreshInterval = TimeSpan.FromMinutes(5)
});

FilteringAgentSkillsSource

スキルを含めるか除外する述語を適用します。 述語はスキルと AgentSkillsSourceContext を受け取ります。

var filtered = new FilteringAgentSkillsSource(
    innerSource,
    (skill, context) => skill.Frontmatter.Name != "experimental-skill");

カスタム ソース

組み込みのソースがシナリオに対応していない場合は、独自のソースを実装します。 リーフ ソース(データベースやリモート サービスなどの新しいソースからスキルを生成するもの)の場合は AgentSkillsSource をサブクラス化し、別のソースの出力を変換するデコレーターの場合は DelegatingAgentSkillsSource をサブクラス化します。

末端ソース

AgentSkillsSourceから派生し、GetSkillsAsyncを実装します。 AgentSkillsSourceContext引数を使用すると、ソースは現在の要求に合わせて結果を調整できます。たとえば、要求元エージェントに応じて異なるスキル セットを返します。 ソースがクライアントや接続などのリソースを所有している場合は、 Dispose(bool) をオーバーライドします。

public sealed class TenantSkillsSource : AgentSkillsSource
{
    private readonly ISkillStore _store;

    public TenantSkillsSource(ISkillStore store)
    {
        _store = store;
    }

    public override async Task<IList<AgentSkill>> GetSkillsAsync(
        AgentSkillsSourceContext context,
        CancellationToken cancellationToken = default)
    {
        // Use the requesting agent to decide which skills to load.
        var tenantId = context.Agent.Name ?? "default";
        return await _store.GetSkillsForTenantAsync(tenantId, cancellationToken);
    }
}

カスタム デコレーター

DelegatingAgentSkillsSourceから派生し、InnerSource.GetSkillsAsyncを呼び出し、結果を変換または観察します。 これは、組み込みのキャッシュ、重複除去、およびフィルター処理デコレーターが使用するのと同じパターンです。 たとえば、結果を変更せずに要求ごとに返されたスキルの数を記録するデコレーターです。

public sealed class MetricsAgentSkillsSource : DelegatingAgentSkillsSource
{
    private readonly ILogger<MetricsAgentSkillsSource> _logger;

    public MetricsAgentSkillsSource(
        AgentSkillsSource innerSource,
        ILogger<MetricsAgentSkillsSource> logger)
        : base(innerSource)
    {
        _logger = logger;
    }

    public override async Task<IList<AgentSkill>> GetSkillsAsync(
        AgentSkillsSourceContext context,
        CancellationToken cancellationToken = default)
    {
        var skills = await base.GetSkillsAsync(context, cancellationToken);
        _logger.LogInformation(
            "Returned {SkillCount} skills to agent {AgentName}.",
            skills.Count,
            context.Agent.Name);
        return skills;
    }
}

両方のカスタム ソースを AgentSkillsProvider に直接渡すか、組み込みのソースと同様に、より大きなパイプライン内に入れ子にすることができます。

プロバイダーの構築

AgentSkillsProvider は、エージェントにスキルを公開するコンポーネントです。 1 つ以上のソースをラップし、 load_skillread_skill_resource、および run_skill_script ツールを登録します。 1 つを作成するには、次の 3 つの方法があります。

  1. AgentSkillsProviderBuilder - 自動集計、重複除去、キャッシュ、およびオプションのフィルター処理を使用して、複数のスキルの種類を 1 つのプロバイダーに作成します。 ファイルベース、コード定義、クラスベース、MCP ベースのスキルを組み合わせたシナリオに最適です。
  2. 直接ソース構成 - パブリック AgentSkillsSource クラスを使用してソース パイプラインを自分で構築します。 自動キャッシュまたは重複除去は適用されません。パイプライン全体を制御します。 順序付け、条件付きロジック、またはカスタム デコレーターの動作を制御する必要がある場合に最適です。
  3. 便利なコンストラクター - ファイル パスまたはスキル インスタンスから直接プロバイダーを作成します。 重複除去とキャッシュを自動的に適用します。 単一ソースのシナリオに最適です。

AgentSkillsProviderBuilder の使用

次のいずれかが必要な場合は、 AgentSkillsProviderBuilder を使用します。

  • 混合スキルの種類 - ファイル ベース、コード定義 (AgentInlineSkill)、クラスベース (AgentClassSkill)、および MCP ベースのスキルを 1 つのプロバイダーで結合します。
  • スキルのフィルター処理 - 述語を使用してスキルを含めるか除外します。

混合スキルの種類

UseFileSkillUseSkillUseMcpSkillsUseFileScriptRunnerを連結して、1 つのプロバイダーで複数のスキルの種類を結合します。

var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))  // file-based skills
    .UseSkill(volumeConverterSkill)                                  // AgentInlineSkill
    .UseSkill(temperatureConverter)                                  // AgentClassSkill
    .UseMcpSkills(mcpClient)                                         // MCP-based skills
    .UseFileScriptRunner(SubprocessScriptRunner.RunAsync)            // runner for file scripts
    .Build();

スキルフィルタリング

UseFilterを使用して、条件を満たすスキルのみを含めます。たとえば、共有ディレクトリからスキルを読み込み、試験的なスキルは除外します。

var approvedSkillNames = new HashSet<string> { "expense-report", "code-style" };

var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))
    .UseFilter((skill, context) => approvedSkillNames.Contains(skill.Frontmatter.Name))
    .Build();

ソースを直接作成する

ビルダーが必要なコントロールを提供していない場合は、ソース クラスを自分で作成し、結果のパイプラインを AgentSkillsProviderに渡します。 使用可能なソースとそのオプションの完全な一覧については、 スキル ソースを参照してください。

次の例では、同等のマルチソース パイプラインを構築しますが、各デコレーターを明示的に制御できます。

// 1. Create the leaf sources
var fileSource = new AgentFileSkillsSource(
    [Path.Combine(AppContext.BaseDirectory, "skills")],
    SubprocessScriptRunner.RunAsync);

var inMemorySource = new AgentInMemorySkillsSource(
    [volumeConverterSkill, temperatureConverter]);

// 2. Aggregate them into one source
var aggregated = new AggregatingAgentSkillsSource([fileSource, inMemorySource]);

// 3. Add deduplication and caching decorators
var deduplicated = new DeduplicatingAgentSkillsSource(aggregated);
var cached = new CachingAgentSkillsSource(deduplicated);

// 4. Create the provider, transferring source ownership
var skillsProvider = new AgentSkillsProvider(
    cached,
    options: new AgentSkillsProviderOptions(),
    ownsSource: true);

ownsSourcetrueされると、プロバイダーを破棄すると、ソース パイプライン全体も破棄されます。 ソース ライフサイクルを自分で管理する場合は、 false に設定します。

簡易コンストラクター

単一ソースのシナリオでは、 AgentSkillsProvider コンストラクターを直接使用します。 これらは、ビルダーまたは手動のソース構成を必要とせずに、重複除去とキャッシュを自動的に適用します。

ファイルパスから:

var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    scriptRunner: SubprocessScriptRunner.RunAsync);

スキルインスタンスから:

var skillsProvider = new AgentSkillsProvider(volumeConverterSkill, temperatureConverter);

ソース構成: 高度なマルチソース シナリオ

1 つのスキルまたはスキルの一覧を使用する単純なシナリオでは、 SkillsProvider コンストラクターに直接渡します。 ファイル ベースのスキルについては、 SkillsProvider.from_paths() ファクトリを使用します。 高度なシナリオでは、検出、フィルター処理、重複除去を制御するソース クラスを作成します。

  • FileSkillsSource - ディスク上の SKILL.md ファイルからスキルを検出します。
  • InMemorySkillsSource - メモリ内の Skill インスタンス (コード定義またはクラスベース) をラップします。
  • AggregatingSkillsSource - 複数のソースを 1 つに結合します。
  • FilteringSkillsSource - スキルを含めるか除外する述語を適用します。
  • DeduplicatingSkillsSource - 重複するスキル名を削除します(大文字と小文字を区別せず、最初のものを優先)。

混合スキルの種類

AggregatingSkillsSourceを使用して、1 つのプロバイダーでファイル ベース、コード定義、クラスベースのスキルを組み合わせます。 次の例では、プレースホルダー オブジェクトを使用します。

  • volume_converter_skill- InlineSkillに示すように構築された任意の インスタンス。
  • TemperatureConverterSkill- ClassSkillに示すように構築された任意のサブクラス。
  • my_runner - SkillScriptRunner 呼び出し可能オブジェクト。スクリプトの実行に示すように定義されます。
from pathlib import Path
from agent_framework import (
    AggregatingSkillsSource,
    DeduplicatingSkillsSource,
    FileSkillsSource,
    InMemorySkillsSource,
    SkillsProvider,
)

temperature_converter_skill = TemperatureConverterSkill()

skills_provider = SkillsProvider(
    DeduplicatingSkillsSource(
        AggregatingSkillsSource([
            FileSkillsSource(
                Path(__file__).parent / "skills",
                script_runner=my_runner,
            ),
            InMemorySkillsSource([volume_converter_skill, temperature_converter_skill]),
        ])
    )
)

スキルフィルタリング

FilteringSkillsSourceを使用して、エージェントに表示されるスキルを制御します。 述語は各 Skill を受け取り、それを含める True を返します。 たとえば、共有ディレクトリからスキルを読み込み、実験用のスキルを非表示にするには、次のようにします。

from pathlib import Path
from agent_framework import (
    DeduplicatingSkillsSource,
    FileSkillsSource,
    FilteringSkillsSource,
    SkillsProvider,
)

skills_provider = SkillsProvider(
    DeduplicatingSkillsSource(
        FilteringSkillsSource(
            FileSkillsSource(Path(__file__).parent / "skills"),
            predicate=lambda skill: skill.frontmatter.name != "experimental-tools",
        )
    )
)

キャッシュ動作

既定では、ビルダーは、基になるソースによって返されるスキルの一覧をキャッシュする CachingAgentSkillsSource でソース パイプラインをラップします。 最初の要求でスキルが解決されると、後続の要求では、ソースに対してクエリを再実行することなく、キャッシュされたリストが再利用されます。 キャッシュを無効にするには (スキル定義が頻繁に変更される開発中など)、ビルダーで DisableCaching() を使用します。

var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))
    .UseFileScriptRunner(SubprocessScriptRunner.RunAsync)
    .DisableCaching()
    .Build();

キャッシュの無効化は、スキル コンテンツが頻繁に変更される開発時に役立ちます。 運用環境では、パフォーマンスを向上させるためにキャッシュを有効のままにします (既定値)。

キャッシュ動作

既定では、スキル ツールと命令は最初のビルドの後にキャッシュされます。 disable_caching=Trueを設定して、すべての呼び出しで再構築を強制します。

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    disable_caching=True,
)

disable_caching は、コード定義およびクラスベースのスキルの SkillsProvider コンストラクターでも使用できます。

キャッシュの無効化は、スキル コンテンツが頻繁に変更される開発時に役立ちます。 運用環境では、パフォーマンスを向上させるためにキャッシュを有効のままにします (既定値)。

ツールの承認

AgentSkillsProviderによって公開されるすべてのツール (load_skillread_skill_resourcerun_skill_script) には、既定で承認が必要です。 ツール呼び出しで承認が必要な場合、エージェントは一時停止し、すぐに実行するのではなく、 ToolApprovalRequestContent を返します。 自動承認規則 UseToolApproval ミドルウェアを使用して、信頼できる操作のプロンプトを選択的にバイパスします。

using Microsoft.Agents.AI;
using Microsoft.Extensions.AI;

var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    SubprocessScriptRunner.RunAsync);

AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetResponsesClient()
    .AsAIAgent(new ChatClientAgentOptions
    {
        Name = "SkillsAgent",
        ChatOptions = new() { Instructions = "You are a helpful assistant." },
        AIContextProviders = [skillsProvider],
    },
    model: deploymentName)
    .AsBuilder()
    .UseToolApproval(new ToolApprovalAgentOptions
    {
        // Auto-approve read-only skill tools (load_skill, read_skill_resource).
        // run_skill_script still requires explicit user approval.
        AutoApprovalRules = [AgentSkillsProvider.ReadOnlyToolsAutoApprovalRule],
    })
    .Build();

スクリプトの実行を含むすべてのスキル ツールを自動承認するには:

.UseToolApproval(new ToolApprovalAgentOptions
{
    AutoApprovalRules = [AgentSkillsProvider.AllToolsAutoApprovalRule],
})

特定のツールの承認を無効にする

AgentSkillsProviderOptionsを使用して個々のツールの承認を無効にし、承認フローから完全に削除します。

var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    SubprocessScriptRunner.RunAsync,
    options: new AgentSkillsProviderOptions
    {
        DisableLoadSkillApproval = true,
        DisableReadSkillResourceApproval = true,
        // DisableRunSkillScriptApproval remains false - scripts still require approval
    });

一部のツールで承認が必要で、他のツールが同じ応答に含まれていない場合、モデルは両方の型を同時に呼び出すことができます。 EnableNonApprovalRequiredFunctionBypassing設定すると、ユーザーが残りのツールについてのみ入力を求められる間、承認不要のツールがすぐに実行されます。

AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetResponsesClient()
    .AsAIAgent(new ChatClientAgentOptions
    {
        Name = "SkillsAgent",
        ChatOptions = new() { Instructions = "You are a helpful assistant." },
        AIContextProviders = [skillsProvider],
        EnableNonApprovalRequiredFunctionBypassing = true,
    },
    model: deploymentName)
    .AsBuilder()
    .UseToolApproval()
    .Build();

承認要求の処理

ツールで承認が必要な場合 (および自動承認ルールが一致しない場合)、エージェントは、次の作業を続行する前に承認または拒否する必要がある ToolApprovalRequestContent 項目を返します。

AgentSession session = await agent.CreateSessionAsync();
AgentResponse response = await agent.RunAsync("Convert 26.2 miles to kilometers", session);

List<ToolApprovalRequestContent> approvalRequests = response.Messages
    .SelectMany(m => m.Contents)
    .OfType<ToolApprovalRequestContent>()
    .ToList();

while (approvalRequests.Count > 0)
{
    List<ChatMessage> userInputResponses = approvalRequests
        .ConvertAll(request =>
        {
            var toolCall = (FunctionCallContent)request.ToolCall;
            Console.WriteLine($"Approve {toolCall.Name}? (Y/N)");
            bool approved = Console.ReadLine()?.Equals("Y", StringComparison.OrdinalIgnoreCase) ?? false;
            return new ChatMessage(ChatRole.User, [request.CreateResponse(approved)]);
        });

    response = await agent.RunAsync(userInputResponses, session);
    approvalRequests = response.Messages
        .SelectMany(m => m.Contents)
        .OfType<ToolApprovalRequestContent>()
        .ToList();
}

スクリプト エラーの詳細

既定では、スキル スクリプトの実行が失敗すると、基になる FunctionInvokingChatClientに例外が反映されます。 IncludeDetailedErrors プロパティが true に設定されている場合、例外メッセージはモデルに転送され、異なる引数を使用して再試行することで自己修正できるようになります。

AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetResponsesClient()
    .AsAIAgent(
        options: new ChatClientAgentOptions
        {
            Name = "SkillsAgent",
            ChatOptions = new()
            {
                Instructions = "You are a helpful assistant.",
            },
            AIContextProviders = [skillsProvider],
        },
        model: deploymentName,
        clientFactory: client => client
            .AsBuilder()
            .UseFunctionInvocation(configure: (c) => c.IncludeDetailedErrors = true)
            .Build());

FunctionInvokingChatClientを直接構成できない場合は、代わりにAgentSkillsProviderOptions.IncludeDetailedErrors設定してください。 これにより、スキル プロバイダー レベルで例外がキャッチされ、エラー メッセージがモデルに直接返されます。

var skillsProvider = new AgentSkillsProvider(
    Path.Combine(AppContext.BaseDirectory, "skills"),
    SubprocessScriptRunner.RunAsync,
    options: new AgentSkillsProviderOptions
    {
        IncludeDetailedErrors = true,
    });

Warnung

どちらの方法でも、未加工の例外の詳細をモデルに開示できます。 例外メッセージには、接続文字列、ファイル パス、内部サービス名などの機密情報を含めることができます。 さらに、スキルまたはスクリプトの提供元が信頼できない場合、悪意を持って細工されたスクリプトによって、メッセージにプロンプトインジェクションのペイロードを埋め込んだ例外が発生させられる可能性があります。

require_script_approval=TrueSkillsProviderを使用して、人間の承認の背後にあるすべてのスクリプト実行をゲートします。 エージェントは、すぐに実行するのではなく、一時停止し、 result.user_input_requests経由で承認要求を返します。

from textwrap import dedent
from agent_framework import Agent, InlineSkill, SkillFrontmatter, SkillsProvider

deployment_skill = InlineSkill(
    frontmatter=SkillFrontmatter(
        name="deployment",
        description="Tools for deploying application versions to production",
    ),
    instructions=dedent("""\
        Use this skill when the user asks to deploy an application.
        Run the deploy script with the version and environment parameters.
    """),
)

@deployment_skill.script
def deploy(version: str, environment: str = "staging") -> str:
    """Deploy the application to the specified environment."""
    return f"Deployed version {version} to {environment}"

skills_provider = SkillsProvider(deployment_skill, require_script_approval=True)

async with Agent(
    client=client,
    instructions="You are a deployment assistant.",
    context_providers=[skills_provider],
) as agent:
    # Use a session so the agent retains context across approval round-trips
    session = agent.create_session()

    result = await agent.run(
        "Deploy version 2.5.0 to production",
        session=session,
    )

    # Handle approval requests
    while result.user_input_requests:
        for request in result.user_input_requests:
            print(f"Script: {request.function_call.name}")
            print(f"Args: {request.function_call.arguments}")

            approval = request.to_function_approval_response(approved=True)
            result = await agent.run(approval, session=session)

    print(result)

スクリプトが拒否されると (approved=False)、エージェントはユーザーが辞退したことを通知され、それに応じて応答できます。

カスタム システム プロンプト

既定では、スキル プロバイダーは、使用可能なスキルを一覧表示するシステム プロンプトを挿入し、 load_skillread_skill_resourceを使用するようにエージェントに指示します。 このプロンプトは、次のようにカスタマイズできます。

var skillsProvider = new AgentSkillsProvider(
    skillPath: Path.Combine(AppContext.BaseDirectory, "skills"),
    options: new AgentSkillsProviderOptions
    {
        SkillsInstructionPrompt = """
            You have skills available. Here they are:
            {skills}
            When a task matches a skill, use load_skill to retrieve instructions,
            then read_skill_resource for referenced resources, and run_skill_script for scripts.
            """
    });

カスタム テンプレートには、生成されたスキル リストのプレースホルダーとして {skills} が含まれている必要があります。 リテラルの中括弧は {{ および }} としてエスケープする必要があります。

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    instruction_template=(
        "You have skills available. Here they are:\n{skills}\n"
        "{resource_instructions}\n"
        "{runner_instructions}"
    ),
)

カスタム テンプレートには、 {skills} (スキル リスト)、 {resource_instructions} (リソース ツール ヒント)、および {runner_instructions} (スクリプト ツール ヒント) プレースホルダーが含まれている必要があります。 リテラルの中括弧は {{ および }} としてエスケープする必要があります。

サービスとランタイム引数の挿入

スキル リソース関数とスクリプト関数は、実行時に提供される外部アプリケーション コンテキストを受け取ることができます。

スキル リソースとスクリプト デリゲートは、Agent Framework によって自動的に挿入される IServiceProvider パラメーターを宣言できます。 これにより、登録されているアプリケーション サービスをオンデマンドでスキルで解決できます。

セットアップ

アプリケーション サービスを登録し、IServiceProvider パラメーターを使用してビルドされたservicesをエージェントに渡します。

using Microsoft.Extensions.DependencyInjection;

// Register application services
ServiceCollection services = new();
services.AddSingleton<ConversionService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Create the agent and pass the service provider
AIAgent agent = new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
    .GetResponsesClient()
    .AsAIAgent(
        options: new ChatClientAgentOptions
        {
            Name = "ConverterAgent",
            ChatOptions = new() { Instructions = "You are a helpful assistant." },
            AIContextProviders = [skillsProvider],
        },
        model: deploymentName,
        services: serviceProvider);

DI を使用したコード定義スキル

IServiceProviderAddResource または AddScript デリゲートのパラメーターとして宣言します。エージェントがリソースを読み取るかスクリプトを実行すると、フレームワークによって自動的に解決され、挿入されます。

var distanceSkill = new AgentInlineSkill(
    name: "distance-converter",
    description: "Convert between distance units (miles and kilometers).",
    instructions: """
        Use this skill when the user asks to convert between miles and kilometers.
        1. Read the distance-table resource for conversion factors.
        2. Use the convert script to compute the result.
        """)
    .AddResource("distance-table", (IServiceProvider sp) =>
    {
        return sp.GetRequiredService<ConversionService>().GetDistanceTable();
    })
    .AddScript("convert", (double value, double factor, IServiceProvider sp) =>
    {
        return sp.GetRequiredService<ConversionService>().Convert(value, factor);
    });

DI を使用したクラスベースのスキル

[AgentSkillResource]または[AgentSkillScript]を使用してメソッドに注釈を付け、IServiceProvider パラメーターを宣言します。フレームワークはリフレクションを介してこれらのメンバーを検出し、サービス プロバイダーを自動的に挿入します。

internal sealed class WeightConverterSkill : AgentClassSkill<WeightConverterSkill>
{
    public override AgentSkillFrontmatter Frontmatter { get; } = new(
        "weight-converter",
        "Convert between weight units (pounds and kilograms).");

    protected override string Instructions => """
        Use this skill when the user asks to convert between pounds and kilograms.
        1. Read the weight-table resource for conversion factors.
        2. Use the convert script to compute the result.
        """;

    [AgentSkillResource("weight-table")]
    [Description("Lookup table of multiplication factors for weight conversions.")]
    private static string GetWeightTable(IServiceProvider serviceProvider)
    {
        return serviceProvider.GetRequiredService<ConversionService>().GetWeightTable();
    }

    [AgentSkillScript("convert")]
    [Description("Multiplies a value by a conversion factor and returns the result as JSON.")]
    private static string Convert(double value, double factor, IServiceProvider serviceProvider)
    {
        return serviceProvider.GetRequiredService<ConversionService>().Convert(value, factor);
    }
}

ヒント

クラスベースのスキルは、 コンストラクターを介して依存関係を解決することもできます。 ServiceCollectionにスキル クラスを登録し、newを直接呼び出す代わりにコンテナーから解決します。

services.AddSingleton<WeightConverterSkill>();
var weightSkill = serviceProvider.GetRequiredService<WeightConverterSkill>();

これは、スキル クラス自体が、リソースおよびスクリプト デリゲートが使用するものを超えるサービスを挿入する必要がある場合に便利です。

**kwargsを受け入れるリソース関数とスクリプト関数は、agent.run()に渡されたランタイム キーワード引数を自動的に受け取ります。 これにより、スキル関数は、構成、ユーザー ID、サービス クライアントなどのアプリケーション コンテキストにアクセスでき、スキル定義にハードコーディングする必要はありません。

ランタイム引数の受け渡し

function_invocation_kwargsagent.run()に渡して、フレームワークがリソースおよびスクリプト関数に転送するキーワード引数を指定します。

response = await agent.run(
    "How many kilometers is 26.2 miles?",
    function_invocation_kwargs={"precision": 2, "user_id": "alice"},
)

kwargs を使用したコード定義スキル

リソース関数が **kwargsを宣言すると、フレームワークは、エージェントがリソースを読み取るたびにランタイム キーワード引数を転送します。

import os
from typing import Any
from agent_framework import InlineSkill, SkillFrontmatter

project_info_skill = InlineSkill(
    frontmatter=SkillFrontmatter(
        name="project-info",
        description="Project status and configuration information",
    ),
    instructions="Use this skill for questions about the current project.",
)

@project_info_skill.resource(name="environment", description="Current environment configuration")
def environment(**kwargs: Any) -> str:
    """Return environment config, optionally scoped to a user."""
    user_id = kwargs.get("user_id", "anonymous")
    env = os.environ.get("APP_ENV", "development")
    return f"Environment: {env}, Caller: {user_id}"

**kwargsを持たないリソース関数は、引数なしで呼び出され、ランタイム コンテキストを受け取りません。

スクリプト関数が **kwargs を宣言すると、フレームワークはランタイム キーワード引数をエージェントによって提供される args と共に転送します。

import json
from typing import Any
from agent_framework import InlineSkill, SkillFrontmatter

converter_skill = InlineSkill(
    frontmatter=SkillFrontmatter(
        name="unit-converter",
        description="Convert between common units using a conversion factor",
    ),
    instructions="Use the convert script to perform unit conversions.",
)

@converter_skill.script(name="convert", description="Convert a value: result = value × factor")
def convert_units(value: float, factor: float, **kwargs: Any) -> str:
    """Convert a value using a multiplication factor.

    Args:
        value: The numeric value to convert (provided by the agent).
        factor: Conversion factor (provided by the agent).
        **kwargs: Runtime keyword arguments from agent.run().
    """
    precision = kwargs.get("precision", 4)
    result = round(value * factor, precision)
    return json.dumps({"value": value, "factor": factor, "result": result})

エージェントは、ツール呼び出しvalueを介してfactorargsを提供します。アプリケーションは、precisionを介してfunction_invocation_kwargsを提供します。 **kwargsを持たないスクリプト関数は、エージェントによって提供される引数のみを受け取ります。

kwargs を使用したクラスベースのスキル

クラス ベースのスキル メソッドは、ランタイム引数を受け取る **kwargs を受け入れることもできます。 パターンは同じように動作します。リソース メソッドまたはスクリプト メソッドで **kwargs を宣言します。

from typing import Any
from agent_framework import ClassSkill, SkillFrontmatter

class WeightConverterSkill(ClassSkill):
    def __init__(self) -> None:
        super().__init__(
            frontmatter=SkillFrontmatter(
                name="weight-converter",
                description="Convert between weight units (pounds and kilograms).",
            ),
        )

    @property
    def instructions(self) -> str:
        return "Use this skill to convert between pounds and kilograms."

    @ClassSkill.resource(name="weight-table")
    def get_weight_table(self, **kwargs: Any) -> str:
        """Weight conversion factors, scoped to caller context."""
        user_id = kwargs.get("user_id", "anonymous")
        return f"Weight table for {user_id}: | lbs | kg | 0.453592 |"

    @ClassSkill.script(name="convert")
    def convert(self, value: float, factor: float, **kwargs: Any) -> str:
        """Convert a weight value."""
        import json
        precision = kwargs.get("precision", 4)
        result = round(value * factor, precision)
        return json.dumps({"value": value, "factor": factor, "result": result})

セキュリティのベスト プラクティス

エージェント スキルは、プロジェクトに取り込むサードパーティのコードと同様に扱う必要があります。スキル命令はエージェントのコンテキストに挿入され、スキルにはスクリプトを含めることができるため、オープンソースの依存関係に対して行うのと同じレベルのレビューとガバナンスを適用することが不可欠です。

  • 使用前に確認 する - デプロイする前にすべてのスキル コンテンツ (SKILL.md、スクリプト、リソース) を読み取ります。 スクリプトの実際の動作が、指定された意図と一致することを確認します。 安全性ガイドラインのバイパス、データの流出、またはエージェント構成ファイルの変更を試みる敵対的な指示を確認します。
  • ソース信頼 - 信頼できる作成者または検証済みの内部共同作成者からのスキルのみをインストールします。 明確な由来、バージョン管理、アクティブなメンテナンスを備えたスキルやツールを優先します。 人気のあるパッケージを模倣する入力ミスのスキル名を警戒してください。
  • サンドボックス - 分離された環境で実行可能スクリプトを含むスキルを実行します。 ファイルシステム、ネットワーク、システム レベルのアクセスを、スキルに必要なもののみに制限します。 機密性の高い操作を実行する前に、明示的なユーザー確認が必要です。
  • 監査とログ 記録 - 読み込まれるスキル、読み取られたリソース、実行されるスクリプトを記録します。 これにより、問題が発生した場合にエージェントの動作を特定のスキル コンテンツに戻す監査証跡が提供されます。

スキルとワークフローを使用するタイミング

エージェント スキルと エージェント フレームワーク ワークフロー はどちらもエージェントが実行できる機能を拡張しますが、基本的に異なる方法で動作します。 要件に最も適したアプローチを選択します。

  • 制御 - スキルを使用して、AI は命令の実行方法を決定します。 これは、エージェントをクリエイティブまたはアダプティブにする場合に最適です。 ワークフローでは、実行パスを明示的に定義します。 確定的で予測可能な動作が必要な場合は、ワークフローを使用します。
  • 回復性 - スキルは 1 つのエージェント ターン内で実行されます。 何かが失敗した場合は、操作全体を再試行する必要があります。 ワークフローは チェックポイント処理をサポートしているため、失敗後に最後に成功した手順から再開できます。 プロセス全体の再実行コストが高い場合は、ワークフローを選択します。
  • 副作用 - スキルは、操作が冪等または低リスクである場合に適しています。 再試行時に繰り返してはならない副作用 (電子メールの送信、支払いの請求) がステップによって発生する場合は、ワークフローを優先します。
  • 複雑さ - スキルは、1 つのエージェントが処理できる集中型の単一ドメイン タスクに最適です。 ワークフローは、複数のエージェント、人間の承認、または外部システム統合を調整する複数ステップのビジネス プロセスに適しています。

ヒント

経験則として、AI にタスクの実行 方法 を把握させる場合は、スキルを使用します。 実行するステップと順序を保証する必要がある場合は、ワークフローを使用します。

次のステップ