Aptitudes del agente

Las aptitudes del agente son paquetes portátiles de instrucciones, scripts y recursos que proporcionan a los agentes funcionalidades especializadas y conocimientos sobre dominios. Las aptitudes siguen una especificación abierta e implementan un patrón de divulgación progresiva para que los agentes carguen solo el contexto que necesitan, cuando lo necesiten.

Utilice habilidades del agente cuando las necesite:

  • Empaquete la experiencia de dominio: capture conocimientos especializados (políticas de gastos, flujos de trabajo jurídicos, canalizaciones de análisis de datos) en forma de paquetes reutilizables y portables.
  • Ampliar las funcionalidades del agente : proporcione a los agentes nuevas capacidades sin cambiar sus instrucciones principales.
  • Garantizar la coherencia : convierta las tareas de varios pasos en flujos de trabajo repetibles y auditables.
  • Habilitar la interoperabilidad : reutilice la misma aptitud en diferentes productos compatibles con aptitudes del agente.

Estructura de aptitudes

Una aptitud es un directorio que contiene un SKILL.md archivo con subdirectorios opcionales para los 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

El SKILL.md archivo debe contener el frontmatter de YAML seguido del contenido 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 Obligatorio Description
name Máximo de 64 caracteres. Solo letras minúsculas, números y guiones. No debe iniciar ni terminar con un guion o contener guiones consecutivos. Debe coincidir con el nombre del directorio primario.
description Qué hace la aptitud y cuándo usarla. Máximo de 1024 caracteres. Debe incluir palabras clave que ayuden a los agentes a identificar las tareas pertinentes.
license No Nombre de licencia o referencia a un archivo de licencia agrupado.
compatibility No Máximo de 500 caracteres. Indica los requisitos del entorno (producto previsto, paquetes del sistema, acceso a la red, etc.).
metadata No Mapeo arbitrario de clave-valor para metadatos adicionales.
allowed-tools No Lista delimitada por espacios de herramientas preaprobadas que puede usar la destreza. Experimental: el soporte puede variar entre las implementaciones de agentes.

El cuerpo de Markdown después de la frontmatter contiene las instrucciones de aptitud: instrucciones paso a paso, ejemplos de entradas y salidas, casos perimetrales comunes o cualquier contenido que ayude al agente a realizar la tarea. Mantenga SKILL.md en menos de 500 líneas y mueva el material de referencia detallado a archivos independientes.

Divulgación progresiva

Las capacidades del agente usan un modelo de revelación progresiva a lo largo de cuatro etapas para reducir al mínimo el uso del contexto.

  1. Anunciar (~100 tokens por habilidad) - Los nombres y las descripciones de las habilidades se insertan en el prompt del sistema al comienzo de cada ejecución, para que el agente sepa qué habilidades están disponibles.
  2. Cargar (< se recomiendan 5000 tokens) - Cuando una tarea coincide con el dominio de una habilidad, el agente llama a la herramienta load_skill para recuperar el contenido completo de SKILL.md con instrucciones detalladas.
  3. Recursos de lectura (según sea necesario): el agente llama a la read_skill_resource herramienta para capturar archivos complementarios (referencias, plantillas, recursos) solo cuando sea necesario.
  4. Ejecutar scripts (según sea necesario) - El agente usa la herramienta run_skill_script para ejecutar scripts incluidos en una habilidad.

Este patrón mantiene la ventana de contexto del agente ligera al tiempo que proporciona acceso a conocimientos profundos de dominio bajo demanda.

Nota:

load_skill siempre se anuncia. read_skill_resource se anuncia solo cuando al menos una habilidad tiene recursos. run_skill_script solo se anuncia cuando al menos una habilidad tiene scripts.

Proporcionar aptitudes a un agente

Trabajar con habilidades implica tres elementos fundamentales:

  • Proveedor - AgentSkillsProvider (C#) o SkillsProvider (Python) es un proveedor de contexto que expone aptitudes a un agente. Anuncia las aptitudes disponibles en el símbolo del sistema y registra las herramientas que usa el agente para cargar aptitudes, leer recursos y ejecutar scripts.
  • Orígenes : un origen proporciona aptitudes al proveedor. Las aptitudes pueden provenir de varios tipos de origen:
    • Basadas en archivos - habilidades descubiertas en archivos SKILL.md de directorios del sistema de archivos.
    • Definidas en el código: habilidades definidas directamente en el código mediante AgentInlineSkill (C#) o InlineSkill (Python).
    • Basado en clases: aptitudes encapsuladas en una clase derivada de AgentClassSkill<T> (C#) o ClassSkill (Python).
    • Basadas en MCP - habilidades descubiertas en servidores MCP (Model Context Protocol) a través de UseMcpSkills (C#) o MCPSkillsSource (Python).
  • Generador - AgentSkillsProviderBuilder (C#) ensambla varios orígenes en un único proveedor, aplicando agregación, desduplicación, almacenamiento en caché y filtrado opcional. En Python, componga clases de origen como AggregatingSkillsSource, FilteringSkillsSourcey DeduplicatingSkillsSource directamente.

En las secciones siguientes se muestra cómo crear aptitudes de cada tipo de origen y cómo combinar orígenes y construir un proveedor a partir de ellos.

Habilidades basadas en archivos

Cree un AgentSkillsProvider que apunte a un directorio que contenga sus aptitudes y agréguelo a los proveedores de contexto del agente. Pase un ejecutor de scripts para habilitar la ejecución de scripts basados en archivos que se encuentran en directorios de aptitudes:

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

Advertencia

DefaultAzureCredential es conveniente para el desarrollo, pero requiere una consideración cuidadosa en producción. En producción, considere usar una credencial específica (por ejemplo, ManagedIdentityCredential) para evitar problemas de latencia, sondeos de credenciales no deseados y posibles riesgos de seguridad de los mecanismos de respaldo.

Varios directorios de aptitudes

Puede hacer que el proveedor apunte a un único directorio principal: cada subdirectorio que contiene un SKILL.md se detecta automáticamente como una habilidad:

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

O bien, pase una lista de rutas de acceso para buscar varios directorios raíz:

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

El proveedor busca hasta dos niveles en profundidad.

Personalización de la detección de recursos y scripts

De forma predeterminada, el proveedor reconoce recursos con extensiones .md, .json, .yaml, .yml, .csv, .xml y .txt, y scripts con extensiones .py, .js, .sh, .ps1, .cs y .csx. Busca hasta dos niveles en profundidad dentro de cada directorio de aptitudes. Use AgentFileSkillsSourceOptions para cambiar estos valores predeterminados:

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 y ScriptFilter reciben un AgentFileSkillFilterContext con el nombre de la aptitud y la ruta de acceso relativa del archivo, lo que le permite restringir los archivos por ubicación, convención de nomenclatura o cualquier lógica personalizada.

Ejecución de scripts

Pase SubprocessScriptRunner.RunAsync como ejecutor de scripts para habilitar la ejecución de scripts basados en archivos:

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

SubprocessScriptRunner.RunAsync es aproximadamente equivalente a lo siguiente:

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

El ejecutor ejecuta cada script detectado como un subproceso local. Los scripts basados en archivos esperan argumentos como una matriz JSON de cadenas: cada elemento de matriz se convierte en un argumento de línea de comandos posicional.

Advertencia

SubprocessScriptRunner solo se proporciona con fines de demostración. Para su uso en producción, considere la posibilidad de agregar:

  • Sandboxing (por ejemplo, contenedores o entornos de ejecución aislados)
  • Límites de recursos (CPU, memoria, tiempo de espera del reloj)
  • Validación de entrada y lista de permitidos de scripts ejecutables
  • Registros estructurados y seguimientos de auditoría

Habilidades basadas en archivos

Use la factoría SkillsProvider.from_paths() para descubrir habilidades de directorios que contienen archivos SKILL.md, y agregue el proveedor a los proveedores de contexto del 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],
)

Varios directorios de aptitudes

Puede hacer que el proveedor apunte a un único directorio principal: cada subdirectorio que contiene un SKILL.md se detecta automáticamente como una habilidad:

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

O bien, pase una lista de rutas de acceso para buscar varios directorios raíz:

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

El proveedor busca hasta dos niveles en profundidad.

Personalización de la detección de recursos y scripts

De forma predeterminada, los recursos se detectan en los subdirectorios references/ y assets/, y los scripts en scripts/, según la especificación de agentskills.io. Las extensiones de recursos reconocidas son .md, .json, .yaml, .yml.csv, , .xmly .txt. Use resource_extensions, script_extensions, resource_filtery script_filter para personalizar la detección:

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

Usa "." para incluir archivos en el nivel raíz de la skill, además de en los subdirectorios.

Ejecución de scripts

Para habilitar la ejecución de scripts basados en archivos, pase un script_runner a SkillsProvider.from_paths(). Se puede usar cualquier función que sea una llamada síncrona o asíncrona que satisfaga el protocolo 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,
)

El ejecutor recibe los valores resueltos FileSkill, FileSkillScript y un argumento opcional args. Los scripts basados en archivos esperan argumentos como una matriz JSON de cadenas: cada elemento de matriz se convierte en un argumento de línea de comandos posicional. Los scripts se descubren automáticamente en los archivos .py del subdirectorio scripts/ de cada directorio de habilidad.

Advertencia

El ejecutor anterior solo se proporciona con fines de demostración. Para su uso en producción, considere la posibilidad de agregar:

  • Espacio aislado (por ejemplo, contenedores, seccomp o firejail)
  • Límites de recursos (CPU, memoria, tiempo de espera del reloj)
  • Validación de entrada y lista de permitidos de scripts ejecutables
  • Registros estructurados y seguimientos de auditoría

Nota:

Si se proporcionan capacidades basadas en archivos con scripts, pero no se establece script_runner, SkillsProvider produce un error al intentar ejecutar el script.

Aptitudes definidas por código

Además de las aptitudes basadas en archivos detectadas a partir de archivos SKILL.md, puede definir aptitudes por completo en el código mediante AgentInlineSkill. Las aptitudes definidas por código son útiles cuando:

  • El contenido de la aptitud se genera dinámicamente (por ejemplo, leer desde una base de datos o un entorno).
  • Quiere mantener las definiciones de habilidades junto con el código de aplicación que las usa.
  • Necesita recursos que ejecuten lógica en tiempo de lectura en lugar de servir archivos estáticos.
  • Las definiciones de aptitudes deben crearse en tiempo de ejecución a partir de datos ; por ejemplo, crear una aptitud personalizada para cada sesión de usuario en función de su rol o permisos.
  • Una habilidad debe capturar el estado en el punto de llamada (variables locales, clausuras) en lugar de resolver servicios desde un contenedor de inyección de dependencias.

Aptitud de código básico

Cree un AgentInlineSkill con un nombre, una descripción e instrucciones. Adjuntar recursos mediante .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

Pase un delegado de fábrica a .AddResource() para calcular el contenido en tiempo de ejecución. El delegado se invoca cada vez que el agente lee el 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 un delegado como un script ejecutable. Los scripts definidos por código se ejecutan en proceso como llamadas de delegado directas. No se necesita ningún ejecutor de scripts. Los parámetros tipados del delegado se convierten automáticamente en un esquema JSON que el agente utiliza para pasar 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);

Nota:

Para combinar habilidades definidas en el código con habilidades basadas en archivos o en clases en un único proveedor, use AgentSkillsProviderBuilder; consulte Construcción del proveedor.

Además de las habilidades basadas en archivos detectadas en archivos SKILL.md, puede definir habilidades íntegramente en código Python mediante InlineSkill. Las aptitudes definidas por código son útiles cuando:

  • El contenido de la aptitud se genera dinámicamente (por ejemplo, leer desde una base de datos o un entorno).
  • Quiere mantener las definiciones de habilidades junto con el código de aplicación que las usa.
  • Necesita recursos que ejecuten lógica en tiempo de lectura en lugar de servir archivos estáticos.
  • Las definiciones de aptitudes deben crearse en tiempo de ejecución a partir de datos ; por ejemplo, crear una aptitud personalizada para cada sesión de usuario en función de su rol o permisos.
  • Una habilidad debe capturar el estado del sitio de llamada (variables locales, clausuras) en lugar de resolver servicios a través de **kwargs.

Aptitud de código básico

Cree una InlineSkill instancia con un SkillFrontmatter (que contiene el nombre y la descripción) y el contenido de instrucciones. Opcionalmente, adjunte InlineSkillResource instancias con contenido 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 el @skill.resource decorador para registrar una función como un recurso. La función se llama cada vez que el agente lee el recurso, por lo que puede devolver datos actualizados. Se admiten las funciones de sincronización y asincrónica:

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

Cuando el decorador se usa sin argumentos (@skill.resource), el nombre de la función se convierte en el nombre del recurso y docstring se convierte en la descripción. Use @skill.resource(name="...", description="...") para establecerlos explícitamente.

Scripts definidos por código

Use el decorador @skill.script para registrar una función como un script ejecutable en una habilidad. Los scripts definidos por código se ejecutan en proceso y no requieren un ejecutor de scripts. Se admiten las funciones de sincronización y asincrónica:

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

Cuando el decorador se usa sin argumentos (@skill.script), el nombre de la función se convierte en el nombre del script y docstring se convierte en la descripción. Los parámetros tipificados de la función se convierten automáticamente en un esquema JSON que el agente utiliza para el paso de argumentos.

Habilidades basadas en clases

Las aptitudes basadas en clases permiten agrupar todos los componentes de aptitudes ( nombre, descripción, instrucciones, recursos y scripts) en una sola clase de C#. Esto hace que sean fáciles de empaquetar y distribuir como paquetes NuGet: los equipos pueden crear y enviar aptitudes de forma independiente, y los consumidores los agregan con dotnet add package y una sola .UseSkill() llamada. Derive de AgentClassSkill<T> (donde T es la clase ), anote las propiedades con [AgentSkillResource] y los métodos con [AgentSkillScript] para la detección 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 la habilidad basada en clases con AgentSkillsProvider

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

Cuando el [AgentSkillResource] atributo se aplica a una propiedad o método, su valor devuelto se usa como contenido del recurso cuando el agente lee el recurso: use un método cuando el contenido se necesite calcular en tiempo de lectura. Cuando [AgentSkillScript] se aplica a un método, se invoca el método cuando el agente llama al script. Use [Description] desde System.ComponentModel para describir cada recurso y script para el agente.

Nota:

AgentClassSkill<T> también admite la sobrescritura de Resources y Scripts como colecciones para escenarios en los que la detección basada en atributos no es adecuada.

Habilidades basadas en clases

Las aptitudes basadas en clases permiten agrupar todos los componentes de aptitudes (nombre, descripción, instrucciones, recursos y scripts) en una sola clase Python. Esto hace que sea fácil empaquetarlas y distribuirlas como paquetes de PyPI: los equipos pueden crear y publicar habilidades de forma independiente, y los usuarios pueden agregarlas con pip install y una sola llamada a SkillsProvider(). Cree una subclase de ClassSkill y luego use los decoradores @ClassSkill.resource y @ClassSkill.script para la detección 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 la habilidad basada en clases con SkillsProvider

from agent_framework import SkillsProvider

skill = UnitConverterSkill()
skills_provider = SkillsProvider(skill)

Cuando @ClassSkill.resource se aplica como decorador completo (sin argumentos), el nombre del método se convierte en el nombre del recurso (con caracteres de subrayado convertidos en guiones) y la docstring se convierte en la descripción. Use @ClassSkill.resource(name="...", description="...") para establecerlos explícitamente. El mismo patrón se aplica a @ClassSkill.script.

Los recursos se pueden definir como métodos o @property descriptores normales. Cuando se usa @property, coloque @property primero y @ClassSkill.resource segundo. Los valores devueltos de recursos se almacenan en caché después del primer acceso.

Nota:

ClassSkill también admite sobrescribir explícitamente las propiedades resources y scripts para devolver directamente instancias de InlineSkillResource y InlineSkillScript, en aquellos casos en los que la detección basada en decoradores no se ajusta.

Habilidades basadas en MCP

Nota:

Las habilidades basadas en MCP requieren el Microsoft.Agents.AI.Mcp paquete NuGet. La API de aptitudes de MCP es experimental y puede cambiar en futuras versiones.

Las habilidades se pueden descubrir en servidores MCP (Protocolo de Contexto de Modelo) que exponen recursos de habilidades bajo el esquema de URI skill://. El servidor MCP anuncia aptitudes a través de un skill://index.json documento de detección y el marco captura contenido de aptitudes a petición.

Las habilidades basadas en MCP admiten dos tipos de entradas de índice:

  • skill-md - La SKILL.md habilidad y los recursos relacionados se obtienen bajo demanda del servidor MCP.
  • archive - La aptitud se distribuye como un único archivo empaquetado (ZIP, TAR o TAR comprimido por gzip) que se descarga y desempaqueta localmente.

Uso básico

Use el método de extensión UseMcpSkills en AgentSkillsProviderBuilder para agregar un origen de habilidades de 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 de archivado

Para las habilidades de tipo archivo, use AgentMcpSkillsSourceOptions (del paquete Microsoft.Agents.AI.Mcp) para configurar el comportamiento de extracción:

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 expone las siguientes propiedades para controlar la extracción de archivos:

  • ArchiveSkillsDirectory - Directorio base para archivos extraídos. Por defecto, se usa un subdirectorio único dentro del directorio de trabajo actual, generado para cada instancia de origen para evitar colisiones entre múltiples orígenes.
  • ArchiveResourceExtensions - Extensiones permitidas para recursos en archivos extraídos. El valor predeterminado es .md, .json, .yaml, .yml, .csv, .xml, .txt.
  • ArchiveResourceSearchDepth - Profundidad de búsqueda de recursos dentro de cada directorio de habilidades extraído. Tiene como valor predeterminado 2.
  • ArchiveMaxFileCount - Número máximo de archivos por archivo comprimido. Los archivos que superan este límite se omiten. Tiene como valor predeterminado 20.
  • ArchiveMaxSizeBytes - Tamaño máximo de descarga por archivo. Tiene como valor predeterminado 1 MB.
  • ArchiveMaxUncompressedSizeBytes - Tamaño total total sin comprimir por archivo. Tiene como valor predeterminado 1 MB.

Importante

Los scripts agrupados en las aptitudes de tipo de archivo nunca se ejecutan. Se trata de una medida de seguridad deliberada: el contenido ejecutable de los servidores MCP remotos requiere confianza explícita.

Orígenes de aptitudes

Un AgentSkillsProvider recupera habilidades de una o varias fuentes - objetos que implementan AgentSkillsSource. Las fuentes se dividen en dos categorías: fuentes terminales que descubren o contienen habilidades (como AgentFileSkillsSource para habilidades basadas en archivos), y decoradores que transforman el resultado de otra fuente (agregación, eliminación de duplicados, almacenamiento en caché y filtrado). También puede crear un origen personalizado.

Cada origen implementa un único método: GetSkillsAsync(AgentSkillsSourceContext context, CancellationToken cancellationToken = default). AgentSkillsSourceContext contiene información sobre la solicitud actual:

  • Agent : la AIAgent instancia que solicita aptitudes.
  • Session - el AgentSession asociado a la invocación, o null cuando no hay ninguna sesión.

Este contexto está disponible en toda la canalización de origen, por lo que un FilteringAgentSkillsSource predicado o un origen personalizado puede basar su lógica en ella; por ejemplo, devolver un conjunto diferente de aptitudes en función del agente solicitante.

Orígenes hoja

AgentFileSkillsSource

Detecta habilidades a partir de archivos SKILL.md en el disco. Acepta una o varias rutas de directorio, un ejecutor de scripts opcional y un AgentFileSkillsSourceOptions opcional (documentado en habilidades basadas en archivos).

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

AgentInMemorySkillsSource

Encapsula instancias AgentSkill (definidas por código o basadas en clase) en memoria.

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

Combinadores

AggregatingAgentSkillsSource

Combina varios orígenes en uno. Las habilidades se devuelven en orden de registro sin aplicar ninguna deduplicación ni filtrado.

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

Decoradores

Los decoradores envuelven una fuente interna y transforman su salida. Se pueden encadenar para crear una canalización.

DeduplicatingAgentSkillsSource

Quita los nombres de aptitud duplicados (no distingue mayúsculas de minúsculas y gana la primera aparición). Los duplicados se registran como advertencia.

var deduplicated = new DeduplicatingAgentSkillsSource(innerSource);

CachingAgentSkillsSource

Almacena en caché la lista de habilidades devuelta por la fuente interna. Las llamadas simultáneas se serializan por clave de caché, de modo que solo se ejecuta una obtención a la vez. Acepta CachingAgentSkillsSourceOptions de forma opcional:

  • RefreshInterval () :TimeSpan? cuando se establece, los resultados almacenados en caché expiran después de este intervalo y se vuelve a invocar el origen interno. Cuando null (valor predeterminado), los resultados almacenados en caché nunca expiran.
  • CacheIsolationKeySelector (Func<AgentSkillsSourceContext, string?>?) : devuelve una clave de caché para aislar los resultados almacenados en caché por contexto (por ejemplo, por inquilino). Cuando null, todos los procesos que llaman comparten un único contenedor de caché.
var cached = new CachingAgentSkillsSource(innerSource, new CachingAgentSkillsSourceOptions
{
    RefreshInterval = TimeSpan.FromMinutes(5)
});

FilteringAgentSkillsSource

Aplica un predicado para incluir o excluir aptitudes. El predicado recibe la habilidad y un AgentSkillsSourceContext.

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

Orígenes personalizados

Cuando las fuentes integradas no cubran su caso, implemente sus propias fuentes. Subclase AgentSkillsSource para un origen hoja (uno que genera aptitudes a partir de un nuevo origen, como una base de datos o un servicio remoto), o una subclase DelegatingAgentSkillsSource para un decorador que transforma la salida de otro origen.

Origen de la hoja

Derive de AgentSkillsSource e implemente GetSkillsAsync. El AgentSkillsSourceContext argumento permite al origen adaptar su resultado a la solicitud actual; por ejemplo, devolver un conjunto diferente de aptitudes en función del agente solicitante. Sobrescriba Dispose(bool) si la fuente posee recursos como un cliente o una conexión.

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, llame a InnerSource.GetSkillsAsyncy transforme o observe el resultado. Este es el mismo patrón que usan los decoradores integrados de almacenamiento en caché, desduplicación y filtrado. Por ejemplo, un decorador que registra cuántas habilidades se devolvieron en cada solicitud sin cambiar el 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 fuentes personalizadas se pueden pasar directamente a AgentSkillsProvider o anidar en una canalización más grande, al igual que las fuentes integradas.

Construcción del proveedor

AgentSkillsProvider es el componente que pone capacidades a disposición de un agente. Encapsula una o más fuentes y registra las herramientas load_skill, read_skill_resource y run_skill_script. Hay tres maneras de crear una:

  1. AgentSkillsProviderBuilder : compone varios tipos de aptitudes en un proveedor con agregación automática, desduplicación, almacenamiento en caché y filtrado opcional. Ideal para escenarios que combinan aptitudes basadas en archivos, definidas por código, basadas en clases y basadas en MCP.
  2. Composición directa de la fuente - construir la canalización de la fuente usted mismo utilizando las clases públicas AgentSkillsSource. No se aplica ningún almacenamiento en caché o desduplicación automática: se controla la canalización completa. Mejor cuando necesite controlar el orden, la lógica condicional o el comportamiento del decorador personalizado.
  3. Constructores auxiliares - crean un proveedor directamente a partir de una ruta de archivo o de instancias de habilidades. Aplica automáticamente la desduplicación y el almacenamiento en caché. Ideal para escenarios de origen único.

Uso de AgentSkillsProviderBuilder

Use AgentSkillsProviderBuilder cuando necesite cualquiera de las siguientes opciones:

  • Tipos de aptitudes mixtas : combine aptitudes basadas en archivos, definidas por código (AgentInlineSkill), basadas en clases (AgentClassSkill) y basadas en MCP en un único proveedor.
  • Filtrado de aptitudes : incluya o excluya aptitudes mediante un predicado.

Tipos de aptitudes mixtas

Combina varios tipos de habilidades en un proveedor encadenando UseFileSkill, UseSkill, UseMcpSkills y 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();

Filtrado de aptitudes

Use UseFilter para incluir solo las aptitudes que cumplan los criterios; por ejemplo, para cargar aptitudes desde un directorio compartido, pero excluir las experimentales:

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

Composición directa de fuentes

Cuando el constructor no ofrece el control que necesita, cree usted mismo las clases de origen y pase la canalización resultante a AgentSkillsProvider. Consulte Orígenes de aptitudes para obtener la lista completa de orígenes disponibles y sus opciones.

El siguiente ejemplo crea una canalización multiorigen comparable, pero te da un control 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);

Nota:

Cuando ownsSource es true, al desechar el proveedor también se desecha toda la canalización de origen. Configúrelo como false si usted mismo administra el ciclo de vida de la fuente.

Constructores de conveniencia

Para escenarios de origen único, use los AgentSkillsProvider constructores directamente. Estas aplican automáticamente la desduplicación y el almacenamiento en caché sin requerir un builder ni la composición manual del origen.

Desde una ruta de archivo:

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

A partir de instancias de habilidad:

var skillsProvider = new AgentSkillsProvider(volumeConverterSkill, temperatureConverter);

Composición de origen: escenarios avanzados de varios orígenes

Para escenarios sencillos con una sola aptitud o una lista de aptitudes, páselos directamente al SkillsProvider constructor. En el caso de las aptitudes basadas en archivos, use la fábrica SkillsProvider.from_paths(). En escenarios avanzados, cree clases de origen para controlar la detección, el filtrado y la desduplicación:

  • FileSkillsSource - descubre habilidades en archivos SKILL.md del disco.
  • InMemorySkillsSource : encapsula cualquier Skill instancia (definida por código o basada en clases) en memoria.
  • AggregatingSkillsSource : combina varios orígenes en uno.
  • FilteringSkillsSource : aplica un predicado para incluir o excluir aptitudes.
  • DeduplicatingSkillsSource : quita nombres de aptitud duplicados (sin distinción entre mayúsculas y minúsculas, primeros victorias).

Tipos de aptitudes mixtas

Combine habilidades basadas en archivos, definidas mediante código y basadas en clases en un único proveedor con AggregatingSkillsSource. En el ejemplo siguiente se usan objetos de marcador de posición:

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

Filtrado de aptitudes

Use FilteringSkillsSource para controlar qué aptitudes ve el agente. El predicado recibe cada Skill y devuelve True para incluirlo. Por ejemplo, para cargar habilidades desde un directorio compartido pero ocultar una 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",
        )
    )
)

Comportamiento del almacenamiento en caché

De forma predeterminada, el compilador envuelve la canalización de origen con un CachingAgentSkillsSource que almacena en caché la lista de habilidades que devuelven los orígenes subyacentes. Una vez resueltas las aptitudes en la primera solicitud, las solicitudes posteriores reutilizan la lista almacenada en caché sin volver a consultar los orígenes. Para deshabilitar el almacenamiento en caché (por ejemplo, durante el desarrollo cuando las definiciones de aptitudes cambian con frecuencia), use DisableCaching() en el generador:

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

Nota:

Deshabilitar el almacenamiento en caché es útil durante el desarrollo cuando el contenido de la aptitud cambia con frecuencia. En producción, deje habilitado el almacenamiento en caché (valor predeterminado) para mejorar el rendimiento.

Comportamiento del almacenamiento en caché

De forma predeterminada, las herramientas de aptitud y las instrucciones se almacenan en caché después de la primera compilación. Establezca disable_caching=True para forzar una reconstrucción en cada invocación:

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

disable_caching también está disponible en el SkillsProvider constructor para aptitudes definidas por código y basadas en clases.

Nota:

Deshabilitar el almacenamiento en caché es útil durante el desarrollo cuando el contenido de la aptitud cambia con frecuencia. En producción, deje habilitado el almacenamiento en caché (valor predeterminado) para mejorar el rendimiento.

Aprobación de herramientas

Todas las herramientas expuestas por AgentSkillsProvider (load_skill, read_skill_resource, run_skill_script) requieren aprobación de forma predeterminada. Cuando una invocación de una herramienta requiere aprobación, el agente se detiene y devuelve un ToolApprovalRequestContent en lugar de ejecutarse inmediatamente. Use UseToolApproval software intermedio con reglas de aprobación automática para omitir selectivamente los mensajes de confirmación de las operaciones de confianza:

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 aprobar automáticamente todas las herramientas de habilidades, incluida la ejecución de scripts:

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

Deshabilitación de la aprobación de herramientas específicas

Use AgentSkillsProviderOptions para deshabilitar la aprobación de herramientas individuales, quitándolas del flujo de aprobación por completo:

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

Cuando algunas herramientas requieren aprobación y otras no en la misma respuesta, el modelo puede llamar a ambos tipos simultáneamente. Configure EnableNonApprovalRequiredFunctionBypassing para que las herramientas que no requieren aprobación se ejecuten inmediatamente, mientras que solo se solicita al usuario aprobación para las restantes:

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

Control de solicitudes de aprobación

Cuando las herramientas requieren aprobación (y ninguna regla de aprobación automática coincide), el agente devuelve ToolApprovalRequestContent elementos que se deben aprobar o rechazar 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();
}

Detalles del error del script

De forma predeterminada, cuando falla la ejecución de un script de skill, la excepción se propaga al componente subyacente FunctionInvokingChatClient. Si la propiedad IncludeDetailedErrors se establece en true, el mensaje de excepción se transmite al modelo, lo que le permite corregirse automáticamente volviendo a intentarlo con 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());

Si no puede configurar FunctionInvokingChatClient directamente, establezca AgentSkillsProviderOptions.IncludeDetailedErrors en su lugar. Esto detecta la excepción en el nivel de proveedor de aptitudes y devuelve el mensaje de error directamente al modelo:

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

Advertencia

Cualquiera de los enfoques puede revelar al modelo detalles sin procesar de las excepciones. Los mensajes de excepción pueden contener información confidencial, como cadenas de conexión, rutas de acceso de archivo o nombres de servicio internos. Además, si las habilidades o los scripts proceden de fuentes no confiables, un script creado de forma malintencionada podría provocar una excepción cuyo mensaje incorpora una carga útil de inyección de prompts.

Use require_script_approval=True en SkillsProvider para controlar toda la ejecución de scripts con aprobación humana. En lugar de ejecutarse inmediatamente, el agente hace una pausa y devuelve solicitudes de aprobación a través de 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)

Cuando se rechaza un script (approved=False), se informa al agente de que el usuario ha rechazado y que puede responder en consecuencia.

Mensaje del sistema personalizado

De forma predeterminada, el proveedor de aptitudes inserta una solicitud del sistema que enumera las aptitudes disponibles e indica al agente que use load_skill y read_skill_resource. Puede personalizar este mensaje:

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

Nota:

La plantilla personalizada debe contener {skills} como marcador de posición para la lista de aptitudes generada. Las llaves literales deben ser de escape como {{ y }}.

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

Nota:

La plantilla personalizada debe contener los marcadores de posición {skills} (lista de habilidades), {resource_instructions} (sugerencia de herramienta de recursos) y {runner_instructions} (sugerencia de herramienta de script). Las llaves literales deben ser de escape como {{ y }}.

Inserción de servicios y argumentos en tiempo de ejecución

Las funciones de script y recursos de aptitud pueden recibir el contexto de aplicación externo proporcionado en tiempo de ejecución.

Los delegados de script y de recursos de aptitud pueden incluir un parámetro IServiceProvider que el Agent Framework inserta automáticamente. Esto permite que las aptitudes resuelvan los servicios de aplicación registrados a petición.

Configuración

Registre los servicios de aplicación y pase el compilado IServiceProvider al agente mediante el parámetro 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);

Aptitudes definidas por código con DI

Declarar IServiceProvider como parámetro en los delegados AddResource o AddScript: el framework lo resuelve y lo inyecta automáticamente cuando el agente lee un recurso o ejecuta un 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 basadas en clases con inyección de dependencias

Anotación de métodos con [AgentSkillResource] o [AgentSkillScript] y declaración de un IServiceProvider parámetro: el marco detecta estos miembros a través de la reflexión e inserta automáticamente el proveedor de servicios:

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

Sugerencia

Las habilidades basadas en clases también pueden resolver las dependencias a través de su constructor. Registre la clase de aptitud en ServiceCollection y la resuelva desde el contenedor en lugar de llamar directamente a new :

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

Esto resulta útil cuando la propia clase de habilidad necesita servicios inyectados más allá de aquellos utilizados por los delegados de recursos y scripts.

Las funciones de recursos y scripts que aceptan **kwargs reciben automáticamente los argumentos de palabras clave en tiempo de ejecución que se pasan a agent.run(). Esto permite que las funciones de aptitud accedan al contexto de la aplicación , como la configuración, la identidad del usuario o los clientes de servicio, sin codificarlos de forma rígida en la definición de la aptitud.

Pasar argumentos en tiempo de ejecución

Pase function_invocation_kwargs a agent.run() para proporcionar argumentos de palabra clave que el marco reenvía a las funciones de recurso y script:

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

Habilidades definidas mediante código con kwargs

Cuando una función de recurso declara **kwargs, el marco reenvía los argumentos de palabra clave en tiempo de ejecución cada vez que el agente lee el 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}"

Las funciones de recursos sin **kwargs se llaman sin argumentos y no reciben contexto en tiempo de ejecución.

Cuando una función de script declara **kwargs, el marco reenvía los argumentos de palabra clave en tiempo de ejecución junto con el args proporcionado por el 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})

El agente proporciona value y factor a través de la llamada a la herramienta args; la aplicación proporciona precision a través de function_invocation_kwargs. Funciones de script sin **kwargs reciben solo los argumentos que el agente proporciona.

Habilidades basadas en clases con kwargs

Los métodos de habilidad basados en clases también pueden aceptar **kwargs para recibir argumentos en tiempo de ejecución. El patrón funciona de la misma manera: declarar **kwargs en métodos de recursos o 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})

Procedimientos recomendados de seguridad

Las aptitudes del agente deben tratarse como cualquier código de terceros que incorpore al proyecto. Dado que las instrucciones de aptitudes se insertan en el contexto del agente( y las aptitudes pueden incluir scripts), aplicar el mismo nivel de revisión y gobernanza que haría con una dependencia de código abierto es esencial.

  • Revisar antes de usar : lea todo el contenido de las aptitudes (SKILL.md, scripts y recursos) antes de la implementación. Compruebe que el comportamiento real de un script coincide con su intención indicada. Busque instrucciones adversarias que intenten omitir las directrices de seguridad, exfiltrar datos o modificar archivos de configuración de agentes.
  • Confianza de origen : instale solo aptitudes de autores de confianza o colaboradores internos examinados. Prefiere aptitudes con un claro origen, control de versiones y mantenimiento activo. Esté atento a los nombres de habilidades suplantados que imitan paquetes de software populares.
  • Aislamiento - Ejecutar habilidades que incluyan scripts ejecutables en entornos aislados. Limite el acceso del sistema de archivos, la red y el nivel de sistema solo a lo que requiere la aptitud. Requerir confirmación explícita del usuario antes de ejecutar operaciones potencialmente confidenciales.
  • Auditoría y registro : registre qué aptitudes se cargan, qué recursos se leen y qué scripts se ejecutan. Esto le proporciona una pista de auditoría para rastrear el comportamiento del agente hasta contenido específico de habilidades si algo sale mal.

Cuándo usar aptitudes frente a flujos de trabajo

Los flujos de trabajo de Agent Skills y Agent Framework amplían lo que pueden hacer los agentes, pero funcionan de maneras fundamentalmente diferentes. Elija el enfoque que mejor se adapte a sus requisitos:

  • Control : con una aptitud, la inteligencia artificial decide cómo ejecutar las instrucciones. Esto es ideal cuando quieres que el agente sea creativo o adaptable. Con un flujo de trabajo, se define explícitamente la trayectoria de ejecución. Use flujos de trabajo cuando necesite un comportamiento determinista y predecible.
  • Resiliencia - Una habilidad se ejecuta en un único turno del agente. Si se produce un error en algo, se debe reintentar toda la operación. Los flujos de trabajo admiten puntos de control, por lo que pueden reanudarse desde el último paso exitoso después de una falla. Elija flujos de trabajo cuando el costo de volver a ejecutar todo el proceso sea alto.
  • Efectos secundarios - Las habilidades son adecuadas cuando las operaciones son idempotentes o de bajo riesgo. Prefiere flujos de trabajo cuando los pasos producen efectos secundarios (enviar correos electrónicos, cobrar pagos) que no deben repetirse en el reintento.
  • Complejidad : las aptitudes son las mejores para las tareas centradas y de dominio único que un agente puede controlar. Los flujos de trabajo son más adecuados para los procesos empresariales de varios pasos que coordinan varios agentes, aprobaciones humanas o integraciones de sistemas externos.

Sugerencia

Como regla general: si desea que la inteligencia artificial descubra cómo realizar una tarea, use una aptitud. Si necesita garantizar qué pasos se ejecutan y en qué orden, use un flujo de trabajo.

Pasos siguientes