函数 #

概述 #

函数(Function)是 Semantic Kernel 中最小的执行单元。每个函数都属于一个插件,可以是语义函数(基于提示词)或原生函数(代码实现)。

函数类型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    函数类型                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Semantic Function(语义函数)                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  特点:                                              │   │
│  │  - 基于 LLM 执行                                    │   │
│  │  - 使用提示词模板                                   │   │
│  │  - 支持变量替换                                     │   │
│  │  - 可配置执行参数                                   │   │
│  │                                                      │   │
│  │  示例:                                              │   │
│  │  - 文本摘要                                         │   │
│  │  - 翻译                                             │   │
│  │  - 内容生成                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Native Function(原生函数)                                 │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  特点:                                              │   │
│  │  - 使用代码实现                                     │   │
│  │  - 直接执行                                         │   │
│  │  - 不消耗 LLM tokens                                │   │
│  │  - 可访问外部资源                                   │   │
│  │                                                      │   │
│  │  示例:                                              │   │
│  │  - 数学计算                                         │   │
│  │  - 文件操作                                         │   │
│  │  - API 调用                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

创建语义函数 #

从提示词创建 #

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

带执行设置 #

csharp
using Microsoft.SemanticKernel.Connectors.OpenAI;

var function = kernel.CreateFunctionFromPrompt(
    promptTemplate: "写一首关于{{$topic}}的诗",
    functionName: "WritePoem",
    description: "根据主题写诗",
    executionSettings: new OpenAIPromptExecutionSettings
    {
        MaxTokens = 500,
        Temperature = 0.8
    }
);

带输入变量定义 #

csharp
var function = kernel.CreateFunctionFromPrompt(
    promptTemplate: """
        你是一个{{$role}}。
        
        请回答以下问题:{{$question}}
        
        回答:
        """,
    functionName: "AskExpert",
    description: "以专家身份回答问题",
    inputVariables: new[]
    {
        new InputVariable
        {
            Name = "role",
            Description = "专家角色",
            IsRequired = true
        },
        new InputVariable
        {
            Name = "question",
            Description = "用户问题",
            IsRequired = true
        }
    }
);

从文件加载 #

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

config.json:

json
{
    "schema": 1,
    "description": "总结文本内容",
    "execution_settings": {
        "default": {
            "max_tokens": 300,
            "temperature": 0.3
        }
    },
    "input_variables": [
        {
            "name": "input",
            "description": "需要总结的文本",
            "required": true
        },
        {
            "name": "language",
            "description": "输出语言",
            "required": false,
            "default": "中文"
        }
    ]
}

skprompt.txt:

text
请用{{$language}}总结以下文本:

{{$input}}

总结:

加载函数:

csharp
var plugin = kernel.ImportPluginFromPromptDirectory("./Prompts");
var function = plugin["Summarize"];

创建原生函数 #

基本方法 #

csharp
using System.ComponentModel;
using Microsoft.SemanticKernel;

public class MathPlugin
{
    [KernelFunction]
    [Description("计算两个数的和")]
    public double Add(
        [Description("第一个数")] double a,
        [Description("第二个数")] double b)
    {
        return a + b;
    }
}

从方法创建 #

csharp
public static class HelperFunctions
{
    public static string FormatDate(DateTime date)
    {
        return date.ToString("yyyy-MM-dd");
    }
    
    public static int CalculateAge(DateTime birthDate)
    {
        return DateTime.Now.Year - birthDate.Year;
    }
}

var formatDate = kernel.CreateFunctionFromMethod(
    method: HelperFunctions.FormatDate,
    functionName: "FormatDate",
    description: "格式化日期"
);

var calculateAge = kernel.CreateFunctionFromMethod(
    method: HelperFunctions.CalculateAge,
    functionName: "CalculateAge",
    description: "计算年龄"
);

异步函数 #

csharp
public class AsyncPlugin
{
    [KernelFunction("fetch_url")]
    [Description("获取 URL 内容")]
    public async Task<string> FetchUrlAsync(
        [Description("URL 地址")] string url)
    {
        using var client = new HttpClient();
        return await client.GetStringAsync(url);
    }
}

返回复杂类型 #

csharp
public class SearchResult
{
    public string Title { get; set; }
    public string Url { get; set; }
    public string Snippet { get; set; }
}

public class SearchPlugin
{
    [KernelFunction("search")]
    [Description("搜索网络内容")]
    public async Task<SearchResult[]> SearchAsync(
        [Description("搜索关键词")] string query)
    {
        // 实现搜索逻辑
        return new[]
        {
            new SearchResult
            {
                Title = "结果1",
                Url = "https://example.com/1",
                Snippet = "摘要..."
            }
        };
    }
}

调用函数 #

通过 Kernel 调用 #

csharp
// 调用语义函数
var result = await kernel.InvokePromptAsync("你好");

// 调用插件中的函数
var result = await kernel.InvokeAsync(
    pluginName: "Math",
    functionName: "Add",
    arguments: new KernelArguments
    {
        ["a"] = 5,
        ["b"] = 3
    }
);

通过函数引用调用 #

csharp
var function = kernel.Plugins["Math"]["Add"];

var result = await kernel.InvokeAsync(
    function,
    new KernelArguments { ["a"] = 5, ["b"] = 3 }
);

链式调用 #

csharp
var result = await kernel.Plugins["Math"]["Add"]
    .InvokeAsync(kernel, new KernelArguments { ["a"] = 5, ["b"] = 3 });

参数传递 #

KernelArguments 基本用法 #

csharp
var arguments = new KernelArguments
{
    ["name"] = "小明",
    ["age"] = 25,
    ["interests"] = new[] { "编程", "阅读" }
};

var result = await kernel.InvokePromptAsync(
    "你好{{$name}},你今年{{$age}}岁",
    arguments
);

结合执行设置 #

csharp
var settings = new OpenAIPromptExecutionSettings
{
    Temperature = 0.7,
    MaxTokens = 500
};

var arguments = new KernelArguments(settings)
{
    ["topic"] = "人工智能"
};

动态构建参数 #

csharp
var arguments = new KernelArguments();

// 根据条件添加参数
if (includeDetails)
{
    arguments["details"] = "详细信息";
}

// 从字典构建
var dict = new Dictionary<string, object?>
{
    ["key1"] = "value1",
    ["key2"] = "value2"
};
foreach (var kvp in dict)
{
    arguments[kvp.Key] = kvp.Value;
}

函数结果 #

获取字符串结果 #

csharp
var result = await kernel.InvokeAsync("Plugin", "Function", arguments);
string text = result.ToString();

获取类型化结果 #

csharp
var result = await kernel.InvokeAsync<SearchResult[]>("Search", "Search", arguments);
var searchResults = result.GetValue<SearchResult[]>();

获取元数据 #

csharp
var result = await kernel.InvokePromptAsync("问题");

var metadata = result.Metadata;
if (metadata != null)
{
    var usage = metadata["Usage"] as Dictionary<string, object>;
    Console.WriteLine($"Prompt Tokens: {usage?["PromptTokens"]}");
    Console.WriteLine($"Completion Tokens: {usage?["CompletionTokens"]}");
}

流式执行 #

流式提示词 #

csharp
await foreach (var chunk in kernel.InvokePromptStreamingAsync("写一首诗"))
{
    Console.Write(chunk);
}

流式函数调用 #

csharp
var function = kernel.Plugins["Text"]["WriteStory"];

await foreach (var chunk in kernel.InvokeStreamingAsync(function, arguments))
{
    Console.Write(chunk);
}

完整流式示例 #

csharp
var function = kernel.CreateFunctionFromPrompt(
    "写一篇关于{{$topic}}的长文章",
    functionName: "WriteArticle"
);

var fullContent = new StringBuilder();

await foreach (var streamingContent in kernel.InvokeStreamingAsync<StreamingChatMessageContent>(
    function,
    new KernelArguments { ["topic"] = "人工智能" }))
{
    var content = streamingContent.Content;
    if (content != null)
    {
        Console.Write(content);
        fullContent.Append(content);
    }
}

Console.WriteLine("\n--- 完整内容 ---");
Console.WriteLine(fullContent.ToString());

函数组合 #

手动组合 #

csharp
// 步骤 1:翻译
var translated = await kernel.InvokeAsync(
    "Translate", "ToEnglish",
    new KernelArguments { ["input"] = chineseText }
);

// 步骤 2:摘要
var summarized = await kernel.InvokeAsync(
    "Text", "Summarize",
    new KernelArguments { ["input"] = translated.ToString() }
);

// 步骤 3:翻译回中文
var result = await kernel.InvokeAsync(
    "Translate", "ToChinese",
    new KernelArguments { ["input"] = summarized.ToString() }
);

使用管道模式 #

csharp
public static class FunctionPipeline
{
    public static async Task<FunctionResult> PipelineAsync(
        this Kernel kernel,
        params (string Plugin, string Function, KernelArguments Arguments)[] steps)
    {
        FunctionResult? result = null;
        
        foreach (var (plugin, function, args) in steps)
        {
            if (result != null)
            {
                args["input"] = result.ToString();
            }
            
            result = await kernel.InvokeAsync(plugin, function, args);
        }
        
        return result!;
    }
}

// 使用
var result = await kernel.PipelineAsync(
    ("Translate", "ToEnglish", new KernelArguments { ["input"] = chineseText }),
    ("Text", "Summarize", new KernelArguments()),
    ("Translate", "ToChinese", new KernelArguments())
);

函数调用上下文 #

访问 Kernel #

csharp
[KernelFunction("process")]
public async Task<string> ProcessAsync(string input, Kernel kernel)
{
    // 可以在函数内调用其他函数
    var summary = await kernel.InvokeAsync("Text", "Summarize", 
        new KernelArguments { ["input"] = input });
    
    return summary.ToString();
}

使用 KernelFunctionAttribute #

csharp
public class ContextAwarePlugin
{
    [KernelFunction("analyze")]
    public async Task<string> AnalyzeAsync(
        string input,
        Kernel kernel,
        CancellationToken cancellationToken = default)
    {
        // 访问服务
        var chatService = kernel.GetRequiredService<IChatCompletionService>();
        
        // 调用其他函数
        var related = await kernel.InvokeAsync(
            "Search", "FindRelated",
            new KernelArguments { ["query"] = input },
            cancellationToken: cancellationToken
        );
        
        return $"分析结果: {related}";
    }
}

最佳实践 #

1. 清晰的函数描述 #

csharp
[KernelFunction("calculate_mortgage")]
[Description("""
    计算房屋贷款月供。
    
    输入:
    - principal: 贷款本金(元)
    - rate: 年利率(如 0.05 表示 5%)
    - years: 贷款年限
    
    输出:
    - 每月还款金额(元)
    """)]
public double CalculateMortgage(
    [Description("贷款本金")] double principal,
    [Description("年利率")] double rate,
    [Description("贷款年限")] int years)
{
    // 实现
}

2. 参数验证 #

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

3. 异常处理 #

csharp
[KernelFunction("safe_operation")]
public async Task<string> SafeOperationAsync(string input)
{
    try
    {
        // 执行操作
        return await DoWorkAsync(input);
    }
    catch (Exception ex)
    {
        return $"操作失败: {ex.Message}";
    }
}

4. 使用 CancellationToken #

csharp
[KernelFunction("long_operation")]
public async Task<string> LongOperationAsync(
    string input,
    CancellationToken cancellationToken = default)
{
    cancellationToken.ThrowIfCancellationRequested();
    
    // 长时间操作
    await Task.Delay(5000, cancellationToken);
    
    return "完成";
}

下一步 #

现在你已经掌握了函数系统,接下来学习 规划器,了解如何自动编排函数执行!

最后更新:2026-04-04