Ügynöki készségek

Az Ügynökkészség olyan utasítások, szkriptek és erőforrások hordozható csomagjai, amelyek speciális képességeket és tartományi szakértelmet biztosítanak az ügynököknek. A készségek egy nyílt specifikációt követnek, és egy fokozatos részletezési mintát valósítanak meg, így az ügynökök csak a szükséges kontextust töltik be, amikor arra szükségük van.

A következő esetekben használja az ügynöki készségeket:

  • Csomagtartomány-szakértelem – A specializált ismeretek (költségszabályzatok, jogi munkafolyamatok, adatelemzési folyamatok) újrafelhasználható, hordozható csomagokként való rögzítése.
  • Ügynöki képességek kiterjesztése – Új képességeket adhat az ügynököknek az alapvető utasítások módosítása nélkül.
  • Konzisztenciának biztosítása – A többlépéses feladatokat megismételhető, naplózható munkafolyamatokká alakíthatja.
  • Az interoperabilitás engedélyezése – Ugyanaz a készség újra felhasználható különböző Agent Skills-kompatibilis termékekben.

Képességstruktúra

A skill egy könyvtár, amely egy SKILL.md nevű fájlt tartalmaz, opcionális alkönyvtárakkal az erőforrások számára.

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

SKILL.md formátum

A SKILL.md fájlnak TARTALMAZNIa kell a YAML-előtétet, majd a Markdown-tartalmat:

---
name: expense-report
description: File and validate employee expense reports according to company policy. Use when asked about expense submissions, reimbursement rules, or spending limits.
license: Apache-2.0
compatibility: Requires python3
metadata:
  author: contoso-finance
  version: "2.1"
---
Field Szükséges Leírás
name Yes Legfeljebb 64 karakter. Csak kisbetűk, számok és kötőjelek. Nem kezdődhet és nem végződhet kötőjellel, és nem tartalmazhat egymást követő kötőjeleket. Meg kell egyeznie a szülőkönyvtár nevével.
description Yes Mit csinál a képesség, és mikor kell használni. Legfeljebb 1024 karakter. Olyan kulcsszavakat kell tartalmaznia, amelyek segítenek az ügynököknek azonosítani a releváns feladatokat.
license No Licencnév vagy hivatkozás egy csomagban létrehozott licencfájlra.
compatibility No Legfeljebb 500 karakter. Környezeti követelményeket jelez (tervezett termék, rendszercsomagok, hálózati hozzáférés stb.).
metadata No Tetszőleges kulcs-érték leképezés további metaadatokhoz.
allowed-tools No A képesség által használható előre jóváhagyott eszközök szóközzel tagolt listája. Kísérleti – a támogatás az ügynök implementációitól függően változhat.

Az előtér utáni Markdown-törzs tartalmazza a jártassági utasításokat – részletes útmutatást, példákat a bemenetekre és kimenetekre, gyakori peremes eseteket vagy bármilyen olyan tartalmat, amely segít az ügynöknek a feladat végrehajtásában. Tartsa SKILL.md 500 sor alatt, és a részletes referenciaanyagot külön fájlokba helyezze át.

Fokozatos közzététel

Az Ügynökkészség négyfázisú progresszív közzétételi mintát használ a környezethasználat minimalizálásához:

  1. Hirdetés (képességenként~100 token) – A rendszer az egyes futtatások elején beszúrja a képességneveket és a leírásokat a rendszer parancssorába, így az ügynök tudja, hogy milyen képességek érhetők el.
  2. Betöltés (< 5000 token ajánlott) – Ha egy feladat megfelel egy képesség tartományának, az ügynök meghívja az load_skill eszközt, hogy részletes útmutatással kérje le a teljes SKILL.md törzset.
  3. Olvasási erőforrások (igény szerint) – Az ügynök meghívja az read_skill_resource eszközt, hogy csak szükség esetén kérje le a kiegészítő fájlokat (referenciákat, sablonokat, eszközöket).
  4. Szkriptek futtatása (szükség szerint) – Az ügynök a run_skill_script eszközt használja a készséghez mellékelt szkriptek futtatására.

Ez a minta lean tartja az ügynök környezeti ablakát, miközben igény szerint hozzáférést biztosít a mély szaktudáshoz.

Megjegyzés:

load_skill mindig meg van hirdetve. read_skill_resource csak akkor van meghirdetve, ha legalább egy képesség rendelkezik erőforrásokkal. run_skill_script csak akkor van meghirdetve, ha legalább egy képesség rendelkezik szkriptekkel.

Ügynök kompetenciáinak fejlesztése

A készségekkel való munka három építőelemből áll:

  • Szolgáltató - AgentSkillsProvider A (C#) vagy SkillsProvider (Python) egy olyan környezetszolgáltató, amely egy ügynök számára teszi elérhetővé a készségeket. Meghirdeti a rendszer parancssorában elérhető készségeket, és regisztrálja az ügynök által a készségek betöltéséhez, az erőforrások olvasásához és a szkriptek futtatásához használt eszközöket.
  • Források – a forrás készségeket biztosít a szolgáltatónak. A készségek több forrástípusból is származhatnak:
    • Fájlalapú – a fájlrendszerkönyvtárak fájljaiból SKILL.md felderített készségek.
    • Kódban definiált – a készségek közvetlenül a kódban vannak definiálva a AgentInlineSkill (C#) vagy a InlineSkill (Python) használatával.
    • Osztályalapú – a AgentClassSkill<T> (C#) vagy ClassSkill (Python) osztályból származó osztályba zárt képességek.
    • MCP-alapú – az MCP (Model Context Protocol) kiszolgálókon (C#) vagy UseMcpSkills (Python) keresztül MCPSkillsSource felderített készségek.
  • Builder - AgentSkillsProviderBuilder A (C#) több forrást állít össze egyetlen szolgáltatóba, aggregációt, deduplikációt, gyorsítótárazást és opcionális szűrést alkalmazva. A Pythonban közvetlenül hozhat létre forrásosztályokat, például AggregatingSkillsSource, FilteringSkillsSource és DeduplicatingSkillsSource.

A következő szakaszok bemutatják, hogyan hozhat létre készségeket az egyes forrástípusokhoz, majd hogyan kombinálhatja a forrásokat, és hogyan hozhat létre egy szolgáltatót belőlük.

Ügynökkészségek használata a Harness Agenttel

Egy egyszerű ügynökkel hozzon létre egy készségszolgáltatót, adja hozzá az ügynök kontextusszolgáltatóihoz, és szükség esetén állítson össze eszközjóváhagyási köztes réteget. A Harness Agent létrehozhatja vagy hozzáadhatja a szolgáltatót az alapértelmezett konfiguráció részeként.

HarnessAgent alapértelmezés szerint tartalmazza a AgentSkillsProvider elemet, és felismeri a fájlalapú készségeket a Directory.GetCurrentDirectory() helyről. Másik forrás használatához állítsa be a következőt HarnessAgentOptions.AgentSkillsSource:

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

AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
{
    AgentSkillsSource = new AgentFileSkillsSource(
        Path.Combine(AppContext.BaseDirectory, "skills")),
    ToolApprovalAgentOptions = new ToolApprovalAgentOptions
    {
        // Auto-approve load_skill and read_skill_resource, but not run_skill_script.
        AutoApprovalRules = [AgentSkillsProvider.ReadOnlyToolsAutoApprovalRule],
    },
    ChatOptions = new ChatOptions
    {
        Instructions = "Use the available skills when they match the task.",
    },
});

DisableAgentSkillsProvider alapértelmezés szerint false. Állítsa be úgy, hogy true eltávolítsa a beépített szolgáltatót. AgentSkillsSource lecseréli az alapértelmezett aktuális könyvtárforrást, de nem teszi elérhetővé AgentSkillsProviderOptions. Ha például olyan szolgáltatói beállításokra van szüksége, mint a DisableLoadSkillApproval, tiltsa le a beépített szolgáltatót, és adja hozzá a konfigurált AgentSkillsProvider elemet a(z) HarnessAgentOptions.AIContextProviders használatával.

A fájlalapú készségek szkriptjeinek futtatásához adjon át egy AgentFileSkillScriptRunner delegáltat a AgentFileSkillsSource konstruktor második argumentumaként. Futtatókörnyezet nélkül a szkript végrehajtása kérés esetén meghiúsul.

Mindhárom készségeszközhöz alapértelmezés szerint jóváhagyásra van szükség. A hámeszköz-jóváhagyási köztes szoftver alapértelmezés szerint engedélyezve van, de az alapértelmezett beállítások nem hagyják jóvá automatikusan az eszközöket. Használjon AgentSkillsProvider.ReadOnlyToolsAutoApprovalRule vagy AgentSkillsProvider.AllToolsAutoApprovalRule csak megbízható képességforrásokat.

Az ügynöki készségek opcionálisan engedélyezhetők a következőhöz: create_harness_agent. Adja át a(z) skills_paths értéket a fájlalapú felderítéshez:

from pathlib import Path

from agent_framework import SkillsProvider, create_harness_agent

agent = create_harness_agent(
    client=client,
    agent_instructions="Use the available skills when they match the task.",
    skills_paths=Path(__file__).parent / "skills",
    # Auto-approve load_skill and read_skill_resource, but not run_skill_script.
    auto_approval_rules=[SkillsProvider.read_only_tools_auto_approval_rule],
)

session = agent.create_session()
result = await agent.run("Use the appropriate skill for this task.", session=session)

A skills_paths elfogad egy str-et vagy Path-t, illetve ezek sorozatát. Ha mind a skills_provider, mind a skills_paths értéke None (ezek az alapértelmezett értékek), a tesztkeretrendszer nem ad hozzá egy SkillsProvider elemet. Mindkét paraméter kombinálható kódalapú és fájlalapú készségekkel.

A skills_paths parancsikon SkillsProvider.from_paths() hoz létre script_runner nélkül. Ha a fájlalapú készségeknek szkripteket kell végrehajtaniuk, hozza létre saját maga a szolgáltatót a SkillsProvider.from_paths(..., script_runner=...) használatával, és adja át a skills_provider paraméteren keresztül.

Mindhárom készségeszközhöz alapértelmezés szerint jóváhagyásra van szükség. Mivel a keretrendszer alapértelmezés szerint telepíti a ToolApprovalMiddleware elemet, minden futtatásnál adjon meg egy munkamenetet, és megbízható, csak olvasható vagy minden eszközre kiterjedő jóváhagyási szabályzatok esetén használja a auto_approval_rules elemet.

A csomagolt Go-hám jelenleg nem érhető el. Regisztrálja a Go készségszolgáltatót a agent.Config.ContextProviders elemben, és közvetlenül állítsa össze a jóváhagyási middleware-t.

Fájlalapú készségek

Hozzon létre egy AgentSkillsProvider , a készségeit tartalmazó könyvtárra mutató mutatót, és adja hozzá az ügynök környezetszolgáltatóihoz. Adjon át egy szkriptfuttatót a képességkönyvtárakban található fájlalapú szkriptek végrehajtásának engedélyezéséhez:

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

Warning

DefaultAzureCredential a fejlesztéshez kényelmes, de a termelési környezetben gondos megfontolást igényel. Éles környezetben fontolja meg egy adott hitelesítő adat (pl. ManagedIdentityCredential) használatát a késési problémák elkerülése, a hitelesítő adatok nem szándékos próbálgatásának és a tartalék mechanizmusokból eredő esetleges biztonsági kockázatok elkerülése érdekében.

Több készségkönyvtár

A szolgáltatót egyetlen szülőkönyvtárra irányíthatja – az azt tartalmazó SKILL.md alkönyvtárak automatikusan felderíthetők képességként:

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

Vagy adja meg az elérési utak listáját több gyökérkönyvtár kereséséhez:

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

A szolgáltató legfeljebb két szint mélyére keres.

Erőforrás- és szkriptfelderítés testreszabása

A szolgáltató alapértelmezés szerint felismeri a bővítményekkel.md, , .json, .yaml, .yml, .csv, , .xmlés .txt szkriptekkel rendelkező erőforrásokat, valamint a bővítményekkel, .py, , .js, .sh.ps1és .csszkriptekkel .csxrendelkező erőforrásokat. Az egyes készségkönyvtárakban legfeljebb két szinten keres. Az alábbi alapértelmezett beállítások módosítására használható AgentFileSkillsSourceOptions :

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

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

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

ResourceFilter és ScriptFilter megkapja AgentFileSkillFilterContext a képesség nevét és a fájl relatív elérési útját, így hely, elnevezési konvenció vagy bármilyen egyéni logika alapján korlátozhatja a fájlokat.

Szkript végrehajtása

A fájlalapú szkriptek végrehajtásának engedélyezéséhez adja át a(z) SubprocessScriptRunner.RunAsync elemet szkriptfuttatóként:

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

SubprocessScriptRunner.RunAsync nagyjából megegyezik a következő értékével:

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

A futó minden felderített szkriptet helyi alfolyamatként futtat. A fájlalapú szkriptek az argumentumokat sztringek JSON-tömbjeként várják el – minden tömbelem pozícióbeli parancssori argumentum lesz.

Warning

SubprocessScriptRunner csak szemléltetés céljából van megadva. Éles használat esetén fontolja meg a következő hozzáadását:

  • Sandboxing (például konténerek vagy elkülönített végrehajtási környezetek)
  • Erőforráskorlátok (CPU, memória, fali óra időtúllépése)
  • Végrehajtható szkriptek bemeneti ellenőrzése és engedélyezési listázása
  • Strukturált naplózás és ellenőrzési nyomok

Fájlalapú készségek

A gyár használatával felderítheti a SkillsProvider.from_paths() fájlokat tartalmazó SKILL.md könyvtárakban található készségeket, és hozzáadhatja a szolgáltatót az ügynök környezetszolgáltatóihoz:

import os
from pathlib import Path

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

Több készségkönyvtár

A szolgáltatót egyetlen szülőkönyvtárra irányíthatja – az azt tartalmazó SKILL.md alkönyvtárak automatikusan felderíthetők képességként:

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

Vagy adja meg az elérési utak listáját több gyökérkönyvtár kereséséhez:

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

A szolgáltató legfeljebb két szint mélyére keres.

Erőforrás- és szkriptfelderítés testreszabása

Alapértelmezés szerint az erőforrások a references/ és assets/ alkönyvtárakból, a szkriptek pedig a scripts/ könyvtárból kerülnek felderítésre, a agentskills.io specification szerint. A felismert erőforrásbővítmények a következők .md: , .json, .yaml, .yml, .csv, .xmlés .txt. Az egyes készségkönyvtárakban legfeljebb két szinten keres. Használja a resource_extensions, script_extensions, search_depth, resource_filter és script_filter elemeket a felderítés testreszabásához:

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    resource_extensions=(".md", ".txt"),
    script_extensions=(".py", ".sh"),
    search_depth=3,  # Search up to 3 levels deep (default is 2)
    resource_filter=lambda skill_name, path: path.startswith("references/"),
    script_filter=lambda skill_name, path: path.startswith("scripts/"),
)

resource_filter A script_filter predikátumok megkapják a képesség nevét és a fájl relatív elérési útját, így hely, elnevezési konvenció vagy bármilyen egyéni logika alapján korlátozhatja a fájlokat. A "." használatával az alkönyvtárak mellett a skill gyökérszintjén található fájlokat is belefoglalhatja.

Szkript végrehajtása

A fájlalapú szkriptek végrehajtásának engedélyezéséhez adjon át egy paramétert script_runner a SkillsProvider.from_paths()-nak. Bármely, a SkillScriptRunner protokollnak megfelelő szinkronizálás vagy aszinkron hívás használható:

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

A futó megkapja a feloldott FileSkill, FileSkillScriptés egy opcionális args argumentumot. A fájlalapú szkriptek az argumentumokat sztringek JSON-tömbjeként várják el – minden tömbelem pozícióbeli parancssori argumentum lesz. A szkripteket a rendszer automatikusan felderíti az .py egyes képességkönyvtárak alkönyvtárában lévő fájlokbólscripts/.

Warning

A fenti futó csak bemutató célokra érhető el. Éles használat esetén fontolja meg a következő hozzáadását:

  • Sandboxing (például konténerek, seccomp, vagy firejail)
  • Erőforráskorlátok (CPU, memória, fali óra időtúllépése)
  • Végrehajtható szkriptek bemeneti ellenőrzése és engedélyezési listázása
  • Strukturált naplózás és ellenőrzési nyomok

Megjegyzés:

Ha a parancsfájlokkal kapcsolatos fájlalapú ismeretek vannak megadva, de nincs script_runner beállítva, SkillsProvider a szkript végrehajtásának megkísérlésekor hiba merül fel.

Fájlalapú készségek

A Go-ügynökök a(z) agent/skills csomagon keresztül támogatják a képességeket. A készségek ugyanazt a progresszív közzétételi mintát követik: hirdesse –> betöltse –> olvassa el az erőforrásokat –> szkripteket futtasson.

Fedezze fel a lemezen lévő fájlokból származó SKILL.md készségeket, és regisztrálja a képességszolgáltatót ügynökkörnyezet-szolgáltatóként:

import (
    "os"

    "github.com/microsoft/agent-framework-go/agent"
    "github.com/microsoft/agent-framework-go/provider/foundryprovider"
    "github.com/microsoft/agent-framework-go/agent/skills"
    "github.com/microsoft/agent-framework-go/agent/skills/fsskills"
)

skillsRoot, _ := os.OpenRoot("skills")
defer skillsRoot.Close()

skillsProvider := skills.NewContextProvider(skills.ContextProviderOptions{
    Sources: []skills.Source{
        fsskills.NewSourceOptions(fsskills.SourceOptions{}, skillsRoot.FS()),
    },
})

a := foundryprovider.NewAgent(endpoint, token, foundryprovider.ModelDeployment(model), foundryprovider.AgentConfig{
    Instructions: "You are a helpful assistant.",
    Config: agent.Config{
        ContextProviders: []agent.ContextProvider{skillsProvider},
    },
})

Kódalapú készségek

A SKILL.md fájlokból felderített fájlalapú képességek mellett a képességeket teljes mértékben kódban is definiálhatja AgentInlineSkill használatával. A kód által definiált készségek akkor hasznosak, ha:

  • A készségtartalmak dinamikusan jönnek létre (például egy adatbázisból vagy környezetből olvasnak).
  • A készségdefiníciókat az őket használó alkalmazáskód mellett szeretné megőrizni.
  • Olyan erőforrásokra van szüksége, amelyek olvasási időben hajtják végre a logikát a statikus fájlok kiszolgálása helyett.
  • A képességdefiníciókat futásidőben kell létrehozni az adatokból – például egy személyre szabott készséget kell létrehozni az egyes felhasználói munkamenetekhez a szerepkörük vagy engedélyük alapján.
  • A képességnek a híváshely állapotánál (helyi változók, lezárások) kell lezárnia ahelyett, hogy egy DI-tárolóból oldja fel a szolgáltatásokat.

Alapszintű kódismeret

Hozzon létre egy AgentInlineSkill nevet, leírást és útmutatást. Erőforrásokat csatoljon a .AddResource() használatával.

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

Dinamikus erőforrások

Adjon át egy gyári delegáltat .AddResource() a tartalom futásidőben történő kiszámításához. A meghatalmazott minden alkalommal meghívódik, amikor az ügynök beolvassa az erőforrást:

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

Kód által definiált szkriptek

A meghatalmazottak végrehajtható szkriptként való regisztrálására szolgál .AddScript() . A kód által definiált szkriptek közvetlen delegált hívásokként futnak a folyamatban . Nincs szükség szkriptfuttatóra. A meghatalmazott beírt paraméterei automatikusan JSON-sémává alakulnak, amelyet az ügynök argumentumok átadására használ:

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

Megjegyzés:

Ha egyetlen szolgáltatóban szeretné kombinálni a kód által definiált készségeket a fájlalapú vagy osztályalapú készségekkel, használja a következőt AgentSkillsProviderBuilder : Provider construction.

A SKILL.md fájlokból felderített fájlalapú készségek mellett a képességeket teljes mértékben Python kódban is definiálhatja a InlineSkill használatával. A kód által definiált készségek akkor hasznosak, ha:

  • A készségtartalmak dinamikusan jönnek létre (például egy adatbázisból vagy környezetből olvasnak).
  • A készségdefiníciókat az őket használó alkalmazáskód mellett szeretné megőrizni.
  • Olyan erőforrásokra van szüksége, amelyek olvasási időben hajtják végre a logikát a statikus fájlok kiszolgálása helyett.
  • A képességdefiníciókat futásidőben kell létrehozni az adatokból – például egy személyre szabott készséget kell létrehozni az egyes felhasználói munkamenetekhez a szerepkörük vagy engedélyük alapján.
  • Egy képességnek a hívási hely állapotát kell körbezárnia (helyi változókat, closure-öket), nem pedig a szolgáltatásokat a(z) **kwargs használatával feloldania.

Alapszintű kódismeret

Hozzon létre egy InlineSkill példányt egy SkillFrontmatter-gyel (amely a nevet és a leírást tartalmazza) és utasítástartalommal. Opcionálisan csatolhat InlineSkillResource példányokat statikus tartalommal.

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)

Dinamikus erőforrások

@skill.resource A dekorátor használatával regisztrálhat egy függvényt erőforrásként. A függvényt minden alkalommal meghívjuk, amikor az ügynök beolvassa az erőforrást, így up-to-date adatokat tud visszaadni. A szinkronizálási és az aszinkron függvények egyaránt támogatottak:

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

Ha a dekoratőr argumentumok nélkül van használatban (@skill.resource), a függvény neve lesz az erőforrás neve, és a dokumentáció lesz a leírás. Használja a @skill.resource(name="...", description="...")-t, hogy explicit módon állítsa be őket.

Kód által definiált szkriptek

Használja a @skill.script dekorátort, hogy egy függvényt végrehajtható szkriptként regisztráljon egy képességben. A kód által definiált szkriptek folyamatban futnak, és nem igényelnek szkriptfuttatót. A szinkronizálási és az aszinkron függvények egyaránt támogatottak:

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

Ha a dekoratőr argumentumok nélkül használatos (@skill.script), akkor a függvény neve lesz a szkript neve, és a dokumentációs sztring lesz a leírás. A függvény beírt paraméterei automatikusan JSON-sémává alakulnak, amelyet az ügynök argumentumok átadására használ.

A fájlokból SKILL.md felderített fájlalapú képességek mellett a képességeket teljes egészében a Go-kódban is definiálhatja:

skill := &skills.Skill{
    Frontmatter: skills.Frontmatter{
        Name:        "unit-converter",
        Description: "Convert between common units using a multiplication factor.",
    },
    GetContent: func(context.Context) (string, error) {
        return "Use this skill when the user asks to convert between units.", nil
    },
    Resources: []skills.Resource{
        {
            Name:        "conversion-table",
            Description: "Lookup table of multiplication factors.",
            Read: func(context.Context) (any, error) {
                return conversionTable, nil
            },
        },
    },
    Scripts: []skills.Script{
        {
            Name:        "convert",
            Description: "Multiplies a value by a conversion factor. Pass value and factor as positional string arguments: [\"<value>\", \"<factor>\"].",
            Run: func(_ context.Context, _ *skills.Skill, args []string) (any, error) {
                if len(args) != 2 {
                    return nil, fmt.Errorf("expected value and factor")
                }
                value, err := strconv.ParseFloat(args[0], 64)
                if err != nil {
                    return nil, err
                }
                factor, err := strconv.ParseFloat(args[1], 64)
                if err != nil {
                    return nil, err
                }
                return map[string]any{
                    "value":  value,
                    "factor": factor,
                    "result": value * factor,
                }, nil
            },
        },
    },
}

provider := skills.NewContextProvider(skills.ContextProviderOptions{
    Skills: []*skills.Skill{skill},
})

GetContent csak akkor tölti be a készségre vonatkozó utasításokat, amikor az ügynök hív load_skill. A szkriptek helyzet szerinti, parancssori stílusú karakterlánc-argumentumokat kapnak, például ["26.2", "1.60934"], és ezeket az argumentumokat a szkript igényei szerint dolgozhatják fel.

Tip

A teljes futtatható mintákhoz tekintse meg a készségekre vonatkozó példákat .

Osztályalapú készségek

Az osztályalapú képességek lehetővé teszik, hogy az összes készségösszetevőt – név, leírás, utasítások, erőforrások és szkriptek – egyetlen C#-osztályba csomagolja. Így egyszerűen csomagolhatók és terjeszthetők NuGet-csomagokként – a csapatok egymástól függetlenül hozhatnak létre és szállíthatnak készségeket, a felhasználók pedig egyetlen dotnet add package hívással .UseSkill() adhatják hozzá őket. Származtassa a AgentClassSkill<T>-ből (ahol az osztály a T), és lássa el annotációkkal a tulajdonságokat [AgentSkillResource] és a metódusokat [AgentSkillScript] az automatikus felfedezéshez.

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

Az osztályalapú jártasság regisztrálása a következővel AgentSkillsProvider:

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

Amikor az [AgentSkillResource] attribútumot egy tulajdonságra vagy metódusra alkalmazza, annak visszatérési értéke lesz az erőforrás tartalma, amikor az ügynök beolvassa az erőforrást – olyan módszert használjon, amikor a tartalmat olvasási időben kell kiszámítani. Amikor [AgentSkillScript] egy metódusra alkalmazza a rendszer, a metódus akkor lesz meghívva, amikor az ügynök meghívja a szkriptet. [Description] használja a System.ComponentModel minden egyes erőforrás és szkript leírására az ügynök számára.

Megjegyzés:

AgentClassSkill<T> emellett támogatja a Resources és Scripts gyűjteményekként való felülbírálást olyan forgatókönyvek esetében, ahol az attribútumalapú felderítés nem megfelelő.

Osztályalapú készségek

Az osztályalapú képességek lehetővé teszik, hogy az összes készségösszetevőt – nevet, leírást, utasításokat, erőforrásokat és szkripteket – egyetlen Python osztályba csomagolja. Ez megkönnyíti a PyPI-csomagok csomagolását és terjesztését – a csapatok egymástól függetlenül hozhatnak létre és szállíthatnak készségeket, és a felhasználók egyetlen pip install hívással SkillsProvider() adhatják hozzá őket. Hozzon létre egy ClassSkill leszármazottat, majd használja a @ClassSkill.resource és @ClassSkill.script dekorátorokat az automatikus felderítéshez:

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

Az osztályalapú jártasság regisztrálása a következővel SkillsProvider:

from agent_framework import SkillsProvider

skill = UnitConverterSkill()
skills_provider = SkillsProvider(skill)

Ha a @ClassSkill.resource elemet paraméterek nélküli dekorátorként használják, a metódus neve lesz az erőforrás neve (ahol az aláhúzásjelek kötőjelekké alakulnak), a docstring pedig a leírás. Használja a @ClassSkill.resource(name="...", description="...")-t, hogy explicit módon állítsa be őket. Ugyanez a minta vonatkozik a @ClassSkill.script.

Az erőforrások definiálhatók normál metódusként vagy @property leíróként. A @property használatakor helyezze a @property elemet elsőként, a @ClassSkill.resource elemet pedig másodikként. Az erőforrások visszatérési értékei az első elérést követően gyorsítótárba kerülnek.

Megjegyzés:

ClassSkill emellett támogatja a resources és scripts tulajdonságok explicit felülírását is, hogy közvetlenül InlineSkillResource és InlineSkillScript példányokat adjanak vissza olyan esetekben, amikor a dekorátoralapú felderítés nem megfelelő.

MCP-alapú készségek

Megjegyzés:

Az MCP-alapú készségekhez a Microsoft.Agents.AI.Mcp NuGet-csomag szükséges. Az MCP skills API kísérleti jellegű, és a jövőbeli kiadásokban változhat.

A képességek olyan MCP-kiszolgálókról (Model Context Protocol) fedezhetők fel, amelyek az URI-sémában teszik elérhetővé a skill:// képességi erőforrásokat. Az MCP-kiszolgáló egy skill://index.json felderítési dokumentumon keresztül hirdeti a készségeket, a keretrendszer pedig igény szerint lekéri a képességtartalmakat.

Az MCP-alapú készségek két indexbejegyzés-típust támogatnak:

  • skill-md - A képesség SKILL.md és a testvér erőforrásai igény szerint lekérhetők az MCP-kiszolgálóról.
  • archive - A képesség egyetlen csomagolt archívumként (ZIP, TAR vagy gzip-tömörített TAR) osztja el, amelyet helyileg tölt le és csomagol ki.

Alapszintű használat

Használja a(z) UseMcpSkills kiterjesztési metódust a(z) AgentSkillsProviderBuilder elemen egy MCP-képességforrás hozzáadásához:

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

Archív típusú készségek

Az archívumtípusú képességekhez használja a AgentMcpSkillsSourceOptions csomagból származó Microsoft.Agents.AI.Mcp elemet a kibontási működés konfigurálásához:

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 a következő tulajdonságokat teszi elérhetővé az archív kinyerés szabályozásához:

  • ArchiveSkillsDirectory - A kibontott archívumok báziskönyvtára. Alapértelmezés szerint az aktuális munkakönyvtáron belüli egyedi alkönyvtárat használ, amelyet forráspéldányonként hoz létre, hogy elkerülje a több forrás közötti ütközéseket.
  • ArchiveResourceExtensions - A kinyert archívumokban lévő erőforrások engedélyezett bővítményei. Alapértelmezett érték: .md, .json, .yaml, .yml, .csv. .xml.txt
  • ArchiveResourceSearchDepth - Milyen mélyre kell keresni az erőforrásokat az egyes kinyert képességkönyvtárakban. Alapértelmezett érték: 2.
  • ArchiveMaxFileCount - Archívumonkénti fájlok maximális száma. A rendszer kihagyja a korlátot meghaladó archívumokat. Alapértelmezett érték: 20.
  • ArchiveMaxSizeBytes - Maximális letöltési méret archívumonként. Alapértelmezett érték: 1 MB.
  • ArchiveMaxUncompressedSizeBytes - Az összes tömörítetlen méret maximális száma archívumonként. Alapértelmezett érték: 1 MB.

Important

Az archív típusú képességekbe csomagolt szkriptek soha nem lesznek végrehajtva. Ez szándékos biztonsági intézkedés – a távoli MCP-kiszolgálókról végrehajtható tartalmak explicit megbízhatóságot igényelnek.

MCP-alapú készségek

Megjegyzés:

Az MCP-alapú készségek kísérleti jellegűek, és a jövőbeli kiadásokban változhatnak. A(z) MCPSkillsSource használata a(z) FutureWarning funkciójelző alatt egy MCP_SKILLS elemet ad ki.

A képességek olyan MCP-kiszolgálókról (Model Context Protocol) fedezhetők fel, amelyek az URI-sémában teszik elérhetővé a skill:// képességi erőforrásokat. Az MCP-kiszolgáló egy skill://index.json felderítési dokumentumon keresztül hirdeti a készségeket, a keretrendszer pedig igény szerint SKILL.mdlekéri az egyes képességek törzsétresources/read.

Csomagolja az MCPClientSession elemet a(z) MCPSkillsSource elembe, majd adja át a(z) SkillsProvider elemnek:

import os
from agent_framework import Agent, MCPSkillsSource, SkillsProvider, ToolApprovalMiddleware
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamable_http_client

mcp_url = os.environ["MCP_SKILLS_SERVER_URL"]

# Connect to the MCP server over streamable HTTP
async with streamable_http_client(url=mcp_url) as (read, write, _), ClientSession(read, write) as session:
    await session.initialize()

    # MCPSkillsSource reads skill://index.json and creates one skill per
    # skill-md entry; SKILL.md bodies are fetched on demand.
    skills_provider = SkillsProvider(MCPSkillsSource(client=session))

    client = FoundryChatClient(
        project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
        model=os.environ.get("FOUNDRY_MODEL", "gpt-4o-mini"),
        credential=AzureCliCredential(),
    )

    async with Agent(
        client=client,
        instructions="You are a helpful assistant. Use available skills to answer the user.",
        context_providers=[skills_provider],
        middleware=[ToolApprovalMiddleware(auto_approval_rules=[SkillsProvider.all_tools_auto_approval_rule])],
    ) as agent:
        response = await agent.run("...")

Megjegyzés:

A Python MCPSkillsSource csak skill-md az indexbejegyzéseket támogatja (a más típusú indexbejegyzések csendben kimaradnak). A .NET implementációtól eltérően nem támogatja az archív típusú készségeket. Ha skill://index.json hiányzik, olvashatatlan, üres vagy nem elemezhető, a forrás üres listát ad vissza.

Important

Egy külső MCP-kiszolgáló szabályozza, hogy milyen készségtartalom – beleértve azokat az utasításokat és szkripteket is, amelyeket az ügynök futtathat – jut el az ügynökhöz. A MCPSkillsSource elemet csak olyan kiszolgálókhoz csatlakoztassa, amelyeket ellenőrzött és megbízhatónak tart, és a válaszaikat nem megbízható bemenetként kezelje.

Képességforrások

Egy AgentSkillsProvider egy vagy több forrásból – vagyis AgentSkillsSource-t megvalósító objektumokból – lekéri a készségeket. A források két kategóriába sorolhatók: a készségeket feltáró vagy tároló levélcsomópont-források (például AgentFileSkillsSource a fájlalapú készségekhez), valamint a másik forrás kimenetét átalakító dekorátorok (aggregáció, deduplikáció, gyorsítótárazás és szűrés). Egyéni forrást is létrehozhat.

Minden forrás egyetlen metódust implementál – GetSkillsAsync(AgentSkillsSourceContext context, CancellationToken cancellationToken = default). Az AgentSkillsSourceContext aktuális kéréssel kapcsolatos információk:

  • Agent – az AIAgent szakértelmet igénylő példány.
  • Session - a AgentSession meghíváshoz társított, vagy null ha nincs munkamenet.

Ez a kontextus a teljes forrásfolyamatban elérhető, így egy FilteringAgentSkillsSource predikátum vagy egy egyéni forrás erre építheti a logikáját – például a kérelmező ügynöktől függően eltérő készségkészletet adhat vissza.

Levélcsomópontok forrásai

AgentFileSkillsSource

Felfedezi a lemezen található SKILL.md fájlokból származó készségeket. Elfogad egy vagy több könyvtár elérési útját, egy opcionális szkriptfuttatót és egy opcionális AgentFileSkillsSourceOptions elemet (amely a Fájlalapú készségek című részben van dokumentálva).

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

AgentInMemorySkillsSource

A memóriában burkolja a AgentSkill példányokat (kódban definiált vagy osztályalapú).

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

Kombinátorok

AggregatingAgentSkillsSource

Több forrást egyesít egybe. A készségeket regisztrációs sorrendben adja vissza, deduplikáció vagy szűrés nélkül.

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

Decorators

A dekorátorok egy belső forrás köré épülnek, és átalakítják a kimenetét. Láncba kapcsolhatók egy feldolgozási folyamat létrehozásához.

DeduplicatingAgentSkillsSource

Eltávolítja a duplikált képességneveket (nem különbözteti meg a kis- és nagybetűket, az első előfordulás marad meg). A rendszer figyelmeztetési szinten naplózza az ismétlődéseket.

var deduplicated = new DeduplicatingAgentSkillsSource(innerSource);

CachingAgentSkillsSource

Gyorsítótárazza a belső forrás által visszaadott képességlistát. Az egyidejű hívók gyorsítótárkulcsonként vannak szerializálva, így egyszerre csak egy beolvasás fut. Elfogad opcionális CachingAgentSkillsSourceOptions:

  • RefreshInterval (TimeSpan?) – ha be van állítva, a gyorsítótárazott eredmények ezen intervallum után lejárnak, és a rendszer újra meghívja a belső forrást. Ha a(z) null érték van beállítva (ez az alapértelmezett), a gyorsítótárazott eredmények soha nem járnak le.
  • CacheIsolationKeySelector (Func<AgentSkillsSourceContext, string?>?) – egy gyorsítótárkulcsot ad vissza a gyorsítótárazott eredmények környezet szerinti elkülönítéséhez (például bérlőnként). Amikor null, az összes hívó egyetlen közös gyorsítótár-vödröt használ.
var cached = new CachingAgentSkillsSource(innerSource, new CachingAgentSkillsSourceOptions
{
    RefreshInterval = TimeSpan.FromMinutes(5)
});

FilteringAgentSkillsSource

Predikátumot alkalmaz a készségek belefoglalására vagy kizárására. A predikátum megkapja a skillt és egy AgentSkillsSourceContext elemet.

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

Egyéni források

Ha a beépített források nem fedik le a forgatókönyvet, implementálja a sajátját. Egy levélforrás alosztálya AgentSkillsSource (amely új forrásból, például adatbázisból vagy távoli szolgáltatásból készít készségeket), vagy egy olyan dekorátor alosztálya DelegatingAgentSkillsSource , amely átalakítja egy másik forrás kimenetét.

Levél forrása

Származzon a(z) AgentSkillsSource elemből, és valósítsa meg a(z) GetSkillsAsync elemet. Az AgentSkillsSourceContext argumentum lehetővé teszi, hogy a forrás az aktuális kéréshez szabja az eredményét – például a kérelmező ügynöktől függően más készségeket ad vissza. Írja felül a(z) Dispose(bool) elemet, ha a forrás olyan erőforrásokat kezel, mint például egy kliens vagy kapcsolat.

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

Egyéni dekorátor

Származzon innen: DelegatingAgentSkillsSource, hívja meg a következőt: InnerSource.GetSkillsAsync, majd alakítsa át, vagy figyelje meg az eredményt. Ez ugyanaz a minta, amelyet a beépített gyorsítótárazás, deduplikáció és szűrési dekorátorok használnak. Például egy dekoratőr, amely rögzíti, hogy kérésenként hány szakértelmet ad vissza az eredmény módosítása nélkül:

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

Mindkét egyéni forrás közvetlenül átadható a AgentSkillsProvider számára, vagy egy nagyobb folyamatba beágyazva, ugyanúgy, mint a beépített források.

Szolgáltató felépítése

AgentSkillsProvider az az összetevő, amely egy ügynök számára teszi elérhetővé a készségeket. Egy vagy több forrást burkol, és regisztrálja a load_skill, read_skill_resourceés run_skill_script az eszközöket. Háromféleképpen hozhat létre egyet:

  1. AgentSkillsProviderBuilder - több képességtípust állít össze egy szolgáltatóba automatikus összesítéssel, deduplikációval, gyorsítótárazással és választható szűréssel. A fájlalapú, kódalapú, osztályalapú és MCP-alapú készségeket kombináló forgatókönyvekhez ajánlott.
  2. Közvetlen forrásösszetétel – saját maga hozza létre a forrásfolyamatot a nyilvános AgentSkillsSource osztályok használatával. Nem kerül alkalmazásra automatikus gyorsítótárazás vagy deduplikáció – ön vezérli a teljes folyamatot. A legjobb, ha szabályoznia kell a rendelést, a feltételes logikát vagy az egyéni dekorátor viselkedését.
  3. Kényelmi konstruktorok – hozzon létre egy szolgáltatót közvetlenül egy fájlútvonalból vagy képességpéldányból. Automatikusan alkalmazza a deduplikációt és a gyorsítótárazást. A legjobb egyforrásos forgatókönyvekhez.

Az AgentSkillsProviderBuilder használata

Akkor használja AgentSkillsProviderBuilder , ha az alábbiak bármelyikére van szüksége:

  • Vegyes képességtípusok – egyetlen szolgáltató fájlalapú, kódalapú (AgentInlineSkill), osztályalapú (AgentClassSkill) és MCP-alapú készségeinek kombinálása.
  • Képességszűrés – a készségeket belefoglalhatja vagy kizárhatja egy predikátum használatával.

Vegyes képességtípusok

Több képességtípus kombinálása egyetlen szolgáltatóban UseFileSkill, UseSkill, UseMcpSkills és UseFileScriptRunner láncba kapcsolásával:

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

Képességek szűrése

Csak a feltételeknek megfelelő készségek befoglalására használható UseFilter – például egy megosztott címtárból való betöltéshez, de kizárja a kísérleti képességeket:

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

Források közvetlen összeállítása

Ha a felépítő nem kínálja a szükséges vezérlőelemet, hozza létre saját maga a forrásosztályokat, és adja át az így kapott folyamatot a AgentSkillsProvider részére. A rendelkezésre álló források és azok lehetőségeinek teljes listájáért tekintse meg a készségforrásokat .

Az alábbi példa egy összehasonlítható több forrásból álló folyamatot hoz létre, de explicit vezérlést biztosít az egyes dekorátorok felett:

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

Megjegyzés:

Ha a(z) ownsSource értéke true, akkor a szolgáltató felszabadításakor a teljes forrásfolyamat is felszabadul. Állítsa false értékre, ha a forrás életciklusát saját maga kezeli.

Kényelmi konstruktorok

Egyforrásos forgatókönyvek esetén használja közvetlenül a AgentSkillsProvider konstruktorokat. Ezek automatikusan alkalmazzák a deduplikációt és a gyorsítótárazást anélkül, hogy szerkesztőt vagy manuális forrásösszeállítást kellene igényelni.

Fájl elérési útja alapján:

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

Képességpéldányokból:

var skillsProvider = new AgentSkillsProvider(volumeConverterSkill, temperatureConverter);

Képességforrások

Egy SkillsProvider egy vagy több forrásból kér le készségeket – olyan objektumokból, amelyek a SkillsSource leszármazottai. A források két kategóriába sorolhatók: a készségeket feltáró vagy tároló levélcsomópont-források (például FileSkillsSource a fájlalapú készségekhez), valamint a másik forrás kimenetét átalakító dekorátorok (aggregáció, deduplikáció, gyorsítótárazás és szűrés). Egyéni forrást is létrehozhat.

Minden forrás egyetlen metódust implementál – async def get_skills(self, context: SkillsSourceContext) -> list[Skill]. Az SkillsSourceContext aktuális kéréssel kapcsolatos információk:

  • agent - az ügynök (SupportsAgentRun) szakértelmet kér.
  • session - a AgentSession meghíváshoz társított, vagy None ha nincs munkamenet.

Ez a kontextus végighalad a teljes forrásfolyamaton, így egy FilteringSkillsSource predikátum vagy egy egyéni forrás erre alapozhatja a logikáját – például a kérést küldő ügynöktől függően eltérő készségkészletet adhat vissza.

Levélcsomópontok forrásai

  • FileSkillsSource - felderíti a lemezen lévő fájlokból származó SKILL.md készségeket. Elfogad egy vagy több könyvtár elérési útját, egy opcionális script_runner elemet, valamint a resource_extensions című részben dokumentált felderítési beállításokat (script_extensions, search_depth, resource_filter, script_filter, ).
  • InMemorySkillsSource - a Skill példányokat (kódban definiált vagy osztályalapú) a memóriában burkolja.
  • MCPSkillsSource - felderíti az MCP-kiszolgáló készségeit (lásd az MCP-alapú készségeket).
from pathlib import Path
from agent_framework import FileSkillsSource, InMemorySkillsSource

file_source = FileSkillsSource(Path(__file__).parent / "skills", script_runner=my_runner)
in_memory_source = InMemorySkillsSource([volume_converter_skill, temperature_converter_skill])

Kombinátor

AggregatingSkillsSource több forrást egyesít egybe. A készségeket regisztrációs sorrendben adja vissza, deduplikáció vagy szűrés nélkül.

from agent_framework import AggregatingSkillsSource

aggregated = AggregatingSkillsSource([file_source, in_memory_source])

Decorators

A dekorátorok egy belső forrás köré épülnek, és átalakítják a kimenetét. Láncba kapcsolhatók egy feldolgozási folyamat létrehozásához.

  • DeduplicatingSkillsSource - eltávolítja a duplikált képességneveket (kis- és nagybetűk érzéketlenek, az első előfordulás nyer). A rendszer figyelmeztetési szinten naplózza az ismétlődéseket.
  • CachingSkillsSource - gyorsítótárazza a belső forrás által visszaadott képességlistát. Az ugyanazon gyorsítótárkulcs egyidejű hívói egyetlen járaton belüli lekérést osztanak meg, így a rendszer kulcsonként legfeljebb egyszer kérdezi le a belső forrást. Két választható kulcsszóargumentumot fogad el:
    • refresh_interval (timedelta | None) - ha be van állítva, a gyorsítótárazott lista elavultként lesz kezelve, ha régebbi az intervallumnál, így a következő hívás újra lekérdezi a belső forrást. Ha a(z) None érték van beállítva (ez az alapértelmezett), a gyorsítótárazott eredmények soha nem járnak le. Olyan belső források esetében hasznos, amelyek készségei a folyamat élettartama során változnak, például MCPSkillsSource.
    • cache_isolation_key_selector (Callable[[SkillsSourceContext], str | None]) – a gyorsítótárkulcsot a környezetből származtatva elkülöníti a gyorsítótárazott eredményeket (például ügynökenként vagy bérlőnként). A kulcsnak alacsony számosságúnak és stabilnak kell lennie. A(z) None visszaadása (vagy None hagyása) egyetlen közös gyorsítótárat használ.
  • FilteringSkillsSource - olyan predikátumot alkalmaz, amely magában foglalja vagy kizárja a készségeket. A predikátum megkapja a képességet ésSkillsSourceContext: Callable[[Skill, SkillsSourceContext], bool].
from datetime import timedelta
from agent_framework import (
    CachingSkillsSource,
    DeduplicatingSkillsSource,
    FilteringSkillsSource,
)

deduplicated = DeduplicatingSkillsSource(aggregated)

cached = CachingSkillsSource(
    deduplicated,
    refresh_interval=timedelta(minutes=5),
    cache_isolation_key_selector=lambda context: context.agent.name,
)

filtered = FilteringSkillsSource(
    cached,
    predicate=lambda skill, context: skill.frontmatter.name != "experimental-skill",
)

Egyéni források

Ha a beépített források nem fedik le a forgatókönyvet, implementálja a sajátját. Egy levélforrás alosztálya SkillsSource (amely új forrásból, például adatbázisból vagy távoli szolgáltatásból készít készségeket), vagy egy olyan dekorátor alosztálya DelegatingSkillsSource , amely átalakítja egy másik forrás kimenetét.

Levél forrása

Származzon a(z) SkillsSource elemből, és valósítsa meg a(z) get_skills elemet. Az SkillsSourceContext argumentum lehetővé teszi, hogy a forrás az aktuális kéréshez szabja az eredményét – például a kérelmező ügynöktől függően más készségeket ad vissza:

from agent_framework import Skill, SkillsSource, SkillsSourceContext

class TenantSkillsSource(SkillsSource):
    def __init__(self, store: "SkillStore") -> None:
        self._store = store

    async def get_skills(self, context: SkillsSourceContext) -> list[Skill]:
        # Use the requesting agent to decide which skills to load.
        tenant_id = context.agent.name or "default"
        return await self._store.get_skills_for_tenant(tenant_id)

Egyéni dekorátor

Származzon innen: DelegatingSkillsSource, hívja meg a következőt: self.inner_source.get_skills(context), majd alakítsa át, vagy figyelje meg az eredményt. Ez ugyanaz a minta, amelyet a beépített gyorsítótárazás, deduplikáció és szűrési dekorátorok használnak. Egy dekoratőr például naplózza, hogy kérésenként hány szakértelmet ad vissza az eredmény módosítása nélkül:

import logging
from agent_framework import DelegatingSkillsSource, Skill, SkillsSourceContext

logger = logging.getLogger(__name__)

class MetricsSkillsSource(DelegatingSkillsSource):
    async def get_skills(self, context: SkillsSourceContext) -> list[Skill]:
        skills = await self.inner_source.get_skills(context)
        logger.info("Returned %d skills to agent %s.", len(skills), context.agent.name)
        return skills

Mindkét egyéni forrás közvetlenül átadható a SkillsProvider számára, vagy egy nagyobb folyamatba beágyazva, ugyanúgy, mint a beépített források.

Szolgáltató felépítése

SkillsProvider az az összetevő, amely egy ügynök számára teszi elérhetővé a készségeket. Egy vagy több forrást burkol, és regisztrálja a load_skill, read_skill_resourceés run_skill_script az eszközöket. Háromféleképpen hozhat létre egyet:

  1. Képességpéldányokból – adjon át egy vagy Skill több készségsort a konstruktornak. A legjobb a kóddal definiált és osztályalapú készségekhez. Automatikusan alkalmazza a deduplikációt és a gyorsítótárazást.
  2. Fájlútvonalakból – használja a SkillsProvider.from_paths() factoryt. Leginkább az egyetlen forrásfájlon alapuló készségekhez alkalmas. Automatikusan alkalmazza a deduplikációt és a gyorsítótárazást.
  3. Közvetlen forrásösszetétel – saját maga hozza létre a forrásfolyamatot a nyilvános SkillsSource osztályok használatával, és adja át a konstruktornak. Ön vezérli a teljes folyamatot. Akkor ideális, ha kézben kell tartania a sorrendezést, a feltételes logikát, a gyorsítótárkulcsokat vagy a dekorátorok egyedi működését.

Képességpéldányokból

from agent_framework import SkillsProvider

# Single skill or a list of skills - deduplicated and cached automatically.
skills_provider = SkillsProvider(volume_converter_skill)
skills_provider = SkillsProvider([volume_converter_skill, temperature_converter_skill])

Fájlútvonalakból

from pathlib import Path
from agent_framework import SkillsProvider

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

Források közvetlen összeállítása

Ha teljes irányításra van szüksége, állítsa össze saját maga a forrásosztályokat, és adja át az így kapott feldolgozási folyamatot a(z) SkillsProvider számára. A rendelkezésre álló források és azok lehetőségeinek teljes listájáért tekintse meg a készségforrásokat .

Az alábbi példa egy több forrásból álló folyamatot hoz létre az egyes dekorátorok explicit vezérlésével. A példa helyőrző objektumokat használ:

  • volume_converter_skill – bármilyen InlineSkill példány, a kódban definiált készségek című részben bemutatott módon létrehozva.
  • temperature_converter_skill – bármely ClassSkill példány, amely az osztályalapú készségek részben bemutatott módon lett létrehozva.
  • my_runner – egy SkillScriptRunner hívható elem, a Szkript végrehajtása részben bemutatott módon definiálva.
from pathlib import Path
from agent_framework import (
    AggregatingSkillsSource,
    CachingSkillsSource,
    DeduplicatingSkillsSource,
    FileSkillsSource,
    InMemorySkillsSource,
    SkillsProvider,
)

# 1. Create the leaf sources
file_source = FileSkillsSource(Path(__file__).parent / "skills", script_runner=my_runner)
in_memory_source = InMemorySkillsSource([volume_converter_skill, temperature_converter_skill])

# 2. Aggregate them, then add deduplication and caching decorators
aggregated = AggregatingSkillsSource([file_source, in_memory_source])
deduplicated = DeduplicatingSkillsSource(aggregated)
cached = CachingSkillsSource(deduplicated)

# 3. Create the provider from the composed pipeline
skills_provider = SkillsProvider(cached)

Important

A hívó által megadott SkillsSource elemet változtatás nélkül használja fel: nem deduplikálja automatikusan, és nem csomagolja be egy elembe. Egy kontextusfüggő forrás automatikus gyorsítótárazása egyetlen közös tárolóban ahhoz vezethet, hogy az egyik ügynök vagy bérlő képességei egy másiknál is érvényesülnek. Amikor szüksége van ezekre, állítsa össze saját kezűleg a DeduplicatingSkillsSource és CachingSkillsSource elemeket (opcionálisan egy cache_isolation_key_selector elemmel). Az automatikus deduplikáció és a gyorsítótárazás csak akkor alkalmazható, ha a készségeket vagy a fájlútvonalakat közvetlenül adja meg (a fent említett 1. és 2. lehetőség esetén).

Vegyes képességtípusok

Egyesítse a fájlalapú, kódalapú és osztályalapú készségeket egy szolgáltatóban a következő használatával AggregatingSkillsSource:

from pathlib import Path
from agent_framework import (
    AggregatingSkillsSource,
    DeduplicatingSkillsSource,
    FileSkillsSource,
    InMemorySkillsSource,
    SkillsProvider,
)

temperature_converter_skill = TemperatureConverterSkill()

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

Képességek szűrése

Annak szabályozására használható FilteringSkillsSource , hogy az ügynök milyen készségeket lát. A predikátum minden egyes Skill elemet és a(z) SkillsSourceContext értéket kapja meg, és a(z) True értékkel tér vissza a készség felvételéhez. Például: ha egy megosztott könyvtárból szeretne készségeket betölteni, de egy kísérleti könyvtárat elrejteni:

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, context: skill.frontmatter.name != "experimental-tools",
        )
    )
)

Gyorsítótárazási viselkedés

Alapértelmezés szerint a létrehozó a forrás-folyamatot egy CachingAgentSkillsSource elembe csomagolja, amely gyorsítótárazza a mögöttes források által visszaadott készségek listáját. Miután az első kérés során megtörténik a készségek feloldása, a későbbi kérések a gyorsítótárazott listát használják fel a források újbóli lekérdezése nélkül. A gyorsítótárazás letiltásához (például fejlesztés közben, amikor a képességdefiníciók gyakran változnak) használja a builderen a DisableCaching() beállítást:

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

Megjegyzés:

A gyorsítótárazás letiltása akkor hasznos a fejlesztés során, ha a képességtartalmak gyakran változnak. Éles környezetben hagyja engedélyezve a gyorsítótárazást (az alapértelmezett) a jobb teljesítmény érdekében.

Gyorsítótárazási viselkedés

Alapértelmezés szerint a képességeszközök és utasítások gyorsítótárazva lesznek az első build után. Állítsa be a(z) disable_caching=True értéket, hogy minden meghíváskor kikényszerítse az újraépítést:

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

disable_caching a SkillsProvider kódban definiált és osztályalapú készségek konstruktorában is elérhető.

Ha a gyorsítótárazást engedélyezve szeretné tartani, de a készségeket időszakosan újra szeretné felfedezni (például amikor egy fájlalapú vagy MCP-forrás megváltozik a folyamat élettartama során), adja át a(z) cache_refresh_interval értéket. A beépített gyorsítótár elavultként lesz kezelve, ha régebbi az intervallumnál, ezért a következő futtatás újra lekérdezi a forrást:

from datetime import timedelta

skills_provider = SkillsProvider.from_paths(
    skill_paths=Path(__file__).parent / "skills",
    cache_refresh_interval=timedelta(minutes=5),
)

cache_refresh_interval csak a provider által belsőleg felépített gyorsítótárat érinti (készségekből vagy fájlútvonalakból); figyelmen kívül marad, ha disable_caching=True, és nincs hatással a hívó által megadott SkillsSource-re (ezekhez állítson össze saját CachingSkillsSource-t egy refresh_interval használatával).

Megjegyzés:

A gyorsítótárazás letiltása akkor hasznos a fejlesztés során, ha a képességtartalmak gyakran változnak. Éles környezetben hagyja engedélyezve a gyorsítótárazást (az alapértelmezett) a jobb teljesítmény érdekében.

Eszköz jóváhagyása

A AgentSkillsProvider által elérhetővé tett összes eszköz (load_skill, read_skill_resource, run_skill_script) alapértelmezés szerint jóváhagyást igényel. Ha egy eszközhívás jóváhagyást igényel, az ügynök szünetel, és azonnali végrehajtás helyett egy ToolApprovalRequestContent elemet ad vissza. Használjon UseToolApproval köztes szoftvereket automatikus jóváhagyási szabályokkal a megbízható műveletekre vonatkozó kérések szelektív megkerüléséhez:

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

Az összes készségeszköz automatikus jóváhagyása, beleértve a szkriptek végrehajtását is:

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

Adott eszközök jóváhagyásának letiltása

Az egyes eszközök jóváhagyásának letiltására használható AgentSkillsProviderOptions , és teljesen eltávolítja őket a jóváhagyási folyamatból:

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

Ha egyes eszközök jóváhagyást igényelnek, míg mások nem ugyanabban a válaszban, a modell mindkét típust egyszerre hívhatja meg. Állítsa be a(z) EnableNonApprovalRequiredFunctionBypassing elemet úgy, hogy a jóváhagyást nem igénylő eszközök azonnal fussanak, miközben a felhasználó csak a fennmaradó eszközök esetében kapjon kérdést:

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

Jóváhagyási kérelmek kezelése

Ha az eszközök jóváhagyást igényelnek (és az automatikus jóváhagyási szabály nem egyezik), az ügynök olyan elemeket ad ToolApprovalRequestContent vissza, amelyeket a folytatás előtt jóvá kell hagyni vagy el kell utasítani:

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

Szkripthiba részletei

Alapértelmezés szerint ha egy készségszkript végrehajtása meghiúsul, a kivétel továbbterjed a mögöttes FunctionInvokingChatClient elemre. Ha a IncludeDetailedErrors tulajdonsága true értékre van állítva, a kivételüzenet továbbításra kerül a modellnek, lehetővé téve, hogy az más argumentumokkal újrapróbálkozva önmagát korrigálja:

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

Ha nem tudja közvetlenül konfigurálni a(z) FunctionInvokingChatClient elemet, inkább a(z) AgentSkillsProviderOptions.IncludeDetailedErrors értéket állítsa be. Ez a kivételt a készségszolgáltató szintjén észleli, és a hibaüzenetet közvetlenül a modellnek adja vissza:

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

Warning

Bármelyik megközelítés közzéteheti a nyers kivétel részleteit a modell számára. A kivételüzenetek bizalmas információkat tartalmazhatnak, például kapcsolati sztringeket, fájlelérési utakat vagy belső szolgáltatásneveket. Emellett, ha a készségek vagy szkriptek nem megbízható forrásból származnak, egy rosszindulatúan kialakított szkript olyan kivételt dobhat, amelynek üzenetébe promptinjekciós tartalom van beágyazva.

A SkillsProvider (load_skill, read_skill_resource és run_skill_script) által elérhetővé tett összes eszköz alapértelmezés szerint jóváhagyást igényel. Ha egy eszközhívás jóváhagyást igényel, az ügynök szünetel, és azonnali végrehajtás helyett a jóváhagyási kérelmeket a result.user_input_requests elemen keresztül küldi vissza. Ön minden kérést request.to_function_approval_response(approved=...) jóváhagyja vagy elutasítja, és visszaküldi a válaszokat:

from textwrap import dedent
from agent_framework import Agent, Content, InlineSkill, Message, 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}"

# All skill tools require approval by default.
skills_provider = SkillsProvider(deployment_skill)

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)

    # Collect a response for every request and send them in one run so the
    # loop always makes progress.
    while result.user_input_requests:
        approval_responses: list[Content] = []
        for request in result.user_input_requests:
            if request.function_call is None:
                approval_responses.append(request.to_function_approval_response(approved=False))
                continue
            print(f"Approve {request.function_call.name}? Args: {request.function_call.arguments}")
            # In a real application, prompt the user here.
            approval_responses.append(request.to_function_approval_response(approved=True))

        result = await agent.run(Message(role="user", contents=approval_responses), session=session)

    print(result)

Ha egy eszközhívást elutasítanak (approved=False), az ügynök értesítést kap arról, hogy a felhasználó elutasított, és ennek megfelelően tud válaszolni.

Megbízható eszközök automatikus jóváhagyása

Ahelyett, hogy minden hívásnál jóváhagyást kérne, telepítse a ToolApprovalMiddleware elemet a SkillsProvider által biztosított statikus automatikus jóváhagyási szabályok egyikével. Ez lehetővé teszi, hogy az írásvédett eszközök automatikusan fussanak, miközben a rendszer továbbra is jóváhagyást kér a szkriptek futtatásához:

from agent_framework import Agent, SkillsProvider, ToolApprovalMiddleware

skills_provider = SkillsProvider(deployment_skill)

# Auto-approve read-only skill tools (load_skill, read_skill_resource).
# run_skill_script still requires explicit approval via result.user_input_requests.
approval_middleware = ToolApprovalMiddleware(
    auto_approval_rules=[SkillsProvider.read_only_tools_auto_approval_rule],
)

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

Két szabály érhető el:

  • SkillsProvider.read_only_tools_auto_approval_rule - csak az írásvédett eszközöket (load_skill, read_skill_resource) hagyja jóvá, miközben továbbra is rákérdez erre: run_skill_script
  • SkillsProvider.all_tools_auto_approval_rule - jóváhagy minden készségeszközt, beleértve run_skill_script (nincs szükség manuális jóváhagyási ciklusra).

Mindkét szabály elutasítja a hívásokat server_label, ezért a szolgáltató helyi eszközeire terjednek ki, és soha nem hagynak jóvá automatikusan egy azonos nevű üzemeltetett eszközt. A szabályok csak azokra az eszközökre vonatkoznak, amelyek továbbra is jóváhagyást igényelnek – az disable_*_approval alábbi argumentumokkal lemondott eszközök jóváhagyás nélkül futnak, függetlenül attól.

Adott eszközök jóváhagyásának letiltása

Megbízható készségek esetén adja meg a(z) disable_load_skill_approval, disable_read_skill_resource_approval és/vagy disable_run_skill_script_approval értéket, hogy az egyes eszközöket teljesen kihagyja a jóváhagyási folyamatból (ezek a(z) approval_mode="never_require" használatával vannak regisztrálva):

skills_provider = SkillsProvider(
    deployment_skill,
    disable_load_skill_approval=True,
    disable_read_skill_resource_approval=True,
    # disable_run_skill_script_approval remains False - scripts still require approval
)

Ezek az argumentumok a következőn SkillsProvider.from_paths()is elérhetők: .

Warning

Csak a megbízható forrásból származó készségek és szkriptek esetén tiltsa le a jóváhagyást, vagy hagyja jóvá automatikusan a szkriptek futtatását. A készségutasítások az ügynök környezetébe kerülnek, és run_skill_script végrehajtják a forrás által megadott kódot.

Testreszabott rendszerfelszólítás

Alapértelmezés szerint a képességszolgáltató egy rendszerkérést injektál, amely felsorolja a rendelkezésre álló készségeket, és utasítja az ügynököt, hogy használja load_skill és read_skill_resource. Ezt a kérést testre szabhatja:

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

Megjegyzés:

Az egyéni sablonnak tartalmaznia kell a(z) {skills} helyőrzőt a generált készséglista számára. A literális kapcsos zárójeleket escape-elni kell {{ és }}.

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

Megjegyzés:

Az egyéni sablonnak tartalmaznia kell a generált képességlistához tartozó {skills} helyőrzőt. Opcionálisan tartalmazhat {resource_instructions} (erőforrás-eszközmutató) és {runner_instructions} (szkripteszköz-tipp) helyőrzőket; ha jelen vannak, beépített útmutatóval töltik fel őket, és ha kihagyják őket, egyszerűen nem jelennek meg (a megfelelő eszközök továbbra is regisztrálva vannak). A literális kapcsos zárójeleket escape-elni kell {{ és }}.

Szolgáltatások és futtatókörnyezet argumentumainak injektálása

A készségerőforrás- és szkriptfüggvények a futtatókörnyezetben megadott külső alkalmazáskörnyezetet fogadják.

A készségerőforrás- és szkriptdelegáltak deklarálhatnak egy paramétert IServiceProvider , amelyet az Ügynök-keretrendszer automatikusan injektál. Ez lehetővé teszi, hogy a modulok igény szerint lekérjék a regisztrált alkalmazás-szolgáltatásokat.

Setup

Regisztrálja az alkalmazásszolgáltatásokat, és adja át a beépített IServiceProvider-t az ügynöknek a services paraméter segítségével.

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

Kóddal meghatározott készségek DI-vel

Deklarálja a(z) IServiceProvider elemet paraméterként a(z) AddResource vagy AddScript delegate-ekben – a keretrendszer automatikusan feloldja és beinjektálja, amikor az ügynök beolvas egy erőforrást, vagy szkriptet futtat:

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

Osztályalapú készségek DI-vel

Lássa el a metódusokat [AgentSkillResource] vagy [AgentSkillScript] annotációval, és deklaráljon egy IServiceProvider paramétert - a keretrendszer reflexió segítségével felismeri ezeket a tagokat, és automatikusan beinjektálja a szolgáltatót:

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

Az osztályalapú készségek a konstruktoron keresztül is megoldhatják a függőségeket. Regisztrálja a képességosztályt a ServiceCollection tárolóban, és oldja fel a közvetlen hívás new helyett:

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

Ez akkor hasznos, ha a képességosztálynak az erőforrás- és szkriptdelegáltak által használtnál több injektált szolgáltatásra van szüksége.

Azok az erőforrás- és szkriptfüggvények, amelyek elfogadják a futásidejű kulcsszóargumentumokat, automatikusan megkapják azokat, amikor átadásra kerülnek **kwargs-hez agent.run(). Ez lehetővé teszi, hogy a képességfüggvények hozzáférjenek az alkalmazás környezetéhez – például a konfigurációhoz, a felhasználói identitáshoz vagy a szolgáltatáskliensekhez – anélkül, hogy ezeket fixen bele kellene kódolni a képesség definíciójába.

Futásidejű argumentumok átadása

Adja át a kulcsszóargumentumokat function_invocation_kwargs-nak/nek agent.run()-ból/ből, amelyeket a keretrendszer továbbít az erőforrás- és szkriptfüggvényeknek:

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

Kódban definiált készségek kwargs használatával

Amikor egy erőforrásfüggvény deklarál **kwargs, a keretrendszer minden alkalommal továbbítja a futtatókörnyezet kulcsszóargumentumait, amikor az ügynök beolvassa az erőforrást:

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

Az **kwargs nélküli erőforrásfüggvényeket argumentumok nélkül hívják meg, és nem kapnak futási környezetet.

Amikor egy szkriptfüggvény deklarál **kwargs, a keretrendszer továbbítja a futtatókörnyezet kulcsszóargumentumait, az ügynök által biztosított args-el együtt.

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

Az ügynök biztosítja a value és factor elemeket a args eszköz hívásán keresztül; az alkalmazás biztosítja a precision elemet a function_invocation_kwargs segítségével. A szkript a **kwargs nélkül úgy működik, hogy csak az ügynök által megadott argumentumokat kapja meg.

Osztályalapú készségek a kwargs-okkal

Az osztályalapú készségmetódusok a futásidejű argumentumok fogadásához a(z) **kwargs paramétert is elfogadhatják. A minta ugyanúgy működik – deklarálás **kwargs erőforrás- vagy szkriptmetelyeken:

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

Ajánlott biztonsági eljárások

Az ügynöki készségeket úgy kell kezelni, mint bármely külső féltől származó kódot, amely a projektbe kerül. Mivel a készségutasítások az ügynök környezetébe kerülnek – és a készségek szkripteket is tartalmazhatnak –, a nyílt forráskódú függőségekre ugyanolyan szintű felülvizsgálatra és irányításra van szükség.

  • Használat előtt tekintse át az összes készségtartalmat (SKILL.mdszkriptet és erőforrást) az üzembe helyezés előtt. Ellenőrizze, hogy a szkript tényleges viselkedése megegyezik-e a megadott szándékkal. Ellenőrizze azokat a támadó utasításokat, amelyek megkísérlik megkerülni a biztonsági irányelveket, kiszűrik az adatokat, vagy módosítják az ügynök konfigurációs fájljait.
  • Forrásmegbízhatóság – Csak megbízható szerzőktől vagy ellenőrzött belső közreműködőktől származó ismeretek telepítése. Előnyben részesítheti a tiszta eredettel, a verziókövetéssel és az aktív karbantartással kapcsolatos készségeket. Figyeljen oda a népszerű csomagokat utánzani próbáló, elgépeléseken alapuló hamis funkciónevekre.
  • Tesztkörnyezet – Futtatási képességek, amelyek végrehajtható szkripteket tartalmaznak izolált környezetekben. A fájlrendszer, a hálózat és a rendszerszintű hozzáférés korlátozása csak arra, amit a képesség igényel. A potenciálisan bizalmas műveletek végrehajtása előtt explicit felhasználói megerősítést igényel.
  • Naplózás és auditálás – Rögzíteni, hogy mely képességek vannak betöltve, mely erőforrások kerülnek beolvasásra, és mely szkriptek kerülnek végrehajtásra. Ezzel naplózhatja, hogy az ügynök viselkedése visszakövethető-e egy adott képességtartalomra, ha valami hiba történik.

Mikor érdemes használni a készségeket és a munkafolyamatokat?

Az ügynöki készségek és az ügynök-keretrendszer munkafolyamatai kiterjesztik az ügynökök által elvégezhető műveleteket, de alapvetően eltérő módon működnek. Válassza ki a követelményeknek leginkább megfelelő megközelítést:

  • Vezérlés – Egy szakértelemmel az AI dönti el, hogyan hajtja végre az utasításokat. Ez akkor ideális, ha azt szeretné, hogy az ügynök kreatív vagy adaptív legyen. Munkafolyamat esetén explicit módon határozza meg a végrehajtási útvonalat. Akkor használjon munkafolyamatokat, ha determinisztikus, kiszámítható viselkedésre van szüksége.
  • Hibatűrés – Egy készség egyetlen ügynökfordulón belül fut le. Ha valami nem sikerül, a teljes műveletet újra kell próbálkozni. A munkafolyamatok támogatják az ellenőrzőpont-készítést, így a hiba utáni utolsó sikeres lépéstől folytathatják a munkát. Válassza ki a munkafolyamatokat, ha a teljes folyamat ismételt végrehajtásának költsége magas.
  • Mellékhatások – A készségek ideálisak idempotens vagy alacsony kockázatú műveletek esetén. Előnyben részesítse a munkafolyamatokat, ha a lépések olyan mellékhatásokat okoznak (e-mailek küldése, kifizetések felszámítása), amelyeket nem szabad megismételni újrapróbálkozáskor.
  • Összetettség – A készségek a legjobbak az olyan koncentrált, egytartományos feladatokhoz, amelyeket egy ügynök képes kezelni. A munkafolyamatok jobban megfelelnek a többlépéses üzleti folyamatoknak, amelyek több ügynököt, emberi jóváhagyást vagy külső rendszerintegrációt koordinálnak.

Tip

Ökölszabályként: ha azt szeretné, hogy az AI kitalálja, hogyan kell elvégezni egy feladatot, használjon egy készséget. Ha garantálnia kell, hogy milyen lépéseket hajt végre és milyen sorrendben, használjon munkafolyamatot.

Következő lépések