插件系统 #

概述 #

插件(Plugin)是 Semantic Kernel 中封装可复用功能的基本单元。每个插件可以包含多个函数,这些函数可以是原生代码实现的,也可以是基于提示词的语义函数。

插件架构 #

text
┌─────────────────────────────────────────────────────────────┐
│                    插件架构                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Kernel                                                     │
│     │                                                       │
│     ├── Plugin: "MathPlugin"                                │
│     │      ├── Function: "Add" (Native)                     │
│     │      ├── Function: "Subtract" (Native)                │
│     │      └── Function: "Solve" (Semantic)                 │
│     │                                                       │
│     ├── Plugin: "TextPlugin"                                │
│     │      ├── Function: "Summarize" (Semantic)             │
│     │      ├── Function: "Translate" (Semantic)             │
│     │      └── Function: "CountWords" (Native)              │
│     │                                                       │
│     └── Plugin: "FilePlugin"                                │            │      ├── Function: "Read" (Native)                        │
│            │      └── Function: "Write" (Native)                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

原生插件 #

创建原生插件 #

csharp
using System.ComponentModel;
using Microsoft.SemanticKernel;

public class MathPlugin
{
    [KernelFunction("add")]
    [Description("将两个数字相加并返回结果")]
    public double Add(
        [Description("第一个数字")] double a,
        [Description("第二个数字")] double b)
    {
        return a + b;
    }

    [KernelFunction("subtract")]
    [Description("将两个数字相减并返回结果")]
    public double Subtract(
        [Description("被减数")] double a,
        [Description("减数")] double b)
    {
        return a - b;
    }

    [KernelFunction("multiply")]
    [Description("将两个数字相乘并返回结果")]
    public double Multiply(
        [Description("第一个数字")] double a,
        [Description("第二个数字")] double b)
    {
        return a * b;
    }

    [KernelFunction("divide")]
    [Description("将两个数字相除并返回结果")]
    public double Divide(
        [Description("被除数")] double a,
        [Description("除数")] double b)
    {
        if (b == 0)
            throw new ArgumentException("除数不能为零");
        return a / b;
    }
}

注册原生插件 #

csharp
var kernel = Kernel.CreateBuilder()
    .AddOpenAIChatCompletion("gpt-4", "api-key")
    .Build();

// 方式 1:从类型注册
kernel.Plugins.AddFromType<MathPlugin>("Math");

// 方式 2:从实例注册
var mathPlugin = new MathPlugin();
kernel.Plugins.AddFromObject(mathPlugin, "Math");

// 方式 3:使用 KernelPluginFactory
var plugin = KernelPluginFactory.CreateFromType<MathPlugin>("Math");
kernel.Plugins.Add(plugin);

带依赖注入的插件 #

csharp
public class DatabasePlugin
{
    private readonly IDatabaseService _db;

    public DatabasePlugin(IDatabaseService db)
    {
        _db = db;
    }

    [KernelFunction("query")]
    [Description("执行数据库查询")]
    public async Task<string> QueryAsync(
        [Description("SQL 查询语句")] string sql)
    {
        return await _db.ExecuteQueryAsync(sql);
    }
}

// 注册时注入依赖
var dbService = new DatabaseService();
kernel.Plugins.AddFromObject(new DatabasePlugin(dbService), "Database");

复杂参数类型 #

csharp
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string[] Interests { get; set; }
}

public class UserPlugin
{
    [KernelFunction("create_user")]
    [Description("创建新用户")]
    public User CreateUser(
        [Description("用户信息")] User user)
    {
        return user;
    }

    [KernelFunction("get_users")]
    [Description("获取用户列表")]
    public User[] GetUsers()
    {
        return new[]
        {
            new User { Name = "Alice", Age = 25, Interests = new[] { "Reading", "Music" } },
            new User { Name = "Bob", Age = 30, Interests = new[] { "Sports", "Travel" } }
        };
    }
}

语义插件 #

从提示词创建 #

csharp
var summarizeFunction = kernel.CreateFunctionFromPrompt(
    promptTemplate: """
        请总结以下文本的主要内容:
        
        文本:
        {{$input}}
        
        总结:
        """,
    functionName: "Summarize",
    description: "总结文本内容"
);

var plugin = KernelPluginFactory.CreateFromFunctions(
    "TextPlugin",
    "文本处理插件",
    new[] { summarizeFunction }
);

kernel.Plugins.Add(plugin);

从文件加载 #

text
Prompts/
└── TextPlugin/
    ├── Summarize/
    │   ├── config.json
    │   └── skprompt.txt
    └── Translate/
        ├── config.json
        └── skprompt.txt

config.json:

json
{
    "schema": 1,
    "description": "总结文本内容",
    "execution_settings": {
        "default": {
            "max_tokens": 500,
            "temperature": 0.3
        }
    },
    "input_variables": [
        {
            "name": "input",
            "description": "需要总结的文本",
            "required": true
        }
    ]
}

skprompt.txt:

text
请总结以下文本的主要内容:

文本:
{{$input}}

总结:

加载插件:

csharp
var plugin = kernel.ImportPluginFromPromptDirectory("./Prompts", "TextPlugin");

使用 Handlebars 模板 #

csharp
using Microsoft.SemanticKernel.PromptTemplates.Handlebars;

var function = kernel.CreateFunctionFromPrompt(
    promptTemplate: """
        请为以下产品写一段描述:
        
        产品名称:{{name}}
        产品类型:{{type}}
        主要特点:
        {{#each features}}
        - {{this}}
        {{/each}}
        
        描述:
        """,
    templateFormat: "handlebars",
    promptTemplateFactory: new HandlebarsPromptTemplateFactory()
);

kernel.Plugins.Add(KernelPluginFactory.CreateFromFunctions(
    "ProductPlugin",
    new[] { function }
));

内置插件 #

ConversationSummaryPlugin #

csharp
using Microsoft.SemanticKernel.Plugins.Core;

kernel.ImportPluginFromType<ConversationSummaryPlugin>();

var summary = await kernel.InvokeAsync(
    "ConversationSummaryPlugin",
    "SummarizeConversation",
    new KernelArguments
    {
        ["input"] = "长对话内容..."
    }
);

FileIOPlugin #

csharp
using Microsoft.SemanticKernel.Plugins.Core;

kernel.ImportPluginFromType<FileIOPlugin>();

// 读取文件
var content = await kernel.InvokeAsync(
    "FileIOPlugin",
    "ReadAsync",
    new KernelArguments { ["path"] = "/path/to/file.txt" }
);

// 写入文件
await kernel.InvokeAsync(
    "FileIOPlugin",
    "WriteAsync",
    new KernelArguments
    {
        ["path"] = "/path/to/output.txt",
        ["content"] = "要写入的内容"
    }
);

HttpPlugin #

csharp
using Microsoft.SemanticKernel.Plugins.Core;

kernel.ImportPluginFromType<HttpPlugin>();

var response = await kernel.InvokeAsync(
    "HttpPlugin",
    "GetAsync",
    new KernelArguments { ["uri"] = "https://api.example.com/data" }
);

TimePlugin #

csharp
using Microsoft.SemanticKernel.Plugins.Core;

kernel.ImportPluginFromType<TimePlugin>();

var now = await kernel.InvokeAsync("TimePlugin", "Now");
var date = await kernel.InvokeAsync("TimePlugin", "Date");
var time = await kernel.InvokeAsync("TimePlugin", "Time");

插件组合 #

链式调用 #

csharp
// 步骤 1:获取数据
var data = await kernel.InvokeAsync(
    "DatabasePlugin",
    "Query",
    new KernelArguments { ["sql"] = "SELECT * FROM products" }
);

// 步骤 2:处理数据
var processed = await kernel.InvokeAsync(
    "TextPlugin",
    "Summarize",
    new KernelArguments { ["input"] = data.ToString() }
);

// 步骤 3:翻译
var translated = await kernel.InvokeAsync(
    "TranslatePlugin",
    "ToChinese",
    new KernelArguments { ["input"] = processed.ToString() }
);

使用 Planner 自动编排 #

csharp
using Microsoft.SemanticKernel.Planning;

var planner = new FunctionCallingStepwisePlanner();

var result = await planner.ExecuteAsync(
    kernel,
    "查询数据库中的产品信息,然后生成一份中文报告"
);

OpenAPI 插件 #

从 OpenAPI 规范导入 #

csharp
var plugin = await kernel.ImportPluginFromOpenApiAsync(
    pluginName: "WeatherApi",
    filePath: "openapi.json",
    executionParameters: new OpenApiFunctionExecutionParameters
    {
        ServerUrlOverride = new Uri("https://api.weather.com")
    }
);

var weather = await kernel.InvokeAsync(
    "WeatherApi",
    "GetCurrentWeather",
    new KernelArguments
    {
        ["city"] = "北京"
    }
);

配置认证 #

csharp
var authCallback = async (HttpRequestMessage request, CancellationToken cancellationToken) =>
{
    request.Headers.Authorization = new AuthenticationHeaderValue(
        "Bearer",
        "your-access-token"
    );
};

var plugin = await kernel.ImportPluginFromOpenApiAsync(
    "MyApi",
    "openapi.json",
    new OpenApiFunctionExecutionParameters
    {
        AuthCallback = authCallback
    }
);

插件最佳实践 #

1. 清晰的命名和描述 #

csharp
public class GoodPlugin
{
    [KernelFunction("calculate_discount")]
    [Description("根据购买金额和会员等级计算折扣价格")]
    public double CalculateDiscount(
        [Description("原始购买金额,必须大于0")] double amount,
        [Description("会员等级:Bronze, Silver, Gold, Platinum")] string memberLevel)
    {
        // 实现
    }
}

2. 参数验证 #

csharp
[KernelFunction("divide")]
public double Divide(double a, double b)
{
    if (b == 0)
    {
        throw new KernelException("除数不能为零");
    }
    return a / b;
}

3. 异步操作 #

csharp
public class AsyncPlugin
{
    [KernelFunction("fetch_data")]
    public async Task<string> FetchDataAsync(string url)
    {
        using var client = new HttpClient();
        return await client.GetStringAsync(url);
    }
}

4. 错误处理 #

csharp
[KernelFunction("process_file")]
public async Task<string> ProcessFileAsync(string path)
{
    try
    {
        var content = await File.ReadAllTextAsync(path);
        return content;
    }
    catch (FileNotFoundException)
    {
        return $"文件未找到: {path}";
    }
    catch (Exception ex)
    {
        return $"处理文件时出错: {ex.Message}";
    }
}

5. 日志记录 #

csharp
public class LoggingPlugin
{
    private readonly ILogger<LoggingPlugin> _logger;

    public LoggingPlugin(ILogger<LoggingPlugin> logger)
    {
        _logger = logger;
    }

    [KernelFunction("important_operation")]
    public async Task<string> ImportantOperationAsync(string input)
    {
        _logger.LogInformation("开始执行重要操作: {Input}", input);
        
        try
        {
            var result = await DoWorkAsync(input);
            _logger.LogInformation("操作成功完成");
            return result;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "操作执行失败");
            throw;
        }
    }
}

下一步 #

现在你已经掌握了插件系统,接下来学习 函数,深入了解函数的创建和调用!

最后更新:2026-04-04