函数调用 #

概述 #

函数调用(Function Calling)允许 LLM 根据用户请求自动选择并调用预定义的函数,实现与外部系统的交互。

工作原理 #

text
┌─────────────────────────────────────────────────────────────┐
│                    函数调用流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 用户请求                                                │
│     "北京今天天气怎么样?"                                   │
│                                                             │
│  2. LLM 分析                                                │
│     ┌─────────────────────────────────────────────────┐    │
│     │  需要调用函数:GetWeather                         │    │
│     │  参数:city = "北京"                             │    │
│     └─────────────────────────────────────────────────┘    │
│                                                             │
│  3. 执行函数                                                │
│     GetWeather("北京") → "晴天,25°C"                       │
│                                                             │
│  4. LLM 生成回答                                            │
│     "北京今天天气晴朗,气温25°C,适合外出活动。"            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

启用函数调用 #

基本配置 #

csharp
using Microsoft.SemanticKernel.Connectors.OpenAI;

var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var result = await kernel.InvokePromptAsync(
    "北京今天天气怎么样?",
    new KernelArguments(settings)
);

注册函数 #

csharp
public class WeatherPlugin
{
    [KernelFunction("get_weather")]
    [Description("获取指定城市的天气信息")]
    public async Task<string> GetWeatherAsync(
        [Description("城市名称")] string city)
    {
        // 模拟天气 API 调用
        return $"{city}今天天气晴朗,气温25°C";
    }
}

kernel.Plugins.AddFromType<WeatherPlugin>("Weather");

函数调用模式 #

Auto 模式 #

csharp
// 自动决定是否调用函数
var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

Required 模式 #

csharp
// 强制调用函数
var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Required(
        functions: new[] { kernel.Plugins["Weather"]["get_weather"] }
    )
};

None 模式 #

csharp
// 禁用函数调用
var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.None()
};

完整示例 #

多函数调用 #

csharp
public class UtilityPlugin
{
    [KernelFunction("get_weather")]
    [Description("获取城市天气")]
    public async Task<string> GetWeatherAsync(string city)
    {
        return $"{city}:晴天,25°C";
    }

    [KernelFunction("get_time")]
    [Description("获取当前时间")]
    public string GetTime()
    {
        return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    }

    [KernelFunction("calculate")]
    [Description("执行数学计算")]
    public double Calculate(string expression)
    {
        var result = new DataTable().Compute(expression, null);
        return Convert.ToDouble(result);
    }
}

kernel.Plugins.AddFromType<UtilityPlugin>("Utility");

var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

// 可能会调用多个函数
var result = await kernel.InvokePromptAsync(
    "现在几点了?北京天气怎么样?还有 25 + 17 等于多少?",
    new KernelArguments(settings)
);

嵌套函数调用 #

csharp
public class DataPlugin
{
    [KernelFunction("get_user")]
    [Description("获取用户信息")]
    public User GetUser(int userId)
    {
        return new User { Id = userId, Name = "张三", City = "北京" };
    }

    [KernelFunction("get_orders")]
    [Description("获取用户订单")]
    public List<Order> GetOrders(int userId)
    {
        return new List<Order>
        {
            new Order { Id = 1, Product = "商品A", Amount = 100 },
            new Order { Id = 2, Product = "商品B", Amount = 200 }
        };
    }
}

// LLM 会自动先调用 get_user 获取用户信息,然后调用 get_orders
var result = await kernel.InvokePromptAsync(
    "查询用户ID为1的订单信息",
    new KernelArguments(settings)
);

处理函数调用结果 #

监听函数调用 #

csharp
kernel.FunctionInvoking += (sender, e) =>
{
    Console.WriteLine($"调用函数: {e.Function.Name}");
    Console.WriteLine($"参数: {string.Join(", ", e.Arguments)}");
};

kernel.FunctionInvoked += (sender, e) =>
{
    Console.WriteLine($"结果: {e.Result}");
};

获取函数调用详情 #

csharp
var result = await kernel.InvokePromptAsync(
    "北京天气怎么样?",
    new KernelArguments(settings)
);

// 检查是否有函数调用
if (result.Metadata?.TryGetValue("FunctionCall", out var functionCall) == true)
{
    Console.WriteLine($"调用了函数: {functionCall}");
}

高级用法 #

并行函数调用 #

csharp
var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(
        options: new FunctionChoiceBehaviorOptions
        {
            AllowParallelCalls = true  // 允许并行调用
        }
    )
};

// LLM 可能同时请求多个独立的函数调用
var result = await kernel.InvokePromptAsync(
    "北京和上海的天气分别是多少?",
    new KernelArguments(settings)
);

流式函数调用 #

csharp
await foreach (var content in kernel.InvokePromptStreamingAsync<StreamingChatMessageContent>(
    "北京天气怎么样?",
    new KernelArguments(settings)))
{
    if (content.Content != null)
    {
        Console.Write(content.Content);
    }
    
    if (content.FunctionCall != null)
    {
        Console.WriteLine($"\n调用函数: {content.FunctionCall.Name}");
    }
}

函数调用确认 #

csharp
public class ConfirmationFilter : IFunctionFilter
{
    public async Task OnFunctionInvocationAsync(
        FunctionInvocationContext context,
        Func<FunctionInvocationContext, Task> next)
    {
        // 敏感函数需要确认
        if (context.Function.Name.Contains("delete") || 
            context.Function.Name.Contains("send"))
        {
            Console.WriteLine($"即将执行: {context.Function.Name}");
            Console.WriteLine("确认执行?(y/n)");
            
            var confirm = Console.ReadLine();
            if (confirm?.ToLower() != "y")
            {
                context.Result = new FunctionResult(context.Function, "用户取消操作");
                return;
            }
        }
        
        await next(context);
    }
}

kernel.FunctionFilters.Add(new ConfirmationFilter());

实战示例 #

智能助手 #

csharp
public class AssistantPlugin
{
    private readonly IEmailService _emailService;
    private readonly ICalendarService _calendarService;
    private readonly ITaskService _taskService;

    [KernelFunction("send_email")]
    [Description("发送邮件")]
    public async Task<string> SendEmailAsync(
        [Description("收件人")] string to,
        [Description("主题")] string subject,
        [Description("内容")] string body)
    {
        await _emailService.SendAsync(to, subject, body);
        return $"邮件已发送给 {to}";
    }

    [KernelFunction("create_event")]
    [Description("创建日历事件")]
    public async Task<string> CreateEventAsync(
        [Description("事件标题")] string title,
        [Description("开始时间")] DateTime startTime,
        [Description("持续时间(分钟)")] int durationMinutes)
    {
        await _calendarService.CreateEventAsync(title, startTime, durationMinutes);
        return $"已创建事件: {title}";
    }

    [KernelFunction("add_task")]
    [Description("添加任务")]
    public async Task<string> AddTaskAsync(
        [Description("任务内容")] string content,
        [Description("截止日期")] DateTime? dueDate = null)
    {
        await _taskService.AddTaskAsync(content, dueDate);
        return $"已添加任务: {content}";
    }
}

// 使用
kernel.Plugins.AddFromType<AssistantPlugin>("Assistant");

var settings = new OpenAIPromptExecutionSettings
{
    FunctionChoiceBehavior = FunctionChoiceBehavior.Auto()
};

var result = await kernel.InvokePromptAsync(
    "帮我给张三发邮件,主题是项目进度,内容是本周完成了核心功能开发",
    new KernelArguments(settings)
);

最佳实践 #

1. 清晰的函数描述 #

csharp
[KernelFunction("search_products")]
[Description("""
    搜索产品信息。
    
    参数:
    - query: 搜索关键词
    - category: 产品类别(可选)
    - minPrice: 最低价格(可选)
    - maxPrice: 最高价格(可选)
    
    返回:匹配的产品列表
    """)]
public async Task<string> SearchProductsAsync(
    string query,
    string? category = null,
    decimal? minPrice = null,
    decimal? maxPrice = null)
{
    // 实现
}

2. 参数验证 #

csharp
[KernelFunction("transfer_money")]
public async Task<string> TransferMoneyAsync(
    string fromAccount,
    string toAccount,
    decimal amount)
{
    if (amount <= 0)
        throw new ArgumentException("金额必须大于0");
    
    if (string.IsNullOrEmpty(fromAccount) || string.IsNullOrEmpty(toAccount))
        throw new ArgumentException("账户不能为空");
    
    // 执行转账
    return "转账成功";
}

3. 错误处理 #

csharp
[KernelFunction("api_call")]
public async Task<string> ApiCallAsync(string endpoint)
{
    try
    {
        var result = await _httpClient.GetStringAsync(endpoint);
        return result;
    }
    catch (HttpRequestException ex)
    {
        return $"API 调用失败: {ex.Message}";
    }
}

下一步 #

现在你已经掌握了函数调用,接下来学习 过滤器,了解如何拦截和处理请求!

最后更新:2026-04-04