Навыки агента

Навыки агента — это переносимые пакеты инструкций, скриптов и ресурсов, которые предоставляют агентам специализированные возможности и опыт работы с доменом. Навыки соответствуют открытой спецификации и реализуют прогрессивный шаблон раскрытия, чтобы агенты загружали только нужный контекст, когда им нужен.

Используйте навыки агента, если требуется:

  • Упакуйте отраслевую экспертизу — представьте специализированные знания (правила учета расходов, юридические процессы, конвейеры анализа данных) в виде многократно используемых переносимых пакетов.
  • Расширение возможностей агента— предоставление агентам новых возможностей без изменения основных инструкций.
  • Обеспечьте согласованность — превратите многоэтапные задачи в повторяемые рабочие процессы, поддающиеся аудиту.
  • Обеспечьте совместимость — Используйте один и тот же навык повторно в разных продуктах, поддерживающих Agent Skills.

Структура навыка

Умение — это каталог, в котором находится 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, за которой следует содержимое markdown:

---
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"
---
Поле Обязательно Description
name Да Максимум 64 символов. Только строчные буквы, цифры и дефисы. Не должно начинаться или заканчиваться дефисом или содержать последовательные дефисы. Должно соответствовать имени родительского каталога.
description Да Что делает навык и когда его использовать. Максимум 1024 символов. Следует включать ключевые слова, помогающие агентам определять соответствующие задачи.
license нет Имя лицензии или ссылка на пакетный файл лицензии.
compatibility нет Максимум 500 символов. Указывает требования к среде (предназначенный продукт, системные пакеты, сетевой доступ и т. д.).
metadata нет Произвольное сопоставление значений ключа для дополнительных метаданных.
allowed-tools нет Список предварительно утвержденных инструментов, разделённый пробелами, которые могут использоваться умением. Экспериментальная поддержка может отличаться от реализаций агента.

Текст markdown после frontmatter содержит инструкции по навыку— пошаговые инструкции, примеры входных и выходных данных, распространенные пограничные варианты или любое содержимое, которое помогает агенту выполнять задачу. Держите SKILL.md в пределах 500 строк и перемещайте подробные справочные материалы в отдельные файлы.

Прогрессивное раскрытие информации

Навыки агента используют четырехэтапный прогрессивный шаблон раскрытия для минимизации использования контекста:

  1. Объявление (~100 токенов на навык) — имена и описания навыков внедряются в системный запрос в начале каждого запуска, поэтому агент знает, какие навыки доступны.
  2. Загрузка (< рекомендуется 5000 маркеров) — когда задача соответствует домену навыка, агент вызывает load_skill средство для получения полного SKILL.md текста с подробными инструкциями.
  3. Чтение ресурсов (по мере необходимости) — агент вызывает read_skill_resource средство для получения дополнительных файлов (ссылок, шаблонов, ресурсов) только при необходимости.
  4. Запуск скриптов (при необходимости) — агент вызывает средство run_skill_script для выполнения скриптов, входящих в состав навыка.

Этот паттерн сохраняет окно контекста агента компактным, предоставляя ему доступ к обширным специальным знаниям по требованию.

Замечание

load_skill всегда рекламируется. read_skill_resource рекламируется только в том случае, если хотя бы у одного навыка есть ресурсы. run_skill_script объявляется только в том случае, если по крайней мере один навык имеет скрипты.

Предоставление навыков агенту

AgentSkillsProvider (C#) и SkillsProvider (Python) — это поставщики контекстов, которые делают навыки доступными для агентов. Они поддерживают три источника навыков:

  • На основе файлов — навыки, обнаруженные из SKILL.md файлов в каталогах файловой системы
  • Code-defined — навыки, определенные в коде с помощью AgentInlineSkill (C#) или InlineSkill (Python)
  • Class-based — навыки, инкапсулированные в класс, производный от AgentClassSkill<T> (C#) или ClassSkill (Python)

Чтобы объединить несколько источников в одном поставщике, используйте AgentSkillsProviderBuilder (C#) или скомпонуйте классы источников, такие как AggregatingSkillsSource, FilteringSkillsSource и DeduplicatingSkillsSource (Python), — см. Builder: расширенные сценарии с несколькими источниками (C#) или Компоновка источников: расширенные сценарии с несколькими источниками (Python).

Навыки, связанные с файлами

Создайте указатель 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);

Предупреждение

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"),
    ]);

Поставщик выполняет поиск до глубины двух уровней.

Настройка обнаружения ресурсов

По умолчанию поставщик распознает ресурсы с расширениями.md, .json, .yaml, .yml, .csvи .xml.txt в referencesassets подкаталогах. Используйте AgentFileSkillsSourceOptions для изменения этих значений по умолчанию:

var fileOptions = new AgentFileSkillsSourceOptions
{
    AllowedResourceExtensions = [".md", ".txt"],
    ResourceDirectories = ["docs", "templates"],
};

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

Выполнение сценария

Передайте SubprocessScriptRunner.RunAsync как второй аргумент в AgentSkillsProvider, чтобы включить выполнение скриптов на основе файлов.

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<string> RunAsync(
    AgentFileSkill skill,
    AgentFileSkillScript script,
    JsonElement? args,
    IServiceProvider? serviceProvider)
{
    var psi = new ProcessStartInfo("python3")
    {
        RedirectStandardOutput = true,
        UseShellExecute = false,
    };
    psi.ArgumentList.Add(Path.Combine(skill.Path, script.Path));
    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();
    await process.WaitForExitAsync();
    return output.Trim();
}

Выполняющая среда запускает каждый обнаруженный скрипт как локальный подпроцесс. Скрипты на основе файлов ожидают аргументы в виде массива строк JSON— каждый элемент массива становится позициальным аргументом командной строки.

Предупреждение

SubprocessScriptRunner предоставляется только для демонстрационных целей. Для использования в рабочей среде рекомендуется добавить:

  • Сэндбоксинг (например, контейнеры или изолированные среды выполнения)
  • Ограничения ресурсов (процессор, память, тайм-аут по времени)
  • Проверка входных данных и перечисление исполняемых скриптов
  • Структурированные журналы и следы аудита

Настройка обнаружения скриптов

По умолчанию поставщик распознает скрипты с расширениями.py, , .js.sh, , .ps1и .cs.csx в подкаталогеscripts. Используйте AgentFileSkillsSourceOptions для изменения этих значений по умолчанию:

Передайте AgentFileSkillsSourceOptions конструктору или AgentSkillsProviderUseFileSkill / UseFileSkills в построителе:

var fileOptions = new AgentFileSkillsSourceOptions
{
    AllowedScriptExtensions = [".py"],
    ScriptDirectories = ["scripts", "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();

Навыки, связанные с файлами

Используйте фабрику 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",
    ]
)

Поставщик выполняет поиск до глубины двух уровней.

Настройка обнаружения ресурсов и скриптов

По умолчанию ресурсы обнаруживаются в подкаталогах references/ и assets/, а скрипты — в scripts/, в соответствии со спецификацией agentskills.io. Распознаваемыми расширениями ресурсов являются .md, .json, .yaml, .yml, .csv, .xml и .txt. Используйте resource_directories, script_directories и resource_extensions, чтобы настроить эти значения по умолчанию:

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    resource_extensions=(".md", ".txt"),
    resource_directories=["docs", "templates"],
    script_directories=["scripts", "tools"],
)

Используйте "." для включения файлов на корневом уровне навыка в дополнение к подкаталогам.

Выполнение сценария

Чтобы включить выполнение скриптов на основе файлов, передайте script_runner в SkillsProvider.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,
)

Исполнитель получает обработанные аргументы FileSkill, FileSkillScript и необязательный аргумент args. Скрипты на основе файлов ожидают аргументы в виде массива строк JSON— каждый элемент массива становится позициальным аргументом командной строки. Скрипты автоматически обнаруживаются в файлах .py в подкаталоге scripts/ каждого каталога навыка.

Предупреждение

Приведенный выше бегун предоставляется только для демонстрационных целей. Для использования в рабочей среде рекомендуется добавить:

  • Sandboxing (например, контейнеры, seccomp или firejail)
  • Ограничения ресурсов (процессор, память, тайм-аут по времени)
  • Проверка входных данных и перечисление исполняемых скриптов
  • Структурированные журналы и следы аудита

Замечание

Если предоставляются навыки на основе файлов со скриптами, но значение 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);

Замечание

Чтобы объединить навыки, определяемые кодом, с навыками на основе файлов или классов в одном поставщике, используйте AgentSkillsProviderBuilder — см. Builder: расширенные сценарии с несколькими источниками.

Помимо навыков на основе файлов, обнаруженных из файлов SKILL.md, можно полностью определить навыки в коде Python с помощью InlineSkill. Определяемые кодом навыки полезны при следующих случаях:

  • Содержимое навыка создается динамически (для примера, чтение из базы данных или окружающей среды).
  • Вы хотите сохранить определения навыков вместе с кодом приложения, который использует их.
  • Вам нужны ресурсы, выполняющие логику во время чтения, а не обслуживающие статические файлы.
  • Определения навыков необходимо создавать во время выполнения из данных , например создание персонализированного навыка для каждого сеанса пользователя на основе их роли или разрешений.
  • Навык должен закрыть состояние сайта вызова (локальные переменные, закрытия), а не разрешать службы через **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), имя функции становится именем ресурса, а докстринг становится описанием. Используйте @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, которую агент использует для передачи аргументов.

Навыки на базе классов

Навыки на основе классов позволяют объединить все компоненты навыка — имя, описание, инструкции, ресурсы и скрипты — в один класс C#. Это позволяет легко упаковывать и распространять их как пакеты NuGet — команды могут независимо создавать и публиковать навыки, а пользователи добавляют их с помощью dotnet add package и одного вызова .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> также поддерживает переопределение Resources и Scripts в качестве коллекций для случаев, когда обнаружение на основе атрибутов не подходит.

Навыки на базе классов

Навыки на основе классов позволяют объединить все компоненты навыка — имя, описание, инструкции, ресурсы и скрипты — в один класс Python. Это позволяет легко упаковывать и распространять их в виде пакетов PyPI. Команды могут независимо создавать и публиковать навыки, а пользователи — добавлять их с помощью pip install и одного вызова 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 используется как декоратор без аргументов, имя метода становится именем ресурса (где символы подчёркивания заменяются дефисами), а строка документации становится его описанием. Используйте @ClassSkill.resource(name="...", description="..."), чтобы задать их явно. Тот же шаблон применяется к @ClassSkill.script.

Ресурсы могут быть определены либо как обычные методы, либо как @property дескрипторы. При использовании @propertyпоместите @property первое и @ClassSkill.resource второе. Возвращаемые значения ресурсов кешируются после первого обращения.

Замечание

ClassSkill также поддерживает явное переопределение свойств resources и scripts, чтобы они напрямую возвращали экземпляры InlineSkillResource и InlineSkillScript, в случаях, когда обнаружение на основе декораторов не подходит.

Конструктор: расширенные сценарии с несколькими источниками

Для простых сценариев с одним исходным кодом используйте AgentSkillsProvider конструкторы напрямую. Используйте AgentSkillsProviderBuilder, если вам потребуется следующее:

  • Смешанные типы навыков — объединение навыков на основе файлов, определяемых кодом (AgentInlineSkill) и навыков на основеAgentClassSkill классов в одном поставщике.
  • Фильтрация навыков — включение или исключение навыков с помощью предиката.

Смешанные типы навыков

Объедините все три типа навыков в одном поставщике, используя UseFileSkill, UseSkill и UseFileScriptRunner.

var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))  // file-based skills
    .UseSkill(volumeConverterSkill)                                  // AgentInlineSkill
    .UseSkill(temperatureConverter)                                  // AgentClassSkill
    .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 => approvedSkillNames.Contains(skill.Frontmatter.Name))
    .Build();

Исходная композиция: расширенные сценарии с несколькими источниками

Для простых сценариев с одним навыком или списком навыков передайте их непосредственно конструктору SkillsProvider . Для навыков на основе файлов используйте фабрику SkillsProvider.from_paths() . Для расширенных сценариев создайте исходные классы для управления обнаружением, фильтрацией и дедупликацией:

  • FileSkillsSource — обнаруживает навыки из SKILL.md файлов на диске.
  • InMemorySkillsSource — оборачивает в памяти все экземпляры Skill (заданные в коде или на основе класса).
  • AggregatingSkillsSource — объединяет несколько источников в один.
  • FilteringSkillsSource — применяет предикат для включения или исключения навыков.
  • DeduplicatingSkillsSource — удаляет повторяющиеся имена навыков (без учета регистра, первый выигрыш).

Смешанные типы навыков

Объедините навыки на основе файлов, определяемые в коде и на основе классов, в одном поставщике с помощью AggregatingSkillsSource. В приведенном ниже примере используются объекты заполнителей:

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",
        )
    )
)

Утверждение скрипта

Используйте AgentSkillsProviderOptions.ScriptApproval, чтобы ограничить выполнение всех скриптов до получения одобрения человека. Если этот параметр включен, агент приостанавливается и отправляет запрос на утверждение вместо немедленного выполнения.

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

Чтобы включить утверждение скрипта на поставщике, сконфигурированном построителем, используйте UseScriptApproval:

var skillsProvider = new AgentSkillsProviderBuilder()
    .UseFileSkill(Path.Combine(AppContext.BaseDirectory, "skills"))
    .UseScriptApproval(true)
    .Build();

Используйте require_script_approval=True на SkillsProvider для ограничения всех выполнения скриптов человеком. Вместо немедленного выполнения агент приостанавливает и возвращает запросы на утверждение с помощью 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_skill и read_skill_resource. Этот запрос можно настроить:

var skillsProvider = new AgentSkillsProvider(
    skillPath: Path.Combine(AppContext.BaseDirectory, "skills"),
    options: new AgentSkillsProviderOptions
    {
        SkillsInstructionPrompt = """
            You have skills available. Here they are:
            {skills}
            {resource_instructions}
            {script_instructions}
            """
    });

Замечание

Настраиваемый шаблон должен содержать {skills} (местоимения списка навыков), {resource_instructions} (подсказка инструментов ресурсов) и {script_instructions} (подсказка инструментов сценариев). Литеральные фигурные скобки должны быть экранированы как {{ и }}.

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} (подсказка инструментов сценариев). Литеральные фигурные скобки должны быть экранированы как {{ и }}.

Поведение кэширования

По умолчанию инструменты навыков и инструкции сохраняются в кэше после первой сборки. Установите DisableCaching = true на AgentSkillsProviderOptions, чтобы принудительно перестраивать с каждым вызовом:

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

Замечание

Отключение кэширования полезно во время разработки при частом изменении контента навыка. В рабочей среде оставьте кэширование включено (по умолчанию) для повышения производительности.

Поведение кэширования

По умолчанию инструменты навыков и инструкции сохраняются в кэше после первой сборки. Задайте disable_caching=True, чтобы принудительно выполнять пересборку при каждом вызове:

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

disable_caching также доступен в конструкторе SkillsProvider для навыков, определяемых в коде и на основе классов.

Замечание

Отключение кэширования полезно во время разработки при частом изменении контента навыка. В рабочей среде оставьте кэширование включено (по умолчанию) для повышения производительности.

Делегаты ресурсов и скриптов навыка могут объявлять IServiceProvider параметр, который Agent Framework автоматически внедряет. Это позволяет навыкам получать доступ к службам приложения — таким как клиенты баз данных, параметры конфигурации или бизнес-логика — без необходимости жёстко прописывать их в определении навыка.

Setup

Зарегистрируйте службы приложений и передайте созданный 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

Объявите IServiceProvider как параметр в делегатах AddResource или 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() аргументы ключевых слов среды выполнения. Это позволяет функциям навыка получать доступ к контексту приложения, такому как конфигурация, идентификационные данные пользователя или клиенты сервисов, без их жёсткого прописывания в определении навыка.

Передача аргументов среды выполнения

function_invocation_kwargs Передайте agent.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 и factor через вызов инструмента args; приложение предоставляет 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, скрипты и ресурсы). Убедитесь, что фактическое поведение скрипта соответствует указанному намерению. Проверьте враждебные инструкции, которые пытаются обойти рекомендации по безопасности, эксфильтровать данные или изменить файлы конфигурации агента.
  • Доверие к источнику — устанавливайте навыки только от доверенных авторов или проверенных внутренних разработчиков. Предпочитайте навыки с четким происхождением, управлением версиями и активным обслуживанием. Следите за именами навыков typeosquatted, которые имитируют популярные пакеты.
  • Песочница — выполнение навыков, включающих исполняемые скрипты в изолированных средах. Ограничить доступ к файловой системе, сети и системе только тому, что требуется навыку. Перед выполнением потенциально конфиденциальных операций требуется явное подтверждение пользователя.
  • Аудит и ведение журнала — записывайте, какие навыки загружаются, какие ресурсы считываются и какие скрипты выполняются. Это дает путь аудита для трассировки поведения агента обратно к определенному содержимому навыка, если что-то пойдет не так.

Когда использовать навыки и рабочие процессы

Навыки агента и рабочие процессы платформы агента расширяют возможности агентов, но они работают по-разному. Выберите подход, который лучше всего соответствует вашим требованиям:

  • Управление . С помощью навыка ИИ решает, как выполнить инструкции. Это идеально, если вы хотите, чтобы агент был творческим или адаптивным. При использовании рабочего процесса вы явно определяете путь выполнения. Используйте рабочие процессы, если требуется детерминированное, прогнозируемое поведение.
  • Устойчивость — навык выполняется за один шаг агента. Если что-то завершается сбоем, необходимо повторить всю операцию. Рабочие процессы поддерживают контрольные точки, чтобы они могли возобновить работу с последнего успешного шага после сбоя. Выберите рабочие процессы, когда стоимость повторного выполнения всего процесса высока.
  • Побочные эффекты — навыки подходят, когда операции идемпотентны или связаны с низким риском. Предпочитайте рабочие процессы, когда шаги создают побочные эффекты (отправка сообщений электронной почты, плата за платежи), которые не должны повторяться при повторных попытках.
  • Сложность — навыки лучше всего подходят для сфокусированных задач в рамках одной предметной области, с которыми может справиться один агент. Рабочие процессы лучше подходят для многоэтапных бизнес-процессов, координирующих нескольких агентов, формирование человеческих утверждений и интеграцию с внешними системами.

Подсказка

Как правило, если вы хотите, чтобы ИИ определил, как выполнить задачу, используйте навыки. Если вам нужно гарантировать выполнение шагов и в каком порядке, используйте рабочий процесс.

Дальнейшие шаги