Habilidades do Agente

As Habilidades do Agente são pacotes portáteis de instruções, scripts e recursos que fornecem aos agentes recursos especializados e experiência de domínio. As habilidades seguem uma especificação aberta e implementam um padrão de divulgação progressiva para que os agentes carreguem apenas o contexto necessário, quando precisarem.

Use as habilidades do agente quando quiser:

  • Empacote o conhecimento de domínio: capture conhecimento especializado (políticas de despesas, fluxos de trabalho jurídicos, pipelines de análise de dados) como pacotes reutilizáveis e portáteis.
  • Estender as funcionalidades do agente – dê aos agentes novas habilidades sem alterar suas instruções principais.
  • Garantir a consistência – transforme tarefas de várias etapas em fluxos de trabalho repetíveis e auditáveis.
  • Habilitar a interoperabilidade – reutilize a mesma habilidade em diferentes produtos compatíveis com habilidades de agente.

Estrutura de habilidades

Uma habilidade é um diretório que contém um SKILL.md arquivo com subdiretórios opcionais para recursos:

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

formato SKILL.md

O SKILL.md arquivo deve conter o frontmatter YAML seguido pelo conteúdo de 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"
---
Campo Obrigatório DESCRIÇÃO
name Yes Máximo de 64 caracteres. Letras minúsculas, números e hifens somente. Não deve iniciar ou terminar com um hífen ou conter hifens consecutivos. Deve corresponder ao nome do diretório pai.
description Yes O que a habilidade faz e quando usá-la. Máximo de 1024 caracteres. Deve incluir palavras-chave que ajudam os agentes a identificar tarefas relevantes.
license Não Nome da licença ou referência a um arquivo de licença empacotado.
compatibility Não Máximo de 500 caracteres. Indica os requisitos de ambiente (produto pretendido, pacotes do sistema, acesso à rede etc.).
metadata Não Mapeamento arbitrário de chave-valor para metadados adicionais.
allowed-tools Não Lista delimitada por espaço de ferramentas pré-aprovadas que a habilidade pode usar. Experimental – o suporte pode variar entre implementações de agente.

O corpo de markdown após o página inicial contém as instruções de habilidade: orientação passo a passo, exemplos de entradas e saídas, casos de borda comuns ou qualquer conteúdo que ajude o agente a executar a tarefa. Mantenha SKILL.md com menos de 500 linhas e transfira o material detalhado de referência para arquivos separados.

Divulgação progressiva

As Habilidades do Agente usam um padrão de divulgação progressiva de quatro estágios para minimizar o uso de contexto:

  1. Anunciar (aproximadamente 100 tokens por habilidade) – nomes de habilidade e descrições são injetados no prompt do sistema no início de cada execução, para que o agente saiba quais habilidades estão disponíveis.
  2. Carga (< 5000 tokens recomendados), quando uma tarefa corresponde ao domínio de uma habilidade, o agente chama a ferramenta load_skill para recuperar o conteúdo completo do SKILL.md com instruções detalhadas.
  3. Ler recursos (conforme necessário) – O agente chama a read_skill_resource ferramenta para buscar arquivos suplementares (referências, modelos, ativos) somente quando necessário.
  4. Executar os scripts (conforme necessário), o agente chama a ferramenta run_skill_script para executar scripts incluídos em uma habilidade.

Esse padrão mantém a janela de contexto do agente enxuta, dando-lhe acesso ao conhecimento profundo do domínio sob demanda.

Observação

load_skill é sempre anunciado. read_skill_resource é anunciado apenas quando pelo menos uma habilidade tem recursos. run_skill_script é anunciado apenas quando pelo menos uma habilidade tem scripts.

Fornecendo habilidades a um agente

Trabalhar com habilidades envolve três blocos de construção:

  • Provedor - AgentSkillsProvider (C#) ou SkillsProvider (Python) é um provedor de contexto que expõe habilidades a um agente. Ele anuncia as habilidades disponíveis no prompt do sistema e registra as ferramentas que o agente usa para carregar habilidades, ler recursos e executar scripts.
  • Fontes – uma fonte fornece habilidades para o provedor. As habilidades podem vir de vários tipos de origem:
    • Baseado em arquivos - habilidades descobertas em arquivos SKILL.md em diretórios do sistema de arquivos.
    • Definido por código – habilidades definidas embutidas no código usando AgentInlineSkill (C#) ou InlineSkill (Python).
    • Baseado em classe – habilidades encapsuladas em uma classe derivada de AgentClassSkill<T> (C#) ou ClassSkill (Python).
    • Baseadas em MCP - habilidades descobertas a partir de servidores MCP (Model Context Protocol) por meio de UseMcpSkills (C#) ou MCPSkillsSource (Python).
  • Construtor - AgentSkillsProviderBuilder (C#) reúne várias fontes em um único provedor, aplicando agregação, eliminação de duplicação, cache e filtragem opcional. Em Python, redigir classes de origem, como AggregatingSkillsSource, FilteringSkillsSourcee DeduplicatingSkillsSource diretamente.

As seções a seguir mostram como criar habilidades de cada tipo de origem e, em seguida, como combinar fontes e construir um provedor a partir delas.

Habilidades baseadas em arquivo

Crie um AgentSkillsProvider apontando para um diretório que contenha suas habilidades e adicione-o aos provedores de contexto do agente. Passe um executor de script para habilitar a execução de scripts baseados em arquivo encontrados em diretórios de habilidades:

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);

Aviso

DefaultAzureCredential é conveniente para o desenvolvimento, mas requer uma consideração cuidadosa na produção. Em produção, considere o uso de uma credencial específica (por exemplo, ManagedIdentityCredential) para evitar problemas de latência, investigação de credenciais não intencionais e possíveis riscos de segurança de mecanismos de fallback.

Vários diretórios de habilidades

Você pode apontar o provedor para um único diretório pai, cada subdiretório que contém um SKILL.md é descoberto automaticamente como uma habilidade:

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

Ou passe uma lista de caminhos para pesquisar vários diretórios raiz:

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

O provedor pesquisa em até dois níveis de profundidade.

Personalizando a descoberta de recursos e scripts

Por padrão, o provedor reconhece recursos com extensões .md, , .json, .yaml, .yml, .csv, .xmle .txt scripts com extensões .py, .js, .sh, , .ps1, .cse .csx. Ele pesquisa até dois níveis profundos em cada diretório de habilidades. Use AgentFileSkillsSourceOptions para alterar esses padrões:

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 e ScriptFilter receba um AgentFileSkillFilterContext com o nome da habilidade e o caminho relativo do arquivo, permitindo que você restrinja arquivos por local, convenção de nomenclatura ou qualquer lógica personalizada.

Execução do script

Passe SubprocessScriptRunner.RunAsync como o executor de script para habilitar a execução de scripts baseados em arquivo:

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

SubprocessScriptRunner.RunAsync é aproximadamente equivalente ao seguinte:

// 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();
}

O executor executa cada script descoberto como um subprocesso local. Os scripts baseados em arquivo esperam argumentos como uma matriz JSON de cadeias de caracteres – cada elemento de matriz se torna um argumento de linha de comando posicional.

Aviso

SubprocessScriptRunner é fornecido somente para fins de demonstração. Para uso em produção, considere adicionar:

  • Sandboxing (por exemplo, contêineres ou ambientes de execução isolados)
  • Limites de recursos (CPU, memória, tempo limite do relógio de parede)
  • Validação de entrada e lista de permissões de scripts executáveis
  • Registros e trilhas de auditoria estruturadas

Habilidades baseadas em arquivo

Use a fábrica SkillsProvider.from_paths() para descobrir habilidades em diretórios que contêm arquivos SKILL.md e adicione o provedor aos provedores de contexto do agente:

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

Vários diretórios de habilidades

Você pode apontar o provedor para um único diretório pai, cada subdiretório que contém um SKILL.md é descoberto automaticamente como uma habilidade:

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

Ou passe uma lista de caminhos para pesquisar vários diretórios raiz:

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

O provedor pesquisa em até dois níveis de profundidade.

Personalizando a descoberta de recursos e scripts

Por padrão, os recursos são descobertos nos subdiretórios references/ e assets/, e os scripts em scripts/, conforme a especificação agentskills.io. As extensões de recurso reconhecidas são .md, .json, .yaml, .yml, .csv, .xml e .txt. Use resource_extensions, script_extensions, resource_filter e script_filter para personalizar a descoberta:

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

Use "." para incluir arquivos no nível raiz da habilidade, além de subdiretórios.

Execução do script

Para habilitar a execução de scripts baseados em arquivo, passe um script_runner para SkillsProvider.from_paths(). Qualquer sincronização ou chamada assíncrona que atenda ao SkillScriptRunner protocolo pode ser usada:

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

O executor recebe os argumentos resolvidos FileSkill, FileSkillScript e um argumento opcional args. Os scripts baseados em arquivo esperam argumentos como uma matriz JSON de cadeias de caracteres – cada elemento de matriz se torna um argumento de linha de comando posicional. Os scripts são descobertos automaticamente a partir de .py arquivos no scripts/ subdiretório de cada diretório de habilidades.

Aviso

O executor acima é fornecido apenas para fins de demonstração. Para uso em produção, considere adicionar:

  • Sandboxing (por exemplo, contêineres seccomp ou firejail)
  • Limites de recursos (CPU, memória, tempo limite do relógio de parede)
  • Validação de entrada e lista de permissões de scripts executáveis
  • Registros e trilhas de auditoria estruturadas

Observação

Se habilidades baseadas em arquivos com scripts forem fornecidas, mas script_runner não estiver definido, SkillsProvider gerará um erro quando se tentar executar o script.

Habilidades definidas por código

Além das habilidades baseadas em arquivo descobertas nos SKILL.md arquivos, você pode definir habilidades inteiramente no código usando AgentInlineSkill. As habilidades definidas por código são úteis quando:

  • O conteúdo da habilidade é gerado dinamicamente (por exemplo, leitura de um banco de dados ou ambiente).
  • Você deseja manter definições de habilidade junto com o código do aplicativo que as usa.
  • Você precisa de recursos que executem a lógica em tempo de leitura em vez de servir arquivos estáticos.
  • As definições de habilidades precisam ser construídas em runtime a partir de dados , por exemplo, criar uma habilidade personalizada para cada sessão de usuário com base em sua função ou permissões.
  • Uma habilidade precisa ser fechada sobre o estado do site de chamada (variáveis locais, fechamentos) em vez de resolver serviços de um contêiner de DI.

Habilidade básica de código

Crie um AgentInlineSkill com um nome, uma descrição e instruções. Anexar recursos usando .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);

Recursos dinâmicos

Passe um delegado de fábrica para .AddResource() calcular o conteúdo em runtime. O delegado é invocado sempre que o agente lê o recurso:

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

Scripts definidos por código

Use .AddScript() para registrar um delegado como um script executável. Scripts definidos por código são executados em processo como chamadas de delegado direto. Nenhum executor de script é necessário. Os parâmetros digitados do delegado são automaticamente convertidos em um esquema JSON que o agente usa para passar argumentos:

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);

Observação

Para combinar habilidades definidas por código com habilidades baseadas em arquivo ou baseadas em classe em um único provedor, use AgentSkillsProviderBuilder - consulte a construção do provedor.

Além das habilidades baseadas em arquivos descobertas em arquivos SKILL.md, você pode definir habilidades inteiramente em código Python usando InlineSkill. As habilidades definidas por código são úteis quando:

  • O conteúdo da habilidade é gerado dinamicamente (por exemplo, leitura de um banco de dados ou ambiente).
  • Você deseja manter definições de habilidade junto com o código do aplicativo que as usa.
  • Você precisa de recursos que executem a lógica em tempo de leitura em vez de servir arquivos estáticos.
  • As definições de habilidades precisam ser construídas em runtime a partir de dados , por exemplo, criar uma habilidade personalizada para cada sessão de usuário com base em sua função ou permissões.
  • Uma habilidade precisa capturar o estado no local da chamada (variáveis locais, closures) em vez de resolver serviços por meio de **kwargs.

Habilidade básica de código

Crie uma InlineSkill instância com um SkillFrontmatter (contendo o nome e a descrição) e o conteúdo da instrução. Opcionalmente, anexe InlineSkillResource instâncias com conteúdo estático:

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)

Recursos dinâmicos

Use o @skill.resource decorador para registrar uma função como um recurso. A função é chamada cada vez que o agente lê o recurso, para que possa retornar dados atualizados. Há suporte para funções de sincronização e assíncronas:

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

Quando o decorador é usado sem argumentos (@skill.resource), o nome da função se torna o nome do recurso e o docstring se torna a descrição. Use @skill.resource(name="...", description="...") para defini-los explicitamente.

Scripts definidos por código

Use o @skill.script decorador para registrar uma função como um script executável em uma habilidade. Os scripts definidos por código são executados em processo e não exigem um executor de script. Há suporte para funções de sincronização e assíncronas:

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})

Quando o decorador é usado sem argumentos (@skill.script), o nome da função se torna o nome do script e o docstring se torna a descrição. Os parâmetros tipados da função são convertidos automaticamente num esquema JSON que o agente usa para passar argumentos.

Habilidades baseadas em classe

As habilidades baseadas em classe permitem que você agrupe todos os componentes de habilidades – nome, descrição, instruções, recursos e scripts – em uma única classe C#. Isso os torna fáceis de empacotar e distribuir como pacotes NuGet, as equipes podem criar e enviar habilidades de forma independente e os consumidores as adicionam com dotnet add package e uma única .UseSkill() chamada. Derivar de AgentClassSkill<T> (onde T é sua classe), em seguida, anotar propriedades com [AgentSkillResource] e métodos com [AgentSkillScript] para descoberta automática.

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 });
    }
}

Registre a habilidade baseada em classe com AgentSkillsProvider:

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

Quando o [AgentSkillResource] atributo é aplicado a uma propriedade ou método, seu valor retornado é usado como o conteúdo do recurso quando o agente lê o recurso – use um método quando o conteúdo precisar ser computado em tempo de leitura. Quando [AgentSkillScript] é aplicado a um método, o método é invocado quando o agente chama o script. Use [Description] de System.ComponentModel para descrever cada recurso e script para o agente.

Observação

AgentClassSkill<T> também oferece suporte à substituição de Resources e Scripts como coleções para cenários em que a descoberta baseada em atributos não é adequada.

Habilidades baseadas em classe

As habilidades baseadas em classe permitem agrupar todos os componentes de habilidade - nome, descrição, instruções, recursos e scripts - em uma única classe Python. Isso os torna fáceis de empacotar e distribuir como pacotes PyPI, as equipes podem criar e enviar habilidades de forma independente e os consumidores as adicionam com pip install e uma única SkillsProvider() chamada. Crie uma subclasse de ClassSkill e, em seguida, use os decoradores @ClassSkill.resource e @ClassSkill.script para descoberta automática:

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})

Registre a habilidade baseada em classe com SkillsProvider:

from agent_framework import SkillsProvider

skill = UnitConverterSkill()
skills_provider = SkillsProvider(skill)

Quando @ClassSkill.resource é aplicado como um decorador nu (sem argumentos), o nome do método se torna o nome do recurso (com sublinhados convertidos em hifens) e o docstring se torna a descrição. Use @ClassSkill.resource(name="...", description="...") para defini-los explicitamente. O mesmo padrão se aplica a @ClassSkill.script.

Os recursos podem ser definidos como métodos regulares ou @property descritores. Ao usar @property, coloque @property o primeiro e @ClassSkill.resource o segundo. Os valores de retorno de recurso são armazenados em cache após o primeiro acesso.

Observação

ClassSkill também oferece suporte à substituição explícita das propriedades resources e scripts para retornar instâncias de InlineSkillResource e InlineSkillScript diretamente, em cenários nos quais a descoberta baseada em decoradores não é adequada.

Habilidades baseadas em MCP

Observação

As habilidades baseadas em MCP exigem o Microsoft.Agents.AI.Mcp pacote NuGet. A API de habilidades do MCP é experimental e pode mudar em versões futuras.

As habilidades podem ser descobertas em servidores MCP (Protocolo de Contexto do Modelo) que expõem recursos de habilidades no esquema de URI skill://. O servidor MCP anuncia habilidades por meio de um skill://index.json documento de descoberta e a estrutura busca conteúdo de habilidade sob demanda.

As habilidades baseadas em MCP dão suporte a dois tipos de entrada de índice:

  • skill-md - O SKILL.md da habilidade e os recursos relacionados são obtidos sob demanda do servidor MCP.
  • archive - A habilidade é distribuída como um único arquivo empacotado (ZIP, TAR ou TAR compactado por gzip) que é baixado e descompactado localmente.

Uso Básico

Use o método de extensão UseMcpSkills em AgentSkillsProviderBuilder para adicionar uma fonte de habilidades do 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);

Habilidades do tipo arquivo

Para habilidades do tipo arquivo, use AgentMcpSkillsSourceOptions (do pacote Microsoft.Agents.AI.Mcp) para configurar o comportamento de extração:

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 expõe as seguintes propriedades para controlar a extração de arquivos:

  • ArchiveSkillsDirectory - Diretório base para arquivos extraídos. Usa como padrão um subdiretório exclusivo no diretório de trabalho atual, gerado por instância de origem para evitar colisões entre várias fontes.
  • ArchiveResourceExtensions - Extensões permitidas para recursos em arquivos extraídos. O padrão é .md, .json, .yaml, .yml, .csv, .xml, .txt.
  • ArchiveResourceSearchDepth - Qual é a profundidade da pesquisa de recursos em cada diretório de habilidades extraído. Usa 2 como padrão.
  • ArchiveMaxFileCount - Número máximo de arquivos por arquivo compactado. Os arquivos que excedem esse limite são ignorados. Usa 20 como padrão.
  • ArchiveMaxSizeBytes - Tamanho máximo de download por arquivo compactado. Usa 1 MB como padrão.
  • ArchiveMaxUncompressedSizeBytes - Tamanho total máximo não compactado por arquivo compactado. Usa 1 MB como padrão.

Importante

Scripts incluídos em habilidades do tipo arquivo compactado nunca são executados. Essa é uma medida de segurança deliberada – o conteúdo executável de servidores MCP remotos requer confiança explícita.

Fontes de habilidades

Uma AgentSkillsProvider recupera habilidades de uma ou mais fontes – objetos que implementam AgentSkillsSource. As fontes se enquadram em duas categorias: fontes de folha que descobrem ou contêm habilidades (como AgentFileSkillsSource para habilidades baseadas em arquivo), e decoradores que transformam a saída de outra fonte (agregação, eliminação de duplicatas, armazenamento em cache e filtragem). Você também pode criar uma fonte personalizada.

Cada origem implementa um único método – GetSkillsAsync(AgentSkillsSourceContext context, CancellationToken cancellationToken = default). O AgentSkillsSourceContext contém informações sobre a solicitação atual:

  • Agent - a instância AIAgent que solicita habilidades.
  • Session - o AgentSession associado à invocação ou null quando não há sessão.

Esse contexto está disponível em todo o pipeline de origem, portanto, um FilteringAgentSkillsSource predicado ou uma fonte personalizada pode basear sua lógica nele – por exemplo, retornando um conjunto diferente de habilidades dependendo do agente solicitante.

Fontes de folha

AgentFileSkillsSource

Descobre competências de arquivos SKILL.md no disco. Aceita um ou mais caminhos de diretório, um executor de script opcional e um AgentFileSkillsSourceOptions opcional (documentado em habilidades baseadas em arquivos).

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

AgentInMemorySkillsSource

Encapsula em memória instâncias AgentSkill definidas por código ou baseadas em classes.

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

Combinadores

AggregatingAgentSkillsSource

Combina várias fontes em uma. As habilidades são retornadas na ordem de registro, sem remoção de duplicatas nem aplicação de filtragem.

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

Decoradores

Decoradores encapsulam uma fonte interna e transformam sua saída. Eles podem ser encadeados para criar um pipeline.

DeduplicatingAgentSkillsSource

Remove nomes de habilidade duplicados (não diferencia maiúsculas de minúsculas, a primeira ocorrência vence). As duplicatas são registradas no nível de aviso.

var deduplicated = new DeduplicatingAgentSkillsSource(innerSource);

CachingAgentSkillsSource

Armazena em cache a lista de habilidades retornada pela fonte interna. Chamadores simultâneos são serializados por chave de cache, portanto, apenas uma busca é executada por vez. Aceita opcionalmente CachingAgentSkillsSourceOptions:

  • RefreshInterval (TimeSpan?) - quando definidos, os resultados armazenados em cache expiram após esse intervalo e a origem interna é invocada novamente. Quando null (o padrão), os resultados armazenados em cache nunca expiram.
  • CacheIsolationKeySelector (Func<AgentSkillsSourceContext, string?>?) – retorna uma chave de cache para isolar os resultados armazenados em cache por contexto (por exemplo, por locatário). Quando null, todos os chamadores compartilham um único bucket de cache.
var cached = new CachingAgentSkillsSource(innerSource, new CachingAgentSkillsSourceOptions
{
    RefreshInterval = TimeSpan.FromMinutes(5)
});

FilteringAgentSkillsSource

Aplica um predicado para incluir ou excluir habilidades. O predicado recebe a habilidade e um AgentSkillsSourceContext.

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

Fontes personalizadas

Quando as fontes internas não abrangem seu cenário, implemente o seu próprio. Subclasse AgentSkillsSource para uma fonte folha (que produz habilidades de uma nova origem, como um banco de dados ou serviço remoto), ou subclasse DelegatingAgentSkillsSource para um decorador que transforma a saída de outra fonte.

Origem da folha

Derivar de AgentSkillsSource e implementar GetSkillsAsync. O AgentSkillsSourceContext argumento permite que a origem adapte seu resultado à solicitação atual , por exemplo, retornando um conjunto diferente de habilidades, dependendo do agente solicitante. Sobrescreva Dispose(bool) se a fonte tiver recursos como um cliente ou uma conexão.

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);
    }
}

Decorador personalizado

Derive de DelegatingAgentSkillsSource, chame InnerSource.GetSkillsAsync e transforme ou observe o resultado. Este é o mesmo padrão que os decoradores integrados de cache, deduplicação e filtragem usam. Por exemplo, um decorador que registra quantas habilidades foram retornadas por solicitação sem alterar o resultado:

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;
    }
}

Ambas as fontes personalizadas podem ser passadas diretamente para AgentSkillsProvider ou aninhadas em um pipeline maior, assim como as fontes integradas.

Construção do provedor

AgentSkillsProvider é o componente que expõe habilidades a um agente. Ele encapsula uma ou mais fontes e registra as ferramentas load_skill, read_skill_resource e run_skill_script. Há três maneiras de criar uma:

  1. AgentSkillsProviderBuilder - compõe vários tipos de competência em um provedor com agregação automática, remoção de duplicatas, cache e filtragem opcional. Melhor para cenários que combinam habilidades baseadas em arquivo, definidas por código, baseadas em classe e baseadas em MCP.
  2. Composição de origem direta – construa o pipeline de origem por conta própria usando as classes públicas AgentSkillsSource . Nenhum cache automático ou eliminação de duplicação é aplicado – você controla o pipeline completo. Melhor quando você precisa de controle sobre ordenação, lógica condicional ou comportamento personalizado do decorador.
  3. Construtores de conveniência – crie um provedor diretamente de um caminho de arquivo ou instâncias de habilidade. Aplica automaticamente a eliminação de duplicação e o cache. Melhor para cenários de origem única.

Usando AgentSkillsProviderBuilder

Use AgentSkillsProviderBuilder quando precisar de qualquer um dos seguintes:

  • Tipos de habilidades mistas – combinam habilidades baseadas em arquivo, definidas por código (AgentInlineSkill), baseadas em classe (AgentClassSkill) e baseadas em MCP em um único provedor.
  • Filtragem de habilidades – incluir ou excluir habilidades usando um predicado.

Tipos de habilidades mistas

Combine vários tipos de habilidades em um provedor ao encadear UseFileSkill, UseSkill, UseMcpSkills e UseFileScriptRunner:

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();

Filtragem de habilidades

Use UseFilter para incluir apenas as habilidades que atendem aos seus critérios , por exemplo, para carregar habilidades de um diretório compartilhado, mas excluir as experimentais:

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();

Redigindo fontes diretamente

Quando o construtor não oferecer o controle de que você precisa, componha você mesmo as classes de origem e passe o pipeline resultante para AgentSkillsProvider. Consulte as fontes do Skill para obter a lista completa de fontes disponíveis e suas opções.

O exemplo a seguir cria um pipeline comparável com múltiplas fontes, mas permite controle explícito sobre cada decorador:

// 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);

Observação

Quando ownsSource é true, descartar o provedor também descarta todo o pipeline de origem. Defina-o como false se você mesmo gerenciar o ciclo de vida da origem.

Construtores de conveniência

Para cenários de origem única, use os AgentSkillsProvider construtores diretamente. Elas aplicam automaticamente a eliminação de duplicatas e cache sem exigir um builder nem uma composição manual da origem.

A partir de um caminho de arquivo:

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

A partir de instâncias de habilidade:

var skillsProvider = new AgentSkillsProvider(volumeConverterSkill, temperatureConverter);

Composição de origem: cenários avançados de várias fontes

Para cenários simples com uma única habilidade ou uma lista de habilidades, passe-os diretamente para o SkillsProvider construtor. Para as habilidades baseadas em arquivo, use a SkillsProvider.from_paths() fábrica. Para cenários avançados, redigir classes de origem para controlar a descoberta, a filtragem e a eliminação de duplicação:

  • FileSkillsSource – descobre habilidades a partir de arquivos SKILL.md no disco.
  • InMemorySkillsSource - encapsula todas Skill as instâncias (definidas por código ou baseadas em classe) na memória.
  • AggregatingSkillsSource – combina várias fontes em uma.
  • FilteringSkillsSource – aplica um predicado para incluir ou excluir habilidades.
  • DeduplicatingSkillsSource remove os nomes de habilidades duplicados (sem diferenciar maiúsculas de minúsculas, mantendo a primeira ocorrência).

Tipos de habilidades mistas

Combine habilidades baseadas em arquivo, definidas por código e baseadas em classe em um provedor usando AggregatingSkillsSource. O exemplo abaixo usa objetos marcadores:

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

Filtragem de habilidades

Use FilteringSkillsSource para controlar quais habilidades o agente vê. O predicado recebe cada Skill e retorna True para incluí-lo. Por exemplo, para carregar habilidades de um diretório compartilhado, mas ocultar uma experimental:

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

Comportamento de cache

Por padrão, o construtor encapsula o pipeline de origem com um CachingAgentSkillsSource que armazena em cache a lista de habilidades retornadas pelas fontes subjacentes. Depois que as habilidades forem resolvidas na primeira solicitação, as solicitações subsequentes reutilizarão a lista armazenada em cache sem consultar novamente as fontes. Para desabilitar o cache (por exemplo, durante o desenvolvimento, quando as definições de habilidade são alteradas com frequência), use DisableCaching() no construtor:

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

Observação

Desabilitar o cache é útil durante o desenvolvimento quando o conteúdo da habilidade é alterado com frequência. Na produção, deixe o cache habilitado (o padrão) para melhorar o desempenho.

Comportamento de cache

Por padrão, as ferramentas de habilidade e as instruções são armazenadas em cache após o primeiro build. Defina disable_caching=True para forçar uma reconstrução a cada invocação:

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

disable_caching também está disponível no construtor SkillsProvider para habilidades definidas por código e baseadas em classes.

Observação

Desabilitar o cache é útil durante o desenvolvimento quando o conteúdo da habilidade é alterado com frequência. Na produção, deixe o cache habilitado (o padrão) para melhorar o desempenho.

Aprovação da ferramenta

Todas as ferramentas expostas por AgentSkillsProvider (load_skill, read_skill_resource, run_skill_script) exigem aprovação por padrão. Quando uma chamada de ferramenta requer aprovação, o agente pausa e retorna um ToolApprovalRequestContent em vez de executar imediatamente. Use o middleware UseToolApproval com regras de aprovação automática para ignorar seletivamente as solicitações em operações confiáveis:

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();

Para aprovar automaticamente todas as ferramentas de habilidade, incluindo a execução de scripts:

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

Desabilitando a aprovação para ferramentas específicas

Use AgentSkillsProviderOptions para desabilitar a aprovação de ferramentas individuais, removendo-as totalmente do fluxo de aprovação:

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
    });

Quando, na mesma resposta, algumas ferramentas exigem aprovação e outras não, o modelo pode invocar os dois tipos de ferramenta simultaneamente. Defina EnableNonApprovalRequiredFunctionBypassing para que as ferramentas que não exigem aprovação sejam executadas imediatamente, enquanto o usuário receba solicitação apenas para as demais:

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();

Gerenciando solicitações de aprovação

Quando as ferramentas exigem aprovação (e nenhuma regra de aprovação automática se aplica), o agente retorna ToolApprovalRequestContent itens que precisam ser aprovados ou rejeitados antes de continuar:

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();
}

Detalhes do erro de script

Por padrão, quando a execução de um script de habilidade falha, a exceção é propagada para o FunctionInvokingChatClient subjacente. Se sua IncludeDetailedErrors propriedade estiver definida como true, a mensagem de exceção será encaminhada para o modelo, permitindo que ela se auto-corrija repetindo com argumentos diferentes:

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());

Se você não puder configurar FunctionInvokingChatClient diretamente, defina AgentSkillsProviderOptions.IncludeDetailedErrors em vez disso. Isso captura a exceção no nível do provedor de habilidades e retorna a mensagem de erro diretamente para o modelo:

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

Aviso

Qualquer abordagem pode divulgar detalhes brutos de exceção para o modelo. As mensagens de exceção podem conter informações confidenciais, como cadeias de conexão, caminhos de arquivo ou nomes de serviço internos. Além disso, se as habilidades ou scripts se originarem de fontes não confiáveis, um script maliciosamente elaborado poderá lançar uma exceção cuja mensagem incorpore uma carga maliciosa de injeção de prompt.

Use require_script_approval=True no SkillsProvider para condicionar toda a execução de script à aprovação humana. Em vez de executar imediatamente, o agente faz uma pausa e envia solicitações de aprovação via 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)

Quando um script é rejeitado (approved=False), o agente é informado de que o usuário recusou e pode responder adequadamente.

Prompt personalizado do sistema

Por padrão, o provedor de habilidades injeta um prompt do sistema que lista as habilidades disponíveis e instrui o agente a usar load_skill e read_skill_resource. Você pode personalizar este prompt:

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.
            """
    });

Observação

O modelo personalizado deve conter {skills} como espaço reservado para a lista de habilidades gerada. Chaves literais devem ser escapadas como {{ e }}.

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

Observação

O modelo personalizado deve conter os espaços reservados {skills} (lista de habilidades), {resource_instructions} (dica de ferramenta de recurso) e {runner_instructions} (dica de ferramenta de script). Chaves literais devem ser escapadas como {{ e }}.

Injetando serviços e argumentos de runtime

As funções de script e os recursos de habilidade podem receber contexto externo do aplicativo fornecido em tempo de execução.

Os representantes de script e recursos de habilidade podem declarar um IServiceProvider parâmetro que o Agent Framework injeta automaticamente. Isso permite que as habilidades resolvam os serviços de aplicativos registrados sob demanda.

Configuração

Registre seus serviços de aplicativo e passe o compilado IServiceProvider para o agente por meio do services parâmetro:

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);

Habilidades definidas por código com DI

Declare IServiceProvider como um parâmetro em delegates AddResource ou AddScript — o framework o resolve e injeta automaticamente quando o agente lê um recurso ou executa um script:

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);
    });

Habilidades baseadas em classe com DI

Anote métodos com [AgentSkillResource] ou [AgentSkillScript] e declare um parâmetro IServiceProvider — o framework descobre esses membros por reflexão e injeta o provedor de serviços automaticamente:

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);
    }
}

Tip

As habilidades baseadas em classe também podem resolver dependências através de seu construtor. Registre a classe de habilidade no ServiceCollection contêiner e resolva-a em vez de chamar new diretamente:

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

Isso é útil quando a classe de habilidade em si precisa de serviços injetados além do que os delegados de script e de recurso utilizam.

Funções de recurso e script que aceitam **kwargs recebem automaticamente os argumentos de palavra-chave de tempo de execução passados para agent.run(). Isso permite que as funções de habilidade acessem o contexto do aplicativo - como configuração, identidade do usuário ou clientes de serviço - sem codificá-las na definição de habilidade.

Passando argumentos de tempo de execução

Passe function_invocation_kwargs para agent.run() fornecer argumentos de palavra-chave que a estrutura encaminha para funções de recurso e script:

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

Habilidades definidas por código com kwargs

Quando uma função de recurso é declarada **kwargs, a estrutura encaminha os argumentos de palavra-chave de runtime sempre que o agente lê o recurso:

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}"

As funções de recurso sem **kwargs são chamadas sem argumentos e não recebem contexto de runtime.

Quando uma função de script é declarada **kwargs, a estrutura encaminha os argumentos de palavra-chave de runtime junto com o args fornecido pelo agente:

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})

O agente fornece value e factor através da chamada de ferramenta args; o aplicativo fornece precision através do function_invocation_kwargs. Funções de script sem **kwargs recebem apenas os argumentos fornecidos pelo agente.

Habilidades com base em classe com kwargs

Os métodos de habilidade baseados em classe também podem aceitar **kwargs para receber argumentos em tempo de execução. O padrão funciona da mesma maneira : declare **kwargs sobre métodos de recurso ou métodos de script:

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})

Melhores práticas de segurança

As habilidades do agente devem ser tratadas como qualquer código de terceiros que você colocar em seu projeto. Como as instruções de habilidade são injetadas no contexto do agente - e as habilidades podem incluir scripts - aplicar o mesmo nível de revisão e governança que você faria a uma dependência de software livre é essencial.

  • Revise antes de usar - Leia todo o conteúdo da skill (SKILL.md, scripts e recursos) antes de implantar. Verifique se o comportamento real de um script corresponde à sua intenção declarada. Verifique se há instruções adversárias que tentam ignorar diretrizes de segurança, exfiltrar dados ou modificar arquivos de configuração do agente.
  • Confiança de origem – instale apenas habilidades de autores confiáveis ou colaboradores internos examinados. Prefira habilidades com procedência clara, controle de versão e manutenção ativa. Fique atento aos nomes de funcionalidades com erros de digitação que imitam pacotes populares.
  • Área restrita: executar habilidades que incluem scripts executáveis em ambientes isolados. Limite o sistema de arquivos, a rede e o acesso no nível do sistema apenas ao que a habilidade exige. Exigir confirmação explícita do usuário antes de executar operações potencialmente confidenciais.
  • Auditoria e registro - Registra quais habilidades são carregadas, quais recursos são lidos e quais scripts são executados. Isso oferece uma trilha de auditoria para rastrear o comportamento do agente de volta ao conteúdo de habilidade específico se algo der errado.

Quando usar habilidades versus fluxos de trabalho

As habilidades do agente e os fluxos de trabalho do Agent Framework estendem o que os agentes podem fazer, mas funcionam de maneiras fundamentalmente diferentes. Escolha a abordagem que melhor corresponda aos seus requisitos:

  • Controle – Com uma habilidade, a IA decide como executar as instruções. Isso é ideal quando você deseja que o agente seja criativo ou adaptável. Com um fluxo de trabalho, você define explicitamente o caminho de execução. Use fluxos de trabalho quando precisar de um comportamento determinístico e previsível.
  • Resiliência: uma habilidade é executada durante um único turno do agente. Se algo falhar, toda a operação deverá ser repetida. Os fluxos de trabalho dão suporte ao ponto de verificação, para que possam ser retomados da última etapa bem-sucedida após uma falha. Escolha fluxos de trabalho quando o custo de executar novamente todo o processo for alto.
  • Efeitos colaterais: as habilidades são adequadas quando as operações são idempotentes ou de baixo risco. Prefira fluxos de trabalho quando as etapas produzem efeitos colaterais (enviar emails, cobrar pagamentos) que não devem ser repetidos na repetição.
  • Complexidade – as habilidades são melhores para tarefas de domínio único e focadas que um agente pode lidar. Os fluxos de trabalho são mais adequados para processos de negócios de várias etapas que coordenam vários agentes, aprovações humanas ou integrações externas do sistema.

Tip

Como regra geral: se você quiser que a IA descubra como realizar uma tarefa, use uma habilidade. Se você precisar garantir quais etapas são executadas e em que ordem, use um fluxo de trabalho.

Próximas etapas