函数调用 #
概述 #
函数调用(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