2025-11-05
C#
00

目录

什么是函数调用?
函数调用的工作原理
准备工作
实际案例:披萨订购插件
结论

在当今AI快速发展的背景下,函数调用功能为开发者提供了一种强大的方式,让聊天机器人能够与现有代码无缝交互。本文将深入探讨C#中如何利用Semantic Kernel实现高效的函数调用,帮助您构建能够自动化业务流程、生成代码片段等功能的智能应用。

什么是函数调用?

函数调用是聊天完成功能中最强大的特性之一,它允许模型调用您预先定义的函数。通过Semantic Kernel框架,这一过程被大大简化 - 它自动为模型描述您的函数及其参数,并处理模型与代码之间的通信。

函数调用的工作原理

当您使用启用了函数调用的模型发送请求时,Semantic Kernel会执行以下步骤:

  1. 序列化函数 - 将内核中所有可用函数及其输入参数使用JSON Schema进行序列化
  2. 发送消息和函数 - 将序列化的函数和当前聊天历史作为输入发送给模型
  3. 模型处理输入 - 模型处理输入并生成回应,可能是聊天消息或函数调用
  4. 处理回应 - 如果是聊天消息,直接返回;如果是函数调用,则提取函数名称和参数
  5. 调用函数 - 使用提取的函数名称和参数在内核中调用相应函数
  6. 返回函数结果 - 将函数结果返回给模型作为聊天历史的一部分,然后重复2-6步,直到模型返回聊天消息或达到最大迭代次数

准备工作

Markdown
Microsoft.SemanticKernel Microsoft.Extensions.Hosting

实际案例:披萨订购插件

让我们通过一个具体的Pizza订购插件示例来展示函数调用的工作原理:

C#
using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading.Tasks; using Microsoft.SemanticKernel; using Microsoft.SemanticKernel.ChatCompletion; using Microsoft.SemanticKernel.Connectors.OpenAI; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Hosting; using OpenAI; using System.ClientModel; namespace PizzaOrderingBot { class Program { static async Task Main(string[] args) { // 设置依赖注入容器 var services = new ServiceCollection(); // 注册服务 services.AddSingleton<IPizzaService, MockPizzaService>(); services.AddSingleton<IUserContext, MockUserContext>(); services.AddSingleton<IPaymentService, MockPaymentService>(); // 添加日志 services.AddLogging(builder => { builder.AddConsole(); builder.SetMinimumLevel(LogLevel.Information); }); var serviceProvider = services.BuildServiceProvider(); // 创建Semantic Kernel var kernelBuilder = Kernel.CreateBuilder(); // 配置OpenAI客户端凭证 var openAIClientCredential = new ApiKeyCredential("sk-xxxx"); // 配置客户端选项,指向DeepSeek的API端点 var openAIClientOption = new OpenAIClientOptions { Endpoint = new Uri("https://api.deepseek.com/v1"), }; // 创建OpenAI客户端实例 var openapiClient = new OpenAIClient(openAIClientCredential, openAIClientOption); // 注册DeepSeek聊天完成服务 // 第一个参数是服务标识符,第二个是客户端实例,第三个是服务描述 kernelBuilder.Services.AddOpenAIChatCompletion("deepseek-chat", openapiClient, "对话服务ID"); // 添加日志服务 kernelBuilder.Services.AddLogging(builder => builder.AddConsole()); // 构建内核 var kernel = kernelBuilder.Build(); // 将披萨订购插件添加到内核 var pizzaPlugin = new OrderPizzaPlugin( serviceProvider.GetRequiredService<IPizzaService>(), serviceProvider.GetRequiredService<IUserContext>(), serviceProvider.GetRequiredService<IPaymentService>() ); kernel.Plugins.AddFromObject(pizzaPlugin, "OrderPizza"); // 运行聊天对话示例 await RunChatExampleAsync(kernel); } static async Task RunChatExampleAsync(Kernel kernel) { Console.WriteLine("===== 披萨订购聊天机器人示例 ====="); Console.WriteLine("输入'exit'退出对话\n"); // 创建聊天历史 var chatHistory = new ChatHistory(); // 获取聊天完成服务 var chatCompletionService = kernel.GetRequiredService<IChatCompletionService>(); // 设置执行选项 - 启用函数调用 var executionSettings = new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(), Temperature = 0.7, TopP = 0.8 }; while (true) { // 获取用户输入 Console.Write("用户: "); string userInput = Console.ReadLine() ?? string.Empty; if (userInput.ToLower() == "exit") break; // 添加用户消息到聊天历史 chatHistory.AddUserMessage(userInput); try { // 获取AI回复 var response = await chatCompletionService.GetChatMessageContentAsync( chatHistory, executionSettings: executionSettings, kernel: kernel); // 输出AI回复 Console.WriteLine($"AI: {response.Content}"); // 添加AI回复到聊天历史 chatHistory.AddAssistantMessage(response.Content); } catch (Exception ex) { Console.WriteLine($"错误: {ex.Message}"); } } } } #region 模型定义 // 枚举定义 public enum PizzaSize { Small, Medium, Large } public enum PizzaToppings { Cheese, Pepperoni, Mushrooms, Olives, Onions, Bacon, ExtraCheese, GreenPeppers, Pineapple, Sausage } // 数据模型 public class Menu { public List<PizzaMenuItem> Pizzas { get; set; } = new(); public List<string> Sides { get; set; } = new(); public List<string> Drinks { get; set; } = new(); public override string ToString() { return $"菜单: {Pizzas.Count} 种披萨, {Sides.Count} 种配菜, {Drinks.Count} 种饮料"; } } public class PizzaMenuItem { public string Name { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public decimal Price { get; set; } public List<PizzaToppings> DefaultToppings { get; set; } = new(); } public class PizzaItem { public int Id { get; set; } public PizzaSize Size { get; set; } public List<PizzaToppings> Toppings { get; set; } = new(); public int Quantity { get; set; } public string SpecialInstructions { get; set; } = string.Empty; public decimal Price { get; set; } } public class Cart { public Guid Id { get; set; } public List<PizzaItem> Items { get; set; } = new(); public decimal Subtotal { get; set; } public decimal Tax { get; set; } public decimal Total { get; set; } } public class CartDelta { public List<PizzaItem> NewItems { get; set; } = new(); public Cart UpdatedCart { get; set; } = new(); public override string ToString() { return $"已添加 {NewItems.Count} 个商品,购物车总额: ${UpdatedCart.Total}"; } } public class CheckoutResponse { public string OrderId { get; set; } = string.Empty; public decimal Total { get; set; } public string EstimatedDeliveryTime { get; set; } = string.Empty; public override string ToString() { return $"订单 {OrderId} 已确认, 总价: ${Total}, 预计送达时间: {EstimatedDeliveryTime}"; } } #endregion #region 服务接口 // 服务接口 public interface IPizzaService { Task<Menu> GetMenu(); Task<CartDelta> AddPizzaToCart(Guid cartId, PizzaSize size, List<PizzaToppings> toppings, int quantity, string specialInstructions); Task<Cart> GetCart(Guid cartId); Task<Cart> RemoveItemFromCart(Guid cartId, int itemId); Task<CheckoutResponse> Checkout(Guid cartId, Guid paymentId); } public interface IUserContext { Task<Guid> GetCartIdAsync(); Guid GetCartId(); Task<string> GetUserAddressAsync(); } public interface IPaymentService { Task<Guid> RequestPaymentFromUserAsync(Guid cartId); Task<bool> VerifyPaymentAsync(Guid paymentId); } #endregion #region 模拟服务实现 // 模拟服务实现 public class MockPizzaService : IPizzaService { private readonly Dictionary<Guid, Cart> _carts = new(); private int _nextItemId = 1; public Task<Menu> GetMenu() { var menu = new Menu { Pizzas = new List<PizzaMenuItem> { new() { Name = "经典芝士披萨", Description = "传统意式芝士披萨", Price = 9.99m, DefaultToppings = new List<PizzaToppings> { PizzaToppings.Cheese } }, new() { Name = "意式辣香肠披萨", Description = "经典辣香肠披萨", Price = 11.99m, DefaultToppings = new List<PizzaToppings> { PizzaToppings.Cheese, PizzaToppings.Pepperoni } }, new() { Name = "蔬菜至尊披萨", Description = "各类新鲜蔬菜", Price = 12.99m, DefaultToppings = new List<PizzaToppings> { PizzaToppings.Cheese, PizzaToppings.Mushrooms, PizzaToppings.Olives, PizzaToppings.GreenPeppers, PizzaToppings.Onions } } }, Sides = new List<string> { "蒜香面包", "薯条", "鸡翅" }, Drinks = new List<string> { "可乐", "雪碧", "冰红茶", "矿泉水" } }; return Task.FromResult(menu); } public Task<CartDelta> AddPizzaToCart(Guid cartId, PizzaSize size, List<PizzaToppings> toppings, int quantity, string specialInstructions) { // 获取或创建购物车 if (!_carts.TryGetValue(cartId, out var cart)) { cart = new Cart { Id = cartId, Items = new List<PizzaItem>() }; _carts[cartId] = cart; } // 根据尺寸计算基础价格 decimal basePrice = size switch { PizzaSize.Small => 8.99m, PizzaSize.Medium => 10.99m, PizzaSize.Large => 13.99m, _ => 10.99m }; // 每种配料加0.75美元 decimal toppingsPrice = toppings.Count * 0.75m; // 创建新披萨项 var newItem = new PizzaItem { Id = _nextItemId++, Size = size, Toppings = toppings, Quantity = quantity, SpecialInstructions = specialInstructions, Price = (basePrice + toppingsPrice) * quantity }; // 添加到购物车 cart.Items.Add(newItem); // 计算小计、税和总计 cart.Subtotal = cart.Items.Sum(item => item.Price); cart.Tax = Math.Round(cart.Subtotal * 0.08m, 2); cart.Total = cart.Subtotal + cart.Tax; // 返回购物车增量 return Task.FromResult(new CartDelta { NewItems = new List<PizzaItem> { newItem }, UpdatedCart = cart }); } public Task<Cart> GetCart(Guid cartId) { if (!_carts.TryGetValue(cartId, out var cart)) { cart = new Cart { Id = cartId, Items = new List<PizzaItem>() }; _carts[cartId] = cart; } return Task.FromResult(cart); } public Task<Cart> RemoveItemFromCart(Guid cartId, int itemId) { if (!_carts.TryGetValue(cartId, out var cart)) { throw new Exception("购物车不存在"); } var itemToRemove = cart.Items.FirstOrDefault(item => item.Id == itemId); if (itemToRemove == null) { throw new Exception("商品不存在"); } cart.Items.Remove(itemToRemove); // 重新计算购物车总额 cart.Subtotal = cart.Items.Sum(item => item.Price); cart.Tax = Math.Round(cart.Subtotal * 0.08m, 2); cart.Total = cart.Subtotal + cart.Tax; return Task.FromResult(cart); } public Task<CheckoutResponse> Checkout(Guid cartId, Guid paymentId) { if (!_carts.TryGetValue(cartId, out var cart)) { throw new Exception("购物车不存在"); } // 生成订单ID string orderId = Guid.NewGuid().ToString()[..8].ToUpper(); // 模拟送达时间(30-45分钟) var now = DateTime.Now; var deliveryTime = now.AddMinutes(new Random().Next(30, 46)); var response = new CheckoutResponse { OrderId = orderId, Total = cart.Total, EstimatedDeliveryTime = deliveryTime.ToString("HH:mm") }; // 清空购物车 _carts.Remove(cartId); return Task.FromResult(response); } } public class MockUserContext : IUserContext { private readonly Guid _cartId = Guid.NewGuid(); public Task<Guid> GetCartIdAsync() { return Task.FromResult(_cartId); } public Guid GetCartId() { return _cartId; } public Task<string> GetUserAddressAsync() { return Task.FromResult("北京市朝阳区建国路88号"); } } public class MockPaymentService : IPaymentService { public Task<Guid> RequestPaymentFromUserAsync(Guid cartId) { // 模拟支付处理 return Task.FromResult(Guid.NewGuid()); } public Task<bool> VerifyPaymentAsync(Guid paymentId) { // 假设所有付款都通过验证 return Task.FromResult(true); } } #endregion #region 披萨订购插件 // 披萨订购插件 public class OrderPizzaPlugin { private readonly IPizzaService _pizzaService; private readonly IUserContext _userContext; private readonly IPaymentService _paymentService; public OrderPizzaPlugin( IPizzaService pizzaService, IUserContext userContext, IPaymentService paymentService) { _pizzaService = pizzaService; _userContext = userContext; _paymentService = paymentService; } [KernelFunction("get_pizza_menu")] [Description("获取披萨菜单,包括披萨种类、配菜和饮料")] public async Task<Menu> GetPizzaMenuAsync() { return await _pizzaService.GetMenu(); } [KernelFunction("add_pizza_to_cart")] [Description("添加披萨到用户购物车,返回新添加的商品和更新后的购物车")] public async Task<CartDelta> AddPizzaToCart( [Description("披萨尺寸:小号、中号或大号")] PizzaSize size, [Description("披萨配料列表,可选:芝士、意式辣香肠、蘑菇、橄榄、洋葱、培根、额外芝士、青椒、菠萝、香肠")] List<PizzaToppings> toppings, [Description("披萨数量,默认为1")] int quantity = 1, [Description("特殊制作说明,例如'少放盐',默认为空")] string specialInstructions = "") { try { Guid cartId = _userContext.GetCartId(); return await _pizzaService.AddPizzaToCart( cartId: cartId, size: size, toppings: toppings, quantity: quantity, specialInstructions: specialInstructions); } catch (Exception ex) { throw new Exception($"添加披萨失败: {ex.Message}"); } } [KernelFunction("get_cart")] [Description("获取用户当前购物车内容")] public async Task<Cart> GetCart() { try { Guid cartId = _userContext.GetCartId(); return await _pizzaService.GetCart(cartId); } catch (Exception ex) { throw new Exception($"获取购物车失败: {ex.Message}"); } } [KernelFunction("remove_item_from_cart")] [Description("从购物车中移除指定商品")] public async Task<Cart> RemoveItemFromCart( [Description("要移除的商品ID")] int itemId) { try { Guid cartId = _userContext.GetCartId(); return await _pizzaService.RemoveItemFromCart(cartId, itemId); } catch (Exception ex) { throw new Exception($"移除商品失败: {ex.Message}"); } } [KernelFunction("checkout")] [Description("结算用户购物车,处理付款并完成订单")] public async Task<CheckoutResponse> Checkout() { try { Guid cartId = await _userContext.GetCartIdAsync(); Guid paymentId = await _paymentService.RequestPaymentFromUserAsync(cartId); // 确认付款成功 bool paymentVerified = await _paymentService.VerifyPaymentAsync(paymentId); if (!paymentVerified) { throw new Exception("付款验证失败"); } return await _pizzaService.Checkout(cartId, paymentId); } catch (Exception ex) { throw new Exception($"结算失败: {ex.Message}"); } } [KernelFunction("get_delivery_address")] [Description("获取用户的送货地址")] public async Task<string> GetDeliveryAddress() { try { return await _userContext.GetUserAddressAsync(); } catch (Exception ex) { throw new Exception($"获取送货地址失败: {ex.Message}"); } } } #endregion }

image.png

结论

函数调用为C#开发者提供了构建强大、交互式AI应用的能力。通过Semantic Kernel,您可以轻松集成现有代码与大型语言模型。掌握本文介绍的最佳实践,将帮助您开发出反应灵敏、用户友好的聊天机器人,能够处理从订购披萨到执行复杂业务逻辑的各种任务。

通过合理设计函数、优化参数和返回值,以及实现并行函数调用,您可以创建出更高效、更智能的交互式应用程序。

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!