代理人技能

代理技能 是可攜式的指令、腳本與資源套件,賦予代理專業能力與領域專業知識。 技能遵循開放規範,並實施漸進式揭露模式,讓客服人員在需要時只載入他們需要的上下文。

使用代理程式技能的時機是:

  • 封裝領域專業知識 ——將專業知識(費用政策、法律工作流程、資料分析管線)封裝成可重複使用、可攜的套件。
  • 擴展代理能力 ——賦予代理新能力,但不改變其核心指令。
  • 確保一致性 ——將多步驟任務轉化為可重複且可稽核的工作流程。
  • 啟用互通性 ——可在不同相容的代理技能產品間重複使用同一技能。

技能結構

技能是一個包含 SKILL.md 檔案以及用於資源之選用子目錄的目錄:

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

SKILL.md 格式

SKILL.md檔案必須包含 YAML 前導內容,然後是 markdown 內容:

---
name: expense-report
description: File and validate employee expense reports according to company policy. Use when asked about expense submissions, reimbursement rules, or spending limits.
license: Apache-2.0
compatibility: Requires python3
metadata:
  author: contoso-finance
  version: "2.1"
---
Field Required 說明
name Yes 最多64字元。 只限小寫字母、數字和連字號。 不得以連字號開頭或結尾,或包含連續連字號。 必須與父目錄名稱相符。
description Yes 這個技能的作用以及何時使用。 最多 1024 字元。 應包含幫助客服人員辨識相關任務的關鍵字。
license No 授權名稱或綁定授權檔案的引用。
compatibility No 最多 500 字元。 表示環境需求(預期產品、系統套件、網路存取等)。
metadata No 用於其他中繼資料的任意索引鍵/值對應。
allowed-tools No 技能可使用的以空格分隔預先核准工具清單。 實驗性支援可能因代理實作而異。

前置之後的 Markdown 主體包含技能指示:逐步指引、輸入與輸出範例、常見邊緣案例,或任何有助於代理程式執行工作的內容。 行數控制 SKILL.md 在 500 行以內,並將詳細參考資料移到獨立檔案。

漸進式披露

Agent Skills 採用四階段逐步揭露模式,以減少情境依賴:

  1. 公告 (每個技能約 100 個權杖) - 技能名稱和描述會在每次執行開始時插入系統提示中,以便代理程式了解有哪些可用的技能。
  2. 載入 (< 建議 5000 個代幣)-當任務符合技能領域時,代理會呼叫工具 load_skill 以取得完整 SKILL.md 並附上詳細指示。
  3. 讀取資源 (視需要)——代理程式僅在需要時呼叫 read_skill_resource 工具擷取補充檔案(參考、範本、資產)。
  4. 執行腳本 (視需要)-代理呼叫工具 run_skill_script 執行與技能綁定的腳本。

這種模式讓代理的上下文視窗保持精簡,同時能隨時存取深層的領域知識。

備註

load_skill 一律會進行公告。 read_skill_resource 只有當至少一個技能有資源時才會被宣傳。 run_skill_script 只有當至少一個技能有腳本時才會被宣傳。

為經紀人提供技能

處理技能包含三個建構元素:

  • 提供者 - AgentSkillsProvider(C#) 或 SkillsProvider (Python) 是一個上下文提供者,能將技能暴露給代理人。 它會在系統提示中公告可用技能,並登錄代理用來載入技能、讀取資源及執行腳本的工具。
  • 資料來源 ——來源能為提供者提供技能。 技能可以來自多種來源類型:
    • 基於檔案 的技能——從 SKILL.md 檔案系統目錄中的檔案中發現的技能。
    • 程式碼定義 - 使用 AgentInlineSkill(C#)或 InlineSkill(Python)在程式碼中內嵌定義的技能。
    • 基於類別 - 技能封裝在衍生自 AgentClassSkill<T>(C#)或 ClassSkill(Python)的類別中。
    • 基於 MCP 的技能 — 透過 (C#) 或 UseMcpSkills (Python) 從 MCP(模型上下文協定)伺服器MCPSkillsSource中學習的技能。
  • 建造者 - AgentSkillsProviderBuilder (C#) 將多個來源組合成單一提供者,並應用聚合、重複去重、快取及可選過濾。 在 Python 中,直接組合 AggregatingSkillsSourceFilteringSkillsSourceDeduplicatingSkillsSource 等來源類別。

接下來的章節將說明如何建立每種來源類型的技能,接著是如何結合來源並從中構建提供者。

在 Harness Agent 中使用代理技能

使用純代理程式來建立技能提供者,將其新增至代理程式的內容提供者中,並在需要時組合工具核准中介軟體。 Harness Agent 可以建立或將提供者納入其標準設定中。

HarnessAgent 預設包含 AgentSkillsProvider,並從 Directory.GetCurrentDirectory() 偵測檔案型技能。 若要使用其他來源,請設定 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 預設為 false。 將其設為 true,以移除內建提供者。 AgentSkillsSource 取代預設的當前目錄來源,但不會暴露 AgentSkillsProviderOptions。 如果您需要例如 DisableLoadSkillApproval 這類的提供者選項,請停用內建提供者,並透過 HarnessAgentOptions.AIContextProviders 新增您已設定的 AgentSkillsProvider

若要從檔案型技能執行指令碼,請將 AgentFileSkillScriptRunner 委派做為第二個 AgentFileSkillsSource 建構函式引數傳遞。 沒有執行器時,腳本在被要求執行時將無法執行。

這三種技能工具預設都需要核准。 Harness 工具核准中介軟體預設為啟用,但其預設選項不會自動核准任何工具。 使用 AgentSkillsProvider.ReadOnlyToolsAutoApprovalRuleAgentSkillsProvider.AllToolsAutoApprovalRule 只用於你信任的技能來源。

代理程式技能是針對 create_harness_agent 的選擇加入功能。 傳遞 skills_paths 來進行檔案型探索:

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)

skills_paths 可接受一個 strPath,或它們的序列。 當 skills_providerskills_paths 均為 None (預設值) 時,執行框架不會新增 SkillsProvider。 你可以結合這兩個參數,納入程式碼定義和檔案技能。

skills_paths 快速鍵可建立 SkillsProvider.from_paths(),而不需要 script_runner。 如果檔案型技能需要執行指令碼,請使用 SkillsProvider.from_paths(..., script_runner=...) 自行建立提供者,並透過 skills_provider 傳遞它。

這三種技能工具預設都需要核准。 因為執行框架預設會安裝 ToolApprovalMiddleware,每次執行都會傳遞一個工作階段,並將 auto_approval_rules 用於受信任的唯讀或所有工具核准原則。

目前沒有可用的套件 Go 執行框架。 在 agent.Config.ContextProviders 中註冊 Go 技能提供者,並直接組成審核中介軟體。

檔案式技能

建立一個指向包含您技能之目錄的 AgentSkillsProvider,並將其新增至代理程式的內容提供者。 傳遞一個腳本執行工具,來啟用執行技能目錄中基於檔案的腳本:

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

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

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

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

Warning

DefaultAzureCredential 開發方便,但在生產過程中需謹慎考量。 在生產環境中,建議使用特定的憑證(例如 ManagedIdentityCredential),以避免延遲問題、意外的憑證探測,以及備援機制帶來的安全風險。

多重技能目錄

您可以將提供者指向單一父系目錄 - 每個包含 SKILL.md 的子目錄都會自動探索為一項技能:

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

或者傳遞一條路徑清單來搜尋多個根目錄:

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

提供者最多向下搜尋兩層。

自訂資源與指令碼探索

預設情況下,提供者識別的資源副檔名為 .md.json.yaml.yml.csv.xml.txt和 ,以及具有副檔名 .py.js.sh.ps1.cs.csx和 的腳本。 它會在每個技能目錄中向下搜尋最多兩層。 使用 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();

ResourceFilterScriptFilter 會收到 AgentFileSkillFilterContext 一個包含技能名稱和檔案相對路徑的檔案,讓你可以依據位置、命名規則或任何自訂邏輯來限制檔案。

指令碼執行

SubprocessScriptRunner.RunAsync 作為指令碼執行器傳入,以啟用以檔案為基礎的指令碼執行:

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

SubprocessScriptRunner.RunAsync 大致等同於以下數值:

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

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

執行者會將每個發現的腳本作為本地子程序執行。 基於檔案的腳本期望參數以 JSON 字串陣列的形式呈現——每個陣列元素都成為位置命令列參數。

Warning

SubprocessScriptRunner 僅供示範用途。 在生產環境中使用時,請考慮新增以下內容:

  • 沙盒(例如容器或隔離執行環境)
  • 資源限制 (CPU、記憶體、實際時間逾時)
  • 輸入驗證與可執行指令碼的允許清單
  • 結構化記錄與稽核追蹤

檔案式技能

使用 SkillsProvider.from_paths() 工廠,從包含 SKILL.md 檔案的目錄中探索技能,並將該提供者新增至代理程式的內容提供者:

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

多重技能目錄

您可以將提供者指向單一父系目錄 - 每個包含 SKILL.md 的子目錄都會自動探索為一項技能:

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

或者傳遞一條路徑清單來搜尋多個根目錄:

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

提供者最多向下搜尋兩層。

自訂資源與指令碼探索

預設情況下,資源是從 references/assets/ 子目錄中發現,腳本則從 scripts/中發現,依照 agentskills.io 規範。 可辨識的資源副檔名包括 .md.json.yaml.yml.csv.xml.txt。 它會在每個技能目錄中向下搜尋最多兩層。 使用 resource_extensionsscript_extensionssearch_depthresource_filterscript_filter 自訂探索體驗:

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_filterscript_filter 謂詞會接收技能名稱和檔案的相對路徑,讓你可以依據位置、命名規則或任何自訂邏輯來限制檔案。 使用 "." 以在子目錄之外,同時包含技能根目錄層級的檔案。

指令碼執行

要啟用基於檔案的腳本執行,請將 a script_runner 傳遞給 SkillsProvider.from_paths()。 任何符合 SkillScriptRunner 協定的同步或非同步呼叫皆可使用:

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

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

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

執行器會收到已解析的 FileSkillFileSkillScript,以及一個選用的 args 引數。 基於檔案的腳本期望參數以 JSON 字串陣列的形式呈現——每個陣列元素都成為位置命令列參數。 腳本會自動從 .py 每個技能目錄子目錄中的 scripts/ 檔案中被發現。

Warning

上方的滑道 僅供示範使用。 在生產環境中使用時,請考慮新增以下內容:

  • 沙箱 (例如容器、seccompfirejail)
  • 資源限制 (CPU、記憶體、實際時間逾時)
  • 輸入驗證與可執行指令碼的允許清單
  • 結構化記錄與稽核追蹤

備註

若提供含有指令碼的檔案型技能,但未設定 script_runner,則在嘗試執行指令碼時,SkillsProvider 會引發錯誤。

檔案式技能

Go 代理程式透過 agent/skills 套件支援技能。 技能遵循相同的漸進揭露模式:廣告 -> 載入 -> 閱讀資源 -> 執行腳本。

SKILL.md 磁碟檔案中發現技能,並將技能提供者註冊為代理情境提供者:

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

以程式碼定義的技能

除了從 SKILL.md 檔案中探索到的檔案型技能外,您也可以使用 AgentInlineSkill 完全在程式碼中定義技能。 程式碼定義技能在以下情況下非常有用:

  • 技能內容是動態產生的(例如,從資料庫或環境中讀取資料)。
  • 你要把技能定義和使用它們的應用程式程式碼一起保存。
  • 你需要在讀取時執行邏輯的資源,而不是提供靜態檔案。
  • 技能定義需 在執行時從資料中建構 ——例如,根據每個使用者的工作階段角色或權限建立個人化技能。
  • 技能需要封閉呼叫端狀態 (本機變數、關閉),而非從 DI 容器解析服務。

基本程式碼技能

建立一個具有名稱、描述和說明的物件 AgentInlineSkill 。 使用.AddResource()附上資源。

using Microsoft.Agents.AI;

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

var skillsProvider = new AgentSkillsProvider(codeStyleSkill);

動態資源

將工廠代理傳遞給 .AddResource(),以在執行時計算內容。 每次代理讀取資源時,都會呼叫代理:

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

程式碼定義腳本

.AddScript() 來註冊代理為可執行腳本。 程式碼定義的腳本會在 同一進程中 以直接委派呼叫的形式執行。 不需要腳本執行者。 代理者所輸入的參數會自動轉換成 JSON 架構,代理程式用來傳遞參數:

using System.Text.Json;

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

var skillsProvider = new AgentSkillsProvider(unitConverterSkill);

備註

若要將程式碼定義技能與檔案或類別基礎技能合併於單一提供者中,請使用 AgentSkillsProviderBuilder ——參見 提供者建構

除了從 SKILL.md 檔案中發現的基於檔案的技能外,你還可以用 InlineSkill 完全在 Python 程式碼中定義技能。 程式碼定義技能在以下情況下非常有用:

  • 技能內容是動態產生的(例如,從資料庫或環境中讀取資料)。
  • 你要把技能定義和使用它們的應用程式程式碼一起保存。
  • 你需要在讀取時執行邏輯的資源,而不是提供靜態檔案。
  • 技能定義需 在執行時從資料中建構 ——例如,根據每個使用者的工作階段角色或權限建立個人化技能。
  • 技能需要封閉呼叫端狀態 (本機變數、關閉),而不是透過 **kwargs 解析服務。

基本程式碼技能

建立一個 InlineSkill 實例,包含 SkillFrontmatter(內含名稱和描述)以及指令內容。 可選擇性地附加 InlineSkillResource 帶有靜態內容的實例:

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

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

skills_provider = SkillsProvider(code_style_skill)

動態資源

@skill.resource 裝飾工具把一個函數註冊成資源。 每次代理讀取資源時都會呼叫該函式,所以可以回傳最新的資料。 同時支援同步與非同步功能:

import os
from agent_framework import InlineSkill, SkillFrontmatter

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

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

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

當 decorator 未使用參數(@skill.resource),函式名稱即為資源名稱,docstring 則為描述。 用 @skill.resource(name="...", description="...") 來明確設定它們。

程式碼定義腳本

使用 @skill.script 裝飾工具將函式註冊為技能上的可執行指令碼。 程式碼定義腳本在 進行中 執行,且不需腳本執行程式。 同時支援同步與非同步功能:

from agent_framework import InlineSkill, SkillFrontmatter

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

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

當 decorator 未使用參數(@skill.script),函式名稱變為腳本名稱,docstring 則為描述。 函式的型別參數會自動轉換成 JSON 架構,代理程式用來傳遞參數。

除了從 SKILL.md 檔案中發現的基於檔案的技能外,你還可以完全用圍棋程式碼來定義技能:

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 只有當代理呼叫 load_skill時才載入技能指令。 腳本會接收位置式 CLI 風格的字串參數, ["26.2", "1.60934"]例如 ,並可依腳本需求解析這些參數。

Tip

請參閱 技能範例 以獲得完整的可執行範例。

類別型技能

基於職業的技能可以讓你將所有技能組成部分——名稱、描述、指示、資源和腳本——打包到一個 C# 類別中。 這讓它們更容易封裝並作為 NuGet 套件散發——團隊可以獨立撰寫並發行技能,而使用者只需透過 dotnet add package 和一次 .UseSkill() 呼叫即可加入它們。 從 AgentClassSkill<T>(其中 T 是你的類別)進行推導,然後將屬性註解為 [AgentSkillResource],並將方法註解為 [AgentSkillScript] 以便自動發現:

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

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

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

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

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

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

使用 AgentSkillsProvider 註冊類別型技能:

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

[AgentSkillResource] 屬性套用到屬性或方法時,當代理讀取資源時,其回傳值會作為資源內容——當需要在讀取時計算內容時,則使用該方法。 當 [AgentSkillScript] 應用於方法時,代理呼叫腳本時會呼叫該方法。 使用 [Description] from System.ComponentModel 來描述代理的每個資源和腳本。

備註

AgentClassSkill<T> 也支援覆寫 ResourcesScripts 做為集合,適用於屬性型探索不適用的情境。

類別型技能

基於類別的技能可以讓你把所有技能組成部分——名稱、描述、指示、資源和腳本——打包到一個 Python 課程裡。 這使得它們很容易打包並以 PyPI 套件的形式發佈——團隊可以獨立撰寫並交付技能,使用者只需一pip install通通話即可新增技能SkillsProvider()。 子類 ClassSkill,然後使用 @ClassSkill.resource@ClassSkill.script 裝飾器進行自動發現:

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

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

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

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

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

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

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

使用 SkillsProvider 註冊類別型技能:

from agent_framework import SkillsProvider

skill = UnitConverterSkill()
skills_provider = SkillsProvider(skill)

@ClassSkill.resource 應用為裸裝飾器(無參數)時,方法名稱即為資源名稱(底線轉換為連字號),文件字串則為描述。 用 @ClassSkill.resource(name="...", description="...") 來明確設定它們。 同樣的模式也適用於 @ClassSkill.script

資源可以定義為規則方法或 @property 描述子。 使用 @property時,先置 @property 第一和 @ClassSkill.resource 第二。 資源傳回值會在首次存取後進行快取。

備註

ClassSkill 也支援明確覆寫 resourcesscripts 屬性以直接傳回 InlineSkillResourceInlineSkillScript 執行個體,適用於裝飾項目型探索不適用的情境。

以 MCP 為基礎的技能

備註

以 MCP 為基礎的技能需要 Microsoft.Agents.AI.Mcp NuGet 套件。 MCP 技能 API 仍處於實驗階段,未來版本可能會有所變動。

技能可以從在 skill:// URI 結構描述下公開技能資源的 MCP (模型內容通訊協定) 伺服器中探索。 MCP 伺服器透過 skill://index.json 發現文件來宣傳技能,框架則會隨時擷取技能內容。

基於 MCP 的技能支援兩種索引輸入類型:

  • skill-md - SKILL.md 技能與姊妹資源可按需從 MCP 伺服器擷取。
  • archive - 技能以 ZIP 壓縮檔形式發佈,框架會在此下載並解壓縮。

基本使用方式

使用 UseMcpSkills 上的 AgentSkillsProviderBuilder 延伸方法來新增 MCP 技能來源:

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

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

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

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

封存型技能

對於封存類型技能,請使用 AgentMcpSkillsSourceOptions (套件中的 Microsoft.Agents.AI.Mcp) 來設定解壓縮行為:

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

AgentMcpSkillsSourceOptions 會揭露以下屬性以供控制壓縮檔擷取:

  • ArchiveSkillsDirectory - 壓縮檔案的基礎目錄。 預設為目前工作目錄下的唯一子目錄,針對每個來源執行個體產生,以防止多個來源之間發生衝突。
  • ArchiveResourceExtensions - 允許對已解壓縮檔案中的資源進行擴充。 預設為 .md.json.yaml.yml.csv.xml.txt
  • ArchiveResourceSearchDepth - 在每個已解壓縮的技能目錄中搜尋資源的深度。 預設為 2
  • ArchiveMaxFileCount - 每個壓縮檔的最大檔案數。 超過此限制的檔案會被跳過。 預設為 20
  • ArchiveMaxSizeBytes - 每個壓縮檔的最大下載容量。 預設為 1 MB
  • ArchiveMaxUncompressedSizeBytes - 每個壓縮檔的最大未壓縮總大小。 預設為 1 MB

這很重要

歸檔型技能中捆綁的腳本 永遠不會被執行。 這是刻意的安全措施——來自遠端 MCP 伺服器的可執行內容需要明確的信任。

以 MCP 為基礎的技能

備註

基於 MCP 的技能仍屬實驗性質,未來版本可能會有所調整。 使用 MCPSkillsSource 時,會在 FutureWarning 功能旗標下產生 MCP_SKILLS

你可以從在 skill:// URI 配置下公開技能資源的 MCP(模型上下文協定)伺服器探索技能。 MCP 伺服器透過 skill://index.json 發現文件來宣傳技能。 Python 支援透過 resources/read 隨選擷取的 skill-md 項目,以及以 ZIP 檔案形式提供的 archive 項目。

將 MCP ClientSession 包在 MCPSkillsSource 中,並將其傳遞給 SkillsProvider

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
    # supported 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("...")

對於存檔條目,請使用 application/zip 媒體類型或 .zip 網址後綴。 Agent Framework 跳過 TAR、 .tar.gz.tgz及其他不支援的壓縮格式,因此剩餘的索引項目仍可載入。 將現有非 ZIP 技能重新包裝為 ZIP;不需要更改來電端代碼。

當一個壓縮檔條目提供 digest時,必須使用 sha256: ,後面接著 64 個小寫十六進位字元。 Agent Framework 會在解壓縮前,將摘要與解碼後的封存檔位元組進行比對驗證。 無效或不符的摘要會跳過該存檔,但不會阻擋其他條目。 允許省略或為空值的摘要。 摘要驗證僅適用於 archive 項目,不適用於 skill-md 項目或其支援資源。 相符的摘要值證明其與索引一致,但不代表 MCP 伺服器值得信任。

MCPSkillsSource 在記憶體中擷取 ZIP 內容。 利用其 archive_* 建構器選項限制資源擴展、搜尋深度、檔案數量、下載大小及未壓縮總大小。 MCP 壓縮檔中的腳本僅以唯讀資源形式提供,且從未以可執行腳本的形式公開。

備註

skill://index.json 缺失、無法讀取、空或無法解析,原始碼會回傳一個空清單。 代理框架會跳過除 skill-mdarchive以外的索引條目類型。

這很重要

外部 MCP 伺服器會控制哪些技能內容會傳送給代理,包括代理可執行的指令和腳本。 只連接 MCPSkillsSource 你經過審核且信任的伺服器,並將他們的回應視為不可信的輸入。

技能來源

一個 AgentSkillsProvider 從一或多個來源 (實作 AgentSkillsSource 的物件) 擷取技能。 來源分為兩類:分葉節點來源,用於探索或持有技能 (例如用於檔案型技能的 AgentFileSkillsSource),以及裝飾項目,用於轉換另一個來源的輸出 (包含彙總、重複資料刪除、快取和篩選)。 你也可以建立 自訂來源

每個來源都實作一種方法—— GetSkillsAsync(AgentSkillsSourceContext context, CancellationToken cancellationToken = default)。 該文件 AgentSkillsSourceContext 包含關於目前申請的資訊:

  • Agent - 要求技能的 AIAgent 執行個體。
  • Session - 與叫用相關聯的 AgentSession,當沒有工作階段時則為 null

這個上下文在整個原始碼管線中皆可取得,因此 FilteringAgentSkillsSource 謂詞或自訂來源可以基於此建立邏輯——例如,根據請求代理人的不同,回傳不同的技能組合。

葉片來源

AgentFileSkillsSource

從磁碟上的 SKILL.md 檔案中探索技能。 接受一或多個目錄路徑、選用的指令碼執行器,以及選用的 AgentFileSkillsSourceOptions(相關說明請參閱 以檔案為基礎的技能)。

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

AgentInMemorySkillsSource

在記憶體中封裝以程式碼定義或以類別為基礎的 AgentSkill 實例。

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

組合子

AggregatingAgentSkillsSource

將多個來源合併為一。 技能依註冊順序回傳,未進行重複刪除或過濾。

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

Decorators

裝飾者會包裹內部源並轉換其輸出。 它們可以串聯起來建造管線。

DeduplicatingAgentSkillsSource

移除重複的技能名稱(不區分大小寫,先出現者獲勝)。 重複資料刪除會以警告層級記錄。

var deduplicated = new DeduplicatingAgentSkillsSource(innerSource);

CachingAgentSkillsSource

快取內部來源所傳回的技能清單。 並行呼叫器會依快取金鑰序列化,因此一次只能執行一個擷取。 接受選用的 CachingAgentSkillsSourceOptions

  • RefreshIntervalTimeSpan?) - 設定後,快取結果在此區間後失效,內部來源會被重新呼叫。 當 null (預設值)時,快取結果永遠不會過期。
  • CacheIsolationKeySelectorFunc<AgentSkillsSourceContext, string?>?) - 回傳快取金鑰,依上下文(例如每個租戶)隔離快取結果。 當 null 時,所有呼叫者共用同一個快取區。
var cached = new CachingAgentSkillsSource(innerSource, new CachingAgentSkillsSourceOptions
{
    RefreshInterval = TimeSpan.FromMinutes(5)
});

FilteringAgentSkillsSource

應用謂詞來包含或排除技能。 述詞會接收技能與一個 AgentSkillsSourceContext

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

自訂來源

當內建的資源無法涵蓋你的情境時,就自行實施。 分葉節點來源的子類別 AgentSkillsSource (能從新來源產生技能,例如資料庫或遠端服務),或裝飾項目的子類別 DelegatingAgentSkillsSource,轉換另一個來源的輸出。

葉片來源

AgentSkillsSource 推導並實現 GetSkillsAsync。 這個 AgentSkillsSourceContext 參數讓來源能根據當前請求調整結果——例如,根據請求代理人的不同,回傳不同的技能集合。 如果來源擁有用戶端或連線等資源,請覆寫 Dispose(bool)

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

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

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

客製化裝飾師

DelegatingAgentSkillsSource 衍生,呼叫 InnerSource.GetSkillsAsync,並轉換或觀察其結果。 這與內建快取、重複資料刪除和篩選裝飾項目所使用的模式相同。 例如,一個會記錄每個要求傳回多少技能而不改變結果的裝飾項目:

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

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

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

這兩個自訂來源都可以直接傳送到 AgentSkillsProvider 或嵌套在較大的管線中,就像內建的原始碼一樣。

提供者建構函式

AgentSkillsProvider 是將技能暴露給代理人的元件。 它會包裹一個或多個來源,並登錄 load_skillread_skill_resourcerun_skill_script 工具。 有三種方式可以建立一個:

  1. AgentSkillsProviderBuilder - 將多種技能類型整合成一個提供者,並具備自動聚合、重複刪除、快取及可選過濾功能。 最適合用於結合以檔案為基礎、以程式碼定義、以類別為基礎及以 MCP 為基礎的技能之情境。
  2. 直接原始碼組合 ——自己用公開 AgentSkillsSource 類別建構原始碼管線。 不會自動套用快取或去重,整個流程都由你控制。 最適合當你需要控制排序、條件邏輯或自訂裝飾行為時。
  3. 便利建構子 ——直接從檔案路徑或技能實例建立提供者。 自動套用去重與快取。 最適合單一來源情境。

使用 AgentSkillsProviderBuilder

當您需要下列任何一項時,請使用 AgentSkillsProviderBuilder

  • 混合技能類型 ——將檔案基礎、程式碼定義(Code-defined (AgentInlineSkill)、類別基礎(AgentClassSkillclass)及 MCP 技能整合於單一提供者中。
  • 技能篩選 ——使用謂詞來包含或排除技能。

混合技能類型

透過串聯 UseFileSkillUseSkillUseMcpSkillsUseFileScriptRunner,將多種技能類型結合在同一個提供者中:

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

技能過濾

使用 UseFilter 來只包含符合條件的技能,例如從共用目錄載入技能,但排除實驗性技能:

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

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

直接撰寫來源

當建構者無法提供你需要的控制權時,自己組合原始碼類別,並將產生的管線傳到 AgentSkillsProvider。 完整可用資源及其選項,請參閱 技能來源

以下範例會建立相近的多來源管線,但可讓您明確控制每個裝飾器:

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

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

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

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

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

備註

ownsSourcetrue 時,處置提供者也會一併處置整個來源管線。 如果你自己管理原始碼生命週期,可以設定為 false

便利建構函式

對於單一來源的情境,直接使用 AgentSkillsProvider 建構子。 這些工具能自動套用去重與快取,無需建置器或手動原始碼組合。

從檔案路徑出發:

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

從技能執行個體:

var skillsProvider = new AgentSkillsProvider(volumeConverterSkill, temperatureConverter);

技能來源

A SkillsProvider 從一個或多個 來源 取得技能——這些物件源自 SkillsSource。 來源分為兩類:分葉節點來源,用於探索或持有技能 (例如用於檔案型技能的 FileSkillsSource),以及裝飾項目,用於轉換另一個來源的輸出 (包含彙總、重複資料刪除、快取和篩選)。 你也可以建立 自訂來源

每個來源都實作一種方法—— async def get_skills(self, context: SkillsSourceContext) -> list[Skill]。 該文件 SkillsSourceContext 包含關於目前申請的資訊:

  • agent - 請求技能的代理程式(SupportsAgentRun)。
  • session - 與叫用相關聯的 AgentSession,當沒有工作階段時則為 None

此內容會流經整個來源管線,因此 FilteringSkillsSource 述詞或自訂來源可以根據它來編寫邏輯 — 例如,根據要求的代理程式傳回不同的技能集。

葉片來源

  • FileSkillsSource - 從 SKILL.md 磁碟檔案中發現技能。 接受一或多個目錄路徑、一個可選的 script_runner,以及載於 resource_extensions 中的探索選項(script_extensionssearch_depthresource_filterscript_filter)。
  • InMemorySkillsSource - 將 Skill 實例(以程式碼定義或類別為基礎)封裝在記憶體中。
  • MCPSkillsSource - 從 MCP 伺服器發現技能(參見 基於 MCP 的技能)。
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])

組合器

AggregatingSkillsSource 將多個來源合併為一。 技能依註冊順序回傳,未進行重複刪除或過濾。

from agent_framework import AggregatingSkillsSource

aggregated = AggregatingSkillsSource([file_source, in_memory_source])

Decorators

裝飾者會包裹內部源並轉換其輸出。 它們可以串聯起來建造管線。

  • DeduplicatingSkillsSource - 移除重複的技能名稱(不區分大小寫,先出現者獲勝)。 重複資料刪除會以警告層級記錄。
  • CachingSkillsSource - 快取內部來源所傳回的技能清單。 針對同一個快取金鑰的並行呼叫者會共用一個進行中的擷取作業,因此每個金鑰最多只會向內部來源查詢一次。 接受兩個可選的關鍵字參數:
    • refresh_intervaltimedelta | None) - 當設定時,快取清單一旦超過區間就會被視為過時,因此下一次呼叫會重新查詢內部來源。 當 None (預設值)時,快取結果永遠不會過期。 對於技能會隨著流程生命週期而改變的內部資源(例如 MCPSkillsSource)而言,這非常有用。
    • cache_isolation_key_selectorCallable[[SkillsSourceContext], str | None]) - 從上下文中推導快取金鑰,以隔離快取結果(例如每個代理或租戶)。 金鑰應具備低基數且穩定。 回傳 None(或將其保留為 None)會使用單一的共享快取儲存桶。
  • FilteringSkillsSource - 應用謂詞來包含或排除技能。 述詞會接收技能一個 SkillsSourceContextCallable[[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",
)

自訂來源

當內建的資源無法涵蓋你的情境時,就自行實施。 分葉節點來源的子類別 SkillsSource (能從新來源產生技能,例如資料庫或遠端服務),或裝飾項目的子類別 DelegatingSkillsSource,轉換另一個來源的輸出。

葉片來源

SkillsSource 推導並實現 get_skills。 這個 SkillsSourceContext 參數讓來源能根據當前請求調整結果——例如,根據請求代理人的不同,回傳不同的技能組合:

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)

客製化裝飾師

DelegatingSkillsSource 衍生,呼叫 self.inner_source.get_skills(context),並轉換或觀察其結果。 這與內建快取、重複資料刪除和篩選裝飾項目所使用的模式相同。 例如,一個會記錄每個要求傳回多少技能而不改變結果的裝飾項目:

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

這兩個自訂來源都可以直接傳送到 SkillsProvider 或嵌套在較大的管線中,就像內建的原始碼一樣。

提供者建構函式

SkillsProvider 是將技能暴露給代理人的元件。 它會包裹一個或多個來源,並登錄 load_skillread_skill_resourcerun_skill_script 工具。 有三種方式可以建立一個:

  1. 從技能實例——將單一 Skill 或一系列技能傳遞給建構函式。 最適用於以程式碼定義和以類別為基礎的技能。 自動套用去重與快取。
  2. 從檔案路徑 - 使用 SkillsProvider.from_paths() 工廠。 最適合以單一來源檔案為基礎的技能。 自動套用去重與快取。
  3. 直接原始碼組合 ——自己用公開 SkillsSource 類別建構原始碼管線,然後交給建構器。 你掌控整個流程。 當您需要控制排序、條件邏輯、快取金鑰或自訂裝飾項目行為時,這是最佳選擇。

從技能執行個體

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

從檔案路徑

from pathlib import Path
from agent_framework import SkillsProvider

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

直接撰寫來源

當你需要完全控制時,自己組合原始碼類別,並將產生的管線傳到 SkillsProvider。 完整可用資源及其選項,請參閱 技能來源

下列範例建立了一個多來源管線,並明確控制各個裝飾項目。 範例中使用了佔位物件:

  • volume_converter_skill - 任何 InlineSkill 執行個體,依程式碼定義技能所示建置。
  • temperature_converter_skill - 任何 ClassSkill 實例,依照 以類別為基礎的技能 中所示的方式建構。
  • my_runner - SkillScriptRunner 可呼叫物件,其定義如 腳本執行 所示。
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)

這很重要

由呼叫者提供的 SkillsSource按原樣使用:它不會自動刪除重複資料或包裝在 CachingSkillsSource 中。 在單一共用貯體中自動快取具內容感知的來源,可能會將某個代理程式或租用戶的技能重播給另一個代理程式或租用戶。 當你需要時,請自行組合 DeduplicatingSkillsSourceCachingSkillsSource,也可選擇加上 cache_isolation_key_selector。 自動去重和快取只會在你直接傳遞技能或檔案路徑時生效(如上述選項 1 和 2)。

混合技能類型

使用 AggregatingSkillsSource 在單一提供者中結合以檔案為基礎、以程式碼定義及以類別為基礎的技能:

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

temperature_converter_skill = TemperatureConverterSkill()

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

技能過濾

使用 FilteringSkillsSource 來控制代理程式可看到哪些技能。 述詞會接收每個 SkillSkillsSourceContext,並傳回 True 以納入該技能。 例如,從共享目錄載入技能但隱藏實驗性目錄:

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

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

快取行為

預設情況下,建構器會以 CachingAgentSkillsSource 包裝來源管線,而此 CachingAgentSkillsSource 會快取底層來源所傳回的技能清單。 技能在第一次請求中完成解析後,後續請求會重用已快取的清單,而無需重新查詢來源。 要停用快取(例如開發過程中技能定義頻繁變動時),請在建構者中使用 DisableCaching()

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

備註

在開發過程中,當技能內容頻繁變動時,關閉快取非常有用。 在生產環境中,為了提升效能,預設開啟快取。

快取行為

預設情況下,技能工具和指令會在第一次建置後快取。 設定 disable_caching=True 每次召喚都強制重建:

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

disable_caching 也可在 SkillsProvider 建構函式中使用,用於程式碼定義和類別型技能。

若要保持啟用快取但定期重新探索技能 (例如,當檔案型或 MCP 來源在處理程序生命週期中變更時),請傳入 cache_refresh_interval。 內建快取一旦超過區間時被視為過時,下一次執行會重新查詢來源:

from datetime import timedelta

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

cache_refresh_interval 僅影響提供者內部建立的快取 (從技能或檔案路徑);當 disable_caching=True 時會被忽略,且對呼叫者提供的 SkillsSource 沒有影響 (若要處理這些情況,請搭配 refresh_interval 自行撰寫 CachingSkillsSource)。

備註

在開發過程中,當技能內容頻繁變動時,關閉快取非常有用。 在生產環境中,為了提升效能,預設開啟快取。

工具核准

所有由 AgentSkillsProviderload_skillread_skill_resourcerun_skill_script) 公開的工具預設需核准。 當工具呼叫需要核准時,代理會暫停並回傳 a ToolApprovalRequestContent ,而非立即執行。 使用 UseToolApproval 帶有自動核准規則的中介軟體,選擇性地繞過受信任操作的提示:

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

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

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

若要自動核准所有技能工具,包括指令碼執行:

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

停用特定工具的批准功能

使用 AgentSkillsProviderOptions 可停用個別工具的核准要求,將其完全移出核准流程:

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

當在同一個回應中,某些工具需要批准,而其他工具不需要時,模型可能會同時呼叫這兩類工具。 將 EnableNonApprovalRequiredFunctionBypassing 設定為讓免核准工具立即執行,而僅針對其餘工具提示使用者:

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

處理核准申請

當工具需要核准(且沒有符合的自動核准規則)時,代理程式會傳回 ToolApprovalRequestContent 個項目,必須先核准或拒絕這些項目,才能繼續進行:

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

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

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

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

腳本錯誤細節

預設情況下,當技能指令碼執行失敗時,例外狀況會傳播到基礎 FunctionInvokingChatClient。 若其 IncludeDetailedErrors 屬性設為 true,例外訊息會轉發給模型,使其能透過不同參數重試自我修正:

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

如果無法直接設定 FunctionInvokingChatClient ,改用設定 AgentSkillsProviderOptions.IncludeDetailedErrors 。 此方法會在技能提供者層級捕捉例外,並直接將錯誤訊息回傳給模型:

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

Warning

任一種方法都可能向模型揭露原始例外細節。 例外訊息可能包含敏感資訊,如連線字串、檔案路徑或內部服務名稱。 此外,若技能或指令碼源自不受信任的來源,惡意撰寫的指令碼可能會擲回例外狀況,其訊息中會嵌入提示插入酬載。

所有由 SkillsProviderload_skillread_skill_resourcerun_skill_script) 公開的工具預設都需要核准。 當工具呼叫需要核准時,代理會暫停並透過以下 result.user_input_requests 方式回傳核准請求,而非立即執行。 你用以下 request.to_function_approval_response(approved=...) 方式批准或拒絕每個請求,並將回覆寄回:

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)

當工具呼叫被拒絕時(approved=False),代理會被告知使用者拒絕,並可依此回應。

自動核准可信工具

與其在每次呼叫時都提示,不如安裝 ToolApprovalMiddleware,並使用 SkillsProvider 提供的其中一個靜態自動核准規則。 這讓唯讀工具能自動執行,同時仍會提示腳本執行:

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

有兩條規則可用:

  • SkillsProvider.read_only_tools_auto_approval_rule - 僅批准唯讀工具(load_skillread_skill_resource),同時仍會提示 run_skill_script
  • SkillsProvider.all_tools_auto_approval_rule - 核准所有技能工具,包括 run_skill_script (無需手動核准迴圈)。

這兩條規則都拒絕任何帶有 server_label的通話,因此它們仍限於該服務提供者的本地工具,且不會自動核准同名託管工具。 這些規則僅適用於仍需批准的工具——透過下方 disable_*_approval 參數選擇退出的工具,一律可在無需批准的情況下執行。

停用特定工具的批准功能

對於受信任的技能,傳入 disable_load_skill_approvaldisable_read_skill_resource_approval 和/或 disable_run_skill_script_approval,即可讓個別工具完全略過核准流程(這些工具已註冊於 approval_mode="never_require"):

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
)

這些論證也可在 SkillsProvider.from_paths()上取得。

Warning

僅對您信任之來源的技能與指令碼停用核准,或自動核准指令碼執行。 技能指令會注入代理的上下文中,並 run_skill_script 執行來源提供的程式碼。

自訂系統提示

預設情況下,技能提供者會注入系統提示,列出可用技能並指示代理人使用 load_skillread_skill_resource。 你可以自訂這個提示詞:

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

備註

自訂範本必須包含 {skills} 作為產生技能清單的佔位符。 常值大括號必須逸出為 {{ and }}

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

備註

自訂範本必須包含用於產生技能清單的 {skills} 預留位置。 它可選擇包含 {resource_instructions} (資源工具提示)和 {runner_instructions} (腳本工具提示)佔位符;若存在,則以內建指引填充;若省略,則不被渲染(對應工具仍會被註冊)。 常值大括號必須逸出為 {{ and }}

注入服務與執行時參數

技能資源與腳本函式可在執行時接收外部應用程式上下文。

Skill 資源與腳本代理可以宣告一個 IServiceProvider 參數,Agent Framework 會自動注入參數。 這可讓技能視需要解析已註冊的應用程式服務。

設定

註冊應用程式服務,然後通過IServiceProvider參數將建置的services交給代理程式。

using Microsoft.Extensions.DependencyInjection;

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

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

使用 DI 的程式碼定義技能

IServiceProvider 宣告為 AddResourceAddScript 委派中的參數——當代理程式讀取資源或執行指令碼時,框架會自動解析並注入該參數:

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

類別型技能搭配 DI

使用 [AgentSkillResource][AgentSkillScript] 註解方法,並宣告一個 IServiceProvider 參數——框架會透過反射探索到這些成員,並自動注入服務提供者:

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

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

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

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

Tip

基於類別的技能也能透過 建構器解決相依關係。 在 ServiceCollection 中註冊技能類別,並從容器中解析它,而不是直接呼叫 new

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

當技能類別本身需要注入超出資源與腳本委託範圍的服務時,這非常有用。

接受 **kwargs 的資源和指令碼函式會接收傳遞給 agent.run() 的、由主機提供的執行階段關鍵字引數。 資源函式僅接收這些執行時參數。 腳本函式會將它們與模型提供的 args 映射項目合併,所以不要把腳本 **kwargs 中的值當作主機提供的證明。

傳遞執行時參數

Pass function_invocation_kwargs to agent.run() 用來提供關鍵詞參數,框架會將其轉發給資源函式與腳本函式:

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

具有 kwarg 的程式碼定義技能

當資源函式宣 **kwargs告 時,框架會在代理讀取資源時轉發執行時關鍵字的參數:

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

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

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

沒有 **kwargs 參數的資源函式則無參數呼叫,且不會接收執行時上下文。

當腳本函式宣告 **kwargs 時,框架會將執行時關鍵字參數與代理提供的 args 一同轉發:

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

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

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

    Args:
        value: The numeric value to convert (provided by the agent).
        factor: Conversion factor (provided by the agent).
        **kwargs: Additional values from tool-call args or agent.run().
    """
    precision = kwargs.get("precision", 4)
    result = round(value * factor, precision)
    return json.dumps({"value": value, "factor": factor, "result": result})

代理透過工具呼叫value提供 factorargs ;應用程式透過 precision提供 function_invocation_kwargs 。 模型提供的 args 映射中未宣告的條目也可以綁定到 **kwargs。 沒有 **kwargs 的指令碼函式只會接收其所宣告的、由代理程式提供的引數。

具有 kwarg 的類別型技能

以類別為基礎的技能方法也可以接受 **kwargs。 同樣適用資源和腳本論點規則。

from typing import Any
from agent_framework import ClassSkill, SkillFrontmatter

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

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

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

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

安全性最佳做法

Agent Skills 應該像你帶進專案的任何第三方程式碼一樣來對待。因為技能指令會注入代理的情境中——技能也可以包含腳本——因此必須以與開源相依相同的審查與治理程度來處理。

  • 使用前先複習 ——部署前閱讀所有技能內容(SKILL.md包括腳本和資源)。 確認腳本的實際行為是否符合其宣稱的意圖。 檢查是否有敵對指令試圖繞過安全指引、資料外洩或修改代理設定檔。
  • 來源信任 ——僅安裝來自可信作者或經過審核的內部貢獻者的技能。 偏好具備明確來源、版本控制及主動維護的技能。 請注意模仿熱門套件的誤植技能名稱。
  • 沙箱 - 在隔離的環境中執行包含可執行指令碼的技能。 限制檔案系統、網路及系統層級的存取權限,僅限技能所需的範圍。 執行可能敏感的操作前,要求明確的使用者確認。
  • 稽核與日誌 - 記錄哪些技能已載入、讀取了哪些資源,以及執行了哪些腳本。 這會讓你有稽核紀錄,在出錯時可以追溯客服人員行為至特定技能內容。

何時使用技能 vs. 工作流程

Agent Skills 與 Agent Framework Workflow 都擴展了 Agents 的功能,但它們的運作方式根本不同。 選擇最適合您需求的方案:

  • 控制 ——透過一項技能,AI 決定如何執行指令。 當你希望經紀人具創意或適應力時,這非常理想。 有了工作流程,你明確定義了執行路徑。 當你需要確定性且可預測的行為時,使用工作流程。
  • 韌性 ——一項技能會在單一代理人回合內完成。 若失敗,整個行動必須重試。 工作流程支援檢查點,因此在失敗後可以從最後一個成功步驟繼續。 當整個流程重執行成本高時,選擇工作流程。
  • 副作用 - 當作業具冪等性或風險較低時,適合使用技能。 當步驟產生副作用(如發送電子郵件、收費)且不應重試時重複時,優先選擇工作流程。
  • 複雜度 ——技能最適合專注於單一領域的任務,且由一位代理人能處理。 工作流程更適合多步驟業務流程,協調多位客服人員、人工核准或外部系統整合。

Tip

一般來說:如果你想讓 AI 自己決定 怎麼 完成任務,就用技能。 如果你需要確保 哪些 步驟會執行、順序如何,就用工作流程。

下一步