代理人技能

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

使用代理技能的時機是:

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

技能結構

技能是一個目錄,其中包含一個 SKILL.md 檔案,並且可以有用於儲存資源的子目錄:

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

SKILL.md 格式

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

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

前置資料後的 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 等來源類別。

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

檔案式技能

建立一個指向包含你的技能的目錄的 AgentSkillsProvider,並將其加入代理程序的上下文提供者。 傳遞一個腳本執行工具,來啟用執行技能目錄中基於檔案的腳本:

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

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

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

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

警告

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

多重技能目錄

你可以將提供者指向單一的父目錄——每個包含 SKILL.md 的子目錄都會自動識別為一項技能:

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

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

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

提供者會搜尋最多兩層深度。

自訂資源與指令碼探索

預設情況下,提供者識別的資源副檔名為 .md.json.yaml.yml.csv.xml.txt和 ,以及具有副檔名 .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 字串陣列的形式呈現——每個陣列元素都成為位置命令列參數。

警告

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/ 檔案中被發現。

警告

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

  • 沙盒(例如容器、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"]例如 ,並可依腳本需求解析這些參數。

小提示

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

職業技能

基於職業的技能可以讓你將所有技能組成部分——名稱、描述、指示、資源和腳本——打包到一個 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> 同時支援將Resources覆蓋,以及將Scripts作為集合,以應對屬性基礎發現不適用的情況。

職業技能

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

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

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

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

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

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

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

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

註冊類別技能:SkillsProvider

from agent_framework import SkillsProvider

skill = UnitConverterSkill()
skills_provider = SkillsProvider(skill)

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

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

備註

ClassSkill 也支援明確覆寫 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、TAR或gzip壓縮TAR)形式發佈,並可在本地下載並解壓。

基本使用方式

使用 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

Important

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

以 MCP 為基礎的技能

備註

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

可從在 skill:// URI 配置下公開技能資源的 MCP(模型情境協定)伺服器探索到技能。 MCP 伺服器透過 skill://index.json 探索文件來宣告技能,而框架會在需要時透過 SKILL.md 擷取各技能的 resources/read 內容。

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

備註

Python MCPSkillsSourceskill-md支援索引條目(其他類型的索引條目會靜默跳過)。 與 .NET 實作不同,它支援歸檔型技能。 若 skill://index.json 缺失、無法讀取、空或無法解析,原始碼會回傳一個空清單。

Important

外部 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。 完整可用資源及其選項,請參閱 技能來源

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

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

呼叫者提供的 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 沒有影響(若是那些情況,請自行使用 CachingSkillsSource 組合自己的 refresh_interval)。

備註

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

工具核准

所有由 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,
    });

警告

任一種方法都可能向模型揭露原始例外細節。 例外訊息可能包含敏感資訊,如連線字串、檔案路徑或內部服務名稱。 此外,若技能或腳本來自不受信任的來源,惡意撰寫的腳本可能會拋出例外,該例外訊息中嵌入了提示注入有效載荷。

所有由 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()上取得。

警告

僅對來自你信任來源的技能和指令碼停用核准,或自動核准指令碼執行。 技能指令會注入代理的上下文中,並 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} 作為產生技能清單的佔位符。 字面上的大括號必須分別跳脫為{{}}

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} (腳本工具提示)佔位符;若存在,則以內建指引填充;若省略,則不被渲染(對應工具仍會被註冊)。 字面上的大括號必須分別跳脫為{{}}

注入服務與執行時參數

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

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

小提示

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

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

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

接受的 **kwargs 資源與腳本函式會自動接收傳遞給 agent.run()的執行時關鍵字參數。 這讓技能功能能存取應用程式上下文——例如設定、使用者身份或服務客戶端——而不必將它們硬編碼進技能定義中。

傳遞執行時參數

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

以程式碼定義且帶有 kwargs 的技能

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

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

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

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

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

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

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

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

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

    Args:
        value: The numeric value to convert (provided by the agent).
        factor: Conversion factor (provided by the agent).
        **kwargs: Runtime keyword arguments from agent.run().
    """
    precision = kwargs.get("precision", 4)
    result = round(value * factor, precision)
    return json.dumps({"value": value, "factor": factor, "result": result})

代理透過工具呼叫value提供 factorargs ;應用程式透過 precision提供 function_invocation_kwargs 。 腳本函式在沒有 **kwargs 的情況下僅接收代理提供的參數。

含 kwargs 的以類別為基礎的技能

基於類別的技能方法也可以接受 **kwargs,以接收執行階段引數。 模式運作方式相同——在資源方法或腳本方法上宣告 **kwargs

from typing import Any
from agent_framework import ClassSkill, SkillFrontmatter

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

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

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

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

安全性最佳做法

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

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

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

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

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

小提示

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

後續步驟