Semantic Kernel 过滤器中间件-详解
一、过滤器类型(SK 1.78.0)
| 过滤器类型 | 接口 | 作用时机 |
|---|---|---|
| 函数调用过滤器 | IFunctionInvocationFilter |
函数执行前后 |
| Prompt 渲染过滤器 | IPromptRenderFilter |
Prompt 渲染后,调用 LLM 前 |
| 自动函数调用过滤器 | IAutoFunctionInvocationFilter |
自动函数调用流程中 |
过滤器(Filter)是 Semantic Kernel 的中间件机制,允许在函数调用的前后执行自定义逻辑,用于日志记录、监控、安全控制等。
项目引用
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <NoWarn>$(NoWarn);NU5104</NoWarn> </PropertyGroup> <ItemGroup> <Compile Remove="Registry\**" /> <EmbeddedResource Remove="Registry\**" /> <None Remove="Registry\**" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.SemanticKernel" Version="1.78.0" /> <PackageReference Include="Microsoft.SemanticKernel.Connectors.Sqlite" Version="1.51.0-preview" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.78.0-preview" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.78.0-alpha" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.OpenApi" Version="1.78.0" /> <PackageReference Include="Microsoft.SemanticKernel.PromptTemplates.Handlebars" Version="1.78.0" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.Web" Version="1.78.0-alpha" /> <PackageReference Include="Qdrant.Client" Version="1.18.1" /> <PackageReference Include="Sdcb.DashScope" Version="2.0.0" /> <!-- ✅ 新增:限流需要 --> <PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" /> <!-- ✅ 新增:重试需要 --> <PackageReference Include="Polly" Version="8.4.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Services\Services.csproj" /> </ItemGroup> </Project>
1、IFunctionInvocationFilter、Token认证过滤器
token过滤器
/// <summary> /// 认证过滤器 - 简化版(不依赖 IUserContext) /// </summary> public class AuthenticationFilter : IFunctionInvocationFilter { private readonly Func<string, Task<bool>> _validateToken; private readonly Dictionary<string, string[]> _pluginPermissions; /// <summary> /// 创建认证过滤器 /// </summary> /// <param name="validateToken">验证 Token 的委托</param> /// <param name="pluginPermissions">插件权限映射(插件名 -> 允许的角色列表)</param> public AuthenticationFilter( Func<string, Task<bool>> validateToken, Dictionary<string, string[]>? pluginPermissions = null) { _validateToken = validateToken; _pluginPermissions = pluginPermissions ?? new(); } /// <summary> /// 在函数执行前调用 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> /// <exception cref="UnauthorizedAccessException"></exception> public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { // 1️⃣ 从上下文获取 Token var token = GetTokenFromContext(context); // 2️⃣ 验证 Token if (string.IsNullOrEmpty(token) || !await _validateToken(token)) { throw new UnauthorizedAccessException( $"❌ 未授权的函数调用:{context.Function.PluginName}.{context.Function.Name}"); } // 3️⃣ 检查插件权限(如果配置了) if (_pluginPermissions.Count > 0) { var userRole = GetUserRoleFromToken(token); if (_pluginPermissions.TryGetValue(context.Function.PluginName, out var requiredRoles)) { if (!requiredRoles.Contains(userRole)) { throw new UnauthorizedAccessException( $"❌ 用户角色 [{userRole}] 无权限访问插件 [{context.Function.PluginName}]"); } } } Console.WriteLine($"🔐 认证通过:{context.Function.PluginName}.{context.Function.Name}"); // 4️⃣ 继续执行 await next(context); // 5️⃣ 执行后:记录审计日志 Console.WriteLine($"✅ 函数完成:{context.Function.PluginName}.{context.Function.Name}"); } private string GetTokenFromContext(FunctionInvocationContext context) { // 从 Kernel.Data 获取 Token if (context.Kernel.Data.TryGetValue("auth_token", out var token)) { return token?.ToString() ?? ""; } return ""; } private string GetUserRoleFromToken(string token) { // 简化:从 Token 中提取角色 // 实际项目应该解析 JWT 或调用用户服务 if (token.Contains("admin")) return "admin"; if (token.Contains("user")) return "user"; return "guest"; } }
入口
using ConsoleApp1; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Newtonsoft.Json; using Polly; using Polly.Retry; using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Threading.RateLimiting; namespace ConsoleApp4 { class Program { static async Task Main(string[] args) { Console.WriteLine("╔════════════════════════════════════════╗"); Console.WriteLine("║ SK 认证过滤器使用示例 ║"); Console.WriteLine("╚════════════════════════════════════════╝\n"); // 1️⃣ 创建 Kernel var kernel = CreateKernelWithAuth(); // 2️⃣ 注册插件 var weatherPlugin = new WeatherPlugin(); var weatherPluginName = "Weather"; var plugin = KernelPluginFactory.CreateFromObject(weatherPlugin, weatherPluginName); kernel.Plugins.Add(plugin); var weatherHistoryPlugin = new WeatherHistoryPlugin(); var WeatherHistoryPluginName = "WeatherHistory"; var HistoryPlugin = KernelPluginFactory.CreateFromObject(weatherHistoryPlugin, WeatherHistoryPluginName); kernel.Plugins.Add(HistoryPlugin); // ========== 场景 1: 有效 Token ========== Console.WriteLine("═══════════════════════════════════════"); Console.WriteLine("场景 1: 使用有效 Token,角色:user"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_user"; try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ user尝试访问GetWeather,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ user尝试访问GetWeather,认证失败:{ex.Message}"); } // try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeatherByDate", arguments: new KernelArguments { ["city"] = "苏州", ["dt"]=DateTime.Now.AddDays(-3).ToString("yyyy-MM-dd") }); Console.WriteLine($"\n✅ user尝试访问GetWeatherByDate,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ user尝试访问GetWeatherByDate,认证失败:{ex.Message}"); } //尝试访问Admin专属的插件 try { var result = await kernel.InvokeAsync( pluginName: WeatherHistoryPluginName, functionName: "GetWeatherHistory", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ user尝试访问GetWeatherHistory,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ user尝试访问GetWeatherHistory,认证失败:{ex.Message}"); } // ========== 场景 2: 使用有效 Token,角色:guest ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 2: 使用有效 Token,角色:guest"); Console.WriteLine("═══════════════════════════════════════\n"); ///guest 尝试访问 GetWeather kernel.Data["auth_token"] = "valid_token_guest"; try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ guest 尝试访问 GetWeather:结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ guest 尝试访问 GetWeather:认证失败:{ex.Message}"); } // ========== 场景 3: 无 Token 无效token ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 3: 不提供 Token"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data.Remove("auth_token"); try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ 不提供 Token,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ 不提供 Token,认证失败:{ex.Message}"); } } static Kernel CreateKernelWithAuth() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: ConstParm.modelId, apiKey: ConstParm.apiKey, endpoint: new Uri(ConstParm.endpoint)); // 配置日志 builder.Services.AddLogging(b => b .AddConsole() .SetMinimumLevel(LogLevel.Information)); var kernel = builder.Build(); // ========== 注册认证过滤器 ========== // 模拟 Token 验证服务 Func<string, Task<bool>> validateToken = async (token) => { // 模拟网络请求验证 Token await Task.Delay(10); // 简单规则:包含 "valid" 的 Token 有效 return token.Contains("valid"); }; // 配置插件权限(可选) var pluginPermissions = new Dictionary<string, string[]>(); //Weather插件可以被admin 和 user 两种角色访问 pluginPermissions.Add("Weather", new[] { "admin", "user" }); //WeatherHistory插件只能被 admin 角色访问 pluginPermissions.Add("WeatherHistory", new[] { "admin" }); kernel.FunctionInvocationFilters.Add(new AuthenticationFilter( validateToken, pluginPermissions)); return kernel; } } /// <summary> /// /// </summary> public class WeatherPlugin { [KernelFunction("GetWeather"), Description("获取指定城市当天的天气预报")] public async Task<string> GetWeather([Description("城市名称")] string city) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject( new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt = DateTime.Now }); } [KernelFunction("GetWeatherByDate"), Description("获取指定日期及城市的天气预报")] public async Task<string> GetWeatherByDate([Description("城市名称")] string city,DateTime dt) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject(new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt =dt }); } } public class WeatherHistoryPlugin { [KernelFunction("GetWeatherHistory"), Description("获取指定城市近一周的天气情况(管理员专用)")] public async Task<string> GetWeatherHistory([Description("城市名称")] string cityName) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject(new List<string>() { "8.1日晴", "8.2日晴", "8.3日晴", "8.4日晴", "8.5日晴", "8.6日晴", "8.7日阴" }); } } }
2、IFunctionInvocationFilter、日志过滤器
日志过滤器
// ============================================ // ✅ 日志过滤器(彩色控制台输出) // ============================================ public class LoggingFilter : IFunctionInvocationFilter { private readonly ILogger<LoggingFilter> _logger; private readonly Stopwatch _stopwatch; public LoggingFilter(ILogger<LoggingFilter> logger) { _logger = logger; _stopwatch = new Stopwatch(); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { var functionName = $"{context.Function.PluginName}.{context.Function.Name}"; var arguments = string.Join(", ", context.Arguments.Select(kvp => $"{kvp.Key}={kvp.Value}")); // 📝 开始执行函数 - 彩色输出 _logger.LogInformation("📝 开始执行函数:{FunctionName}({Arguments})", functionName, arguments); Console.ForegroundColor = ConsoleColor.Cyan; Console.Write($"📝 开始执行函数:{functionName}("); Console.ForegroundColor = ConsoleColor.White; Console.Write($"{arguments}"); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(")"); Console.ResetColor(); _stopwatch.Restart(); try { // 执行函数 await next(context); _stopwatch.Stop(); var result = context.Result?.GetValue<object>()?.ToString() ?? "null"; // ✅ 函数执行成功 - 彩色输出 _logger.LogInformation( "✅ 函数执行成功:{FunctionName},耗时 {ElapsedMs}ms,结果:{Result}", functionName, _stopwatch.ElapsedMilliseconds, Truncate(result, 100)); Console.ForegroundColor = ConsoleColor.Green; Console.Write($"✅ 函数执行成功:{functionName},"); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.Write($"耗时 {_stopwatch.ElapsedMilliseconds}ms,"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine($"结果:{Truncate(result, 100)}"); Console.ResetColor(); } catch (Exception ex) { _stopwatch.Stop(); // ❌ 函数执行失败 - 彩色输出 _logger.LogError( ex, "❌ 函数执行失败:{FunctionName},耗时 {ElapsedMs}ms,错误:{Error}", functionName, _stopwatch.ElapsedMilliseconds, ex.Message); Console.ForegroundColor = ConsoleColor.Red; Console.Write($"❌ 函数执行失败:{functionName},"); Console.ForegroundColor = ConsoleColor.DarkRed; Console.Write($"耗时 {_stopwatch.ElapsedMilliseconds}ms,"); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"错误:{ex.Message}"); Console.ResetColor(); throw; } } private string Truncate(string text, int maxLength) { if (string.IsNullOrEmpty(text) || text.Length <= maxLength) return text; return text[..maxLength] + "..."; } }
入口处和Token入口一致,只需增加注册日志过滤器

using ConsoleApp1; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Newtonsoft.Json; using Polly; using Polly.Retry; using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Threading.RateLimiting; namespace ConsoleApp4 { class Program { static async Task Main(string[] args) { Console.WriteLine("╔════════════════════════════════════════╗"); Console.WriteLine("║ SK 认证过滤器使用示例 ║"); Console.WriteLine("╚════════════════════════════════════════╝\n"); // 1️⃣ 创建 Kernel var kernel = CreateKernelWithAuth(); // 2️⃣ 注册插件 var weatherPlugin = new WeatherPlugin(); var weatherPluginName = "Weather"; var plugin = KernelPluginFactory.CreateFromObject(weatherPlugin, weatherPluginName); kernel.Plugins.Add(plugin); var weatherHistoryPlugin = new WeatherHistoryPlugin(); var WeatherHistoryPluginName = "WeatherHistory"; var HistoryPlugin = KernelPluginFactory.CreateFromObject(weatherHistoryPlugin, WeatherHistoryPluginName); kernel.Plugins.Add(HistoryPlugin); // ========== 场景 1: 有效 Token ========== Console.WriteLine("═══════════════════════════════════════"); Console.WriteLine("场景 1: 使用有效 Token,角色:user"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_user"; try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ user尝试访问GetWeather,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ user尝试访问GetWeather,认证失败:{ex.Message}"); } // try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeatherByDate", arguments: new KernelArguments { ["city"] = "苏州", ["dt"]=DateTime.Now.AddDays(-3).ToString("yyyy-MM-dd") }); Console.WriteLine($"\n✅ user尝试访问GetWeatherByDate,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ user尝试访问GetWeatherByDate,认证失败:{ex.Message}"); } //尝试访问Admin专属的插件 try { var result = await kernel.InvokeAsync( pluginName: WeatherHistoryPluginName, functionName: "GetWeatherHistory", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ user尝试访问GetWeatherHistory,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ user尝试访问GetWeatherHistory,认证失败:{ex.Message}"); } // ========== 场景 2: 使用有效 Token,角色:guest ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 2: 使用有效 Token,角色:guest"); Console.WriteLine("═══════════════════════════════════════\n"); ///guest 尝试访问 GetWeather kernel.Data["auth_token"] = "valid_token_guest"; try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ guest 尝试访问 GetWeather:结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ guest 尝试访问 GetWeather:认证失败:{ex.Message}"); } // ========== 场景 3: 无 Token 无效token ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 3: 不提供 Token"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data.Remove("auth_token"); try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ 不提供 Token,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"\n❌ 不提供 Token,认证失败:{ex.Message}"); } } static Kernel CreateKernelWithAuth() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: ConstParm.modelId, apiKey: ConstParm.apiKey, endpoint: new Uri(ConstParm.endpoint)); // 配置日志 builder.Services.AddLogging(b => b .AddConsole() .SetMinimumLevel(LogLevel.Information)); var kernel = builder.Build(); // ========== 注册认证过滤器 ========== // 模拟 Token 验证服务 Func<string, Task<bool>> validateToken = async (token) => { // 模拟网络请求验证 Token await Task.Delay(10); // 简单规则:包含 "valid" 的 Token 有效 return token.Contains("valid"); }; // 配置插件权限(可选) var pluginPermissions = new Dictionary<string, string[]>(); //Weather插件可以被admin 和 user 两种角色访问 pluginPermissions.Add("Weather", new[] { "admin", "user" }); //WeatherHistory插件只能被 admin 角色访问 pluginPermissions.Add("WeatherHistory", new[] { "admin" }); // ========== 注册TOKEN过滤器 ========== kernel.FunctionInvocationFilters.Add(new AuthenticationFilter( validateToken, pluginPermissions)); // ========== 注册日志过滤器 ========== var logger = kernel.Services.GetRequiredService<ILogger<LoggingFilter>>(); kernel.FunctionInvocationFilters.Add(new LoggingFilter(logger)); return kernel; } } /// <summary> /// /// </summary> public class WeatherPlugin { [KernelFunction("GetWeather"), Description("获取指定城市当天的天气预报")] public async Task<string> GetWeather([Description("城市名称")] string city) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject( new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt = DateTime.Now }); } [KernelFunction("GetWeatherByDate"), Description("获取指定日期及城市的天气预报")] public async Task<string> GetWeatherByDate([Description("城市名称")] string city,DateTime dt) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject(new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt =dt }); } } public class WeatherHistoryPlugin { [KernelFunction("GetWeatherHistory"), Description("获取指定城市近一周的天气情况(管理员专用)")] public async Task<string> GetWeatherHistory([Description("城市名称")] string cityName) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject(new List<string>() { "8.1日晴", "8.2日晴", "8.3日晴", "8.4日晴", "8.5日晴", "8.6日晴", "8.7日阴" }); } } }
执行结果彩色部分为日志过滤器生成

3、IFunctionInvocationFilter、限流过滤器
接合上述Token过滤器和日志过滤器,现在增加限流过滤器,执行过程如下:
用户调用 kernel.InvokeAsync() ↓ ┌─────────────────────────────────────────┐ │ 1. AuthenticationFilter (认证) │ ← 验证 Token 和权限 └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 2. LoggingFilter (日志 - 前) │ ← 记录开始时间 └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 3. RateLimitingFilter (限流) │ ← 检查频率限制 └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 4. 实际函数执行 │ └─────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────┐ │ 5. LoggingFilter (日志 - 后) │ ← 记录结果和耗时 └─────────────────────────────────────────┘ ↓ 返回结果
注册限流过滤器

// ========== 注册TOKEN过滤器 ========== kernel.FunctionInvocationFilters.Add(new AuthenticationFilter( validateToken, pluginPermissions)); // ========== 注册日志过滤器 ========== var logger = kernel.Services.GetRequiredService<ILogger<LoggingFilter>>(); kernel.FunctionInvocationFilters.Add(new LoggingFilter(logger)); // ========== 注册限流过滤器 ========== var rateLimiter = new RateLimitingFilter(requestsPerMinute: 3); // 全局限流:每分钟 3 次 // 按插件限流(可选) rateLimiter.AddPluginLimit("Weather", 4); // Weather 插件:每分钟 4 次 、WeatherHistory 插件:默认为全局:每分钟 3 次 kernel.FunctionInvocationFilters.Add(rateLimiter);
限流过滤器
// ============================================ // ✅ 限流过滤器 // ============================================ public class RateLimitingFilter : IFunctionInvocationFilter { private readonly RateLimiter _rateLimiter; private readonly Dictionary<string, RateLimiter> _pluginLimiters; /// <summary> /// 全局限流 /// </summary> public RateLimitingFilter(int requestsPerMinute = 60) { _rateLimiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions { PermitLimit = requestsPerMinute, Window = TimeSpan.FromMinutes(1), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 0 // ⚠️ 关键:队列限制设为 0,不等待 }); _pluginLimiters = new(); } /// <summary> /// 按插件限流 /// </summary> public void AddPluginLimit(string pluginName, int requestsPerMinute) { _pluginLimiters[pluginName] = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions { PermitLimit = requestsPerMinute, Window = TimeSpan.FromMinutes(1), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 0 // ⚠️ 关键:队列限制设为 0,不等待 }); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { // 选择限流器(插件级别优先) var limiter = _pluginLimiters.TryGetValue(context.Function.PluginName, out var pluginLimiter) ? pluginLimiter : _rateLimiter; // ✅ 修复:使用 AttemptAcquire 立即返回,不等待 using var lease = limiter.AttemptAcquire(1); if (!lease.IsAcquired) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"🚫 触发限流:{context.Function.PluginName}.{context.Function.Name}"); Console.ResetColor(); throw new RateLimitExceededException( $"🚫 函数调用频率超限:{context.Function.PluginName}.{context.Function.Name}"); } Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine($"🚦 限流通过:{context.Function.Name}"); Console.ResetColor(); await next(context); } } /// <summary> /// 限流异常 /// </summary> public class RateLimitExceededException : Exception { public RateLimitExceededException(string message) : base(message) { } }
程序入口
using ConsoleApp1; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Newtonsoft.Json; using Polly; using Polly.Retry; using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Threading.RateLimiting; namespace ConsoleApp4 { class Program { static async Task Main(string[] args) { Console.WriteLine("╔════════════════════════════════════════╗"); Console.WriteLine("║ SK 认证过滤器使用示例 ║"); Console.WriteLine("╚════════════════════════════════════════╝\n"); // 1️⃣ 创建 Kernel var kernel = CreateKernelWithAuth(); // 2️⃣ 注册插件 var weatherPlugin = new WeatherPlugin(); var weatherPluginName = "Weather"; var plugin = KernelPluginFactory.CreateFromObject(weatherPlugin, weatherPluginName); kernel.Plugins.Add(plugin); var weatherHistoryPlugin = new WeatherHistoryPlugin(); var WeatherHistoryPluginName = "WeatherHistory"; var HistoryPlugin = KernelPluginFactory.CreateFromObject(weatherHistoryPlugin, WeatherHistoryPluginName); kernel.Plugins.Add(HistoryPlugin); // ========== 场景 1: 有效 Token ==========循环调用 6 次,第 6 次会触发限流============== Console.WriteLine("═══════════════════════════════════════"); Console.WriteLine("场景 1: 使用有效 Token,角色:user"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_user"; for (int i = 1; i <= 6; i++) { Console.WriteLine($"\n--- 第 {i} 次调用 ---\n"); try { var result = await kernel.InvokeAsync( pluginName: weatherPluginName, functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"\n✅ user尝试访问GetWeather,结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"❌ 第 {i} 次调用,认证失败:{ex.Message}"); } catch (RateLimitExceededException ex) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"🚫 第 {i} 次调用,触发限流:{ex.Message}"); Console.ResetColor(); } catch (Exception ex) { Console.WriteLine($"❌ 第 {i} 次调用,其他错误:{ex.Message}"); } } } static Kernel CreateKernelWithAuth() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: ConstParm.modelId, apiKey: ConstParm.apiKey, endpoint: new Uri(ConstParm.endpoint)); // 配置日志 builder.Services.AddLogging(b => b .AddConsole() .SetMinimumLevel(LogLevel.Information)); var kernel = builder.Build(); // ========== 注册认证过滤器 ========== // 模拟 Token 验证服务 Func<string, Task<bool>> validateToken = async (token) => { // 模拟网络请求验证 Token await Task.Delay(10); // 简单规则:包含 "valid" 的 Token 有效 return token.Contains("valid"); }; // 配置插件权限(可选) var pluginPermissions = new Dictionary<string, string[]>(); //Weather插件可以被admin 和 user 两种角色访问 pluginPermissions.Add("Weather", new[] { "admin", "user" }); //WeatherHistory插件只能被 admin 角色访问 pluginPermissions.Add("WeatherHistory", new[] { "admin" }); // ========== 注册TOKEN过滤器 ========== kernel.FunctionInvocationFilters.Add(new AuthenticationFilter( validateToken, pluginPermissions)); // ========== 注册日志过滤器 ========== var logger = kernel.Services.GetRequiredService<ILogger<LoggingFilter>>(); kernel.FunctionInvocationFilters.Add(new LoggingFilter(logger)); // ========== 注册限流过滤器 ========== var rateLimiter = new RateLimitingFilter(requestsPerMinute: 3); // 全局限流:每分钟 3 次 // 按插件限流(可选) rateLimiter.AddPluginLimit("Weather", 4); // Weather 插件:每分钟 4 次 、WeatherHistory 插件:默认为全局:每分钟 3 次 kernel.FunctionInvocationFilters.Add(rateLimiter); return kernel; } } /// <summary> /// /// </summary> public class WeatherPlugin { [KernelFunction("GetWeather"), Description("获取指定城市当天的天气预报")] public async Task<string> GetWeather([Description("城市名称")] string city) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject( new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt = DateTime.Now }); } [KernelFunction("GetWeatherByDate"), Description("获取指定日期及城市的天气预报")] public async Task<string> GetWeatherByDate([Description("城市名称")] string city,DateTime dt) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject(new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt =dt }); } } public class WeatherHistoryPlugin { [KernelFunction("GetWeatherHistory"), Description("获取指定城市近一周的天气情况(管理员专用)")] public async Task<string> GetWeatherHistory([Description("城市名称")] string cityName) { await Task.Delay(10);//模拟查询天气预报 return JsonConvert.SerializeObject(new List<string>() { "8.1日晴", "8.2日晴", "8.3日晴", "8.4日晴", "8.5日晴", "8.6日晴", "8.7日阴" }); } } }

执行结果

4、自动函数调用监控过滤器、自动函数调用限制过滤器、Prompt 渲染过滤器(敏感词过滤)、总项目代码
自动函数调用监控过滤器:目的是为了监控LLM调用函数的全过程,形成日志
自动函数调用限制过滤器:目的是防止LLM大量调用函数,或者一些诸如删除数据的敏感函数!
Prompt 渲染过滤器:目的是将密码,AI的ApiKey等敏感信息加以处理,防止机密信息泄露
4.1、项目引用
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <NoWarn>$(NoWarn);NU5104</NoWarn> </PropertyGroup> <ItemGroup> <Compile Remove="Registry\**" /> <EmbeddedResource Remove="Registry\**" /> <None Remove="Registry\**" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.10" /> <PackageReference Include="Microsoft.SemanticKernel" Version="1.78.0" /> <PackageReference Include="Microsoft.SemanticKernel.Connectors.Sqlite" Version="1.51.0-preview" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.Core" Version="1.78.0-preview" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.78.0-alpha" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.OpenApi" Version="1.78.0" /> <PackageReference Include="Microsoft.SemanticKernel.PromptTemplates.Handlebars" Version="1.78.0" /> <PackageReference Include="Microsoft.SemanticKernel.Plugins.Web" Version="1.78.0-alpha" /> <PackageReference Include="Qdrant.Client" Version="1.18.1" /> <PackageReference Include="Sdcb.DashScope" Version="2.0.0" /> <!-- ✅ 新增:限流需要 --> <PackageReference Include="System.Threading.RateLimiting" Version="8.0.0" /> <!-- ✅ 新增:重试需要 --> <PackageReference Include="Polly" Version="8.4.0" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Services\Services.csproj" /> </ItemGroup> </Project>
4.2、main入口
using ConsoleApp1; using Grpc.Core; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using Newtonsoft.Json; using Polly; using Polly.Retry; using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Threading.RateLimiting; namespace ConsoleApp4 { class Program { static async Task Main(string[] args) { Console.WriteLine("╔════════════════════════════════════════╗"); Console.WriteLine("║ SK 8 个过滤器完整演示 ║"); Console.WriteLine("╚════════════════════════════════════════╝\n"); // 1️⃣ 创建 Kernel(包含所有 8 个过滤器) var kernel = CreateKernelWithAuth(); // 2️⃣ 注册插件 var weatherPlugin = new WeatherPlugin(); kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(weatherPlugin, pluginName: "Weather")); var emailPlugin = new EmailPlugin(); kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(emailPlugin, pluginName: "Email")); var adminPlugin = new AdminPlugin(); kernel.Plugins.Add(KernelPluginFactory.CreateFromObject(adminPlugin, pluginName: "Admin")); // ========== 场景 1: 认证过滤器 - 无 Token ========== Console.WriteLine("═══════════════════════════════════════"); Console.WriteLine("场景 1: 认证过滤器 - 无 Token"); Console.WriteLine("═══════════════════════════════════════\n"); try { var result = await kernel.InvokeAsync( pluginName: "Weather", functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"✅ 结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"❌ 认证失败:{ex.Message}"); } // ========== 场景 2: 认证过滤器 - 有效 Token ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 2: 认证过滤器 - 有效 Token"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_user"; try { var result = await kernel.InvokeAsync( pluginName: "Weather", functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"✅ 结果:{result.GetValue<string>()}"); } catch (Exception ex) { Console.WriteLine($"❌ 失败:{ex.Message}"); } // ========== 场景 3: 缓存过滤器 - 相同参数 ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 3: 缓存过滤器 - 相同参数(缓存命中)"); Console.WriteLine("═══════════════════════════════════════\n"); try { var result = await kernel.InvokeAsync( pluginName: "Weather", functionName: "GetWeather", arguments: new KernelArguments { ["city"] = "苏州" }); Console.WriteLine($"✅ 结果:{result.GetValue<string>()}"); } catch (Exception ex) { Console.WriteLine($"❌ 失败:{ex.Message}"); } // ========== 场景 4: Prompt 渲染过滤器 - 敏感词 ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 4: Prompt 渲染过滤器 - 敏感词过滤"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_admin"; var settings = new Microsoft.SemanticKernel.Connectors.OpenAI.OpenAIPromptExecutionSettings { FunctionChoiceBehavior = Microsoft.SemanticKernel.FunctionChoiceBehavior.Auto(), Temperature = 0.7, MaxTokens = 1000 }; try { Console.WriteLine("🤖 用户:我的密码是 123456,请保存\n"); Console.Write("🤖 AI: "); await foreach (var chunk in kernel.InvokePromptStreamingAsync( "我的密码是 123456,请保存", new(settings))) { Console.Write(chunk.ToString()); } Console.WriteLine(); } catch (Exception ex) { Console.WriteLine($"❌ 错误:{ex.Message}"); } // ========== 场景 5: 限流 + 重试过滤器 - 循环调用 ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 5: 限流 + 重试过滤器 - 循环调用 6 次"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_user"; for (int i = 1; i <= 6; i++) { Console.WriteLine($"\n--- 第 {i} 次调用 ---\n"); try { var result = await kernel.InvokeAsync( pluginName: "Weather", functionName: "GetWeather", arguments: new KernelArguments { ["city"] = $"城市{i}" }); Console.WriteLine($"✅ 第 {i} 次调用成功"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"❌ 认证失败:{ex.Message}"); } catch (Exception ex) { Console.WriteLine($"❌ 失败:{ex.Message}"); } } // ========== 场景 6: 自动函数调用监控 + 限制过滤器 ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 6: 自动函数调用监控 + 限制过滤器"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_admin"; try { Console.WriteLine("🤖 用户:请查询苏州、北京、上海、广州、深圳、杭州、南京的天气\n"); Console.Write("🤖 AI: "); await foreach (var chunk in kernel.InvokePromptStreamingAsync( "请查询苏州、北京、上海、广州、深圳、杭州、南京的天气", new(settings))) { Console.Write(chunk.ToString()); } Console.WriteLine(); } catch (Exception ex) { Console.WriteLine($"❌ 错误:{ex.Message}"); } // ========== 场景 7: 权限控制 - 用户访问管理员插件 ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 7: 认证过滤器 - 权限控制"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_user"; try { var result = await kernel.InvokeAsync( pluginName: "Admin", functionName: "GetStats", arguments: new KernelArguments()); Console.WriteLine($"✅ 结果:{result.GetValue<string>()}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"❌ 权限不足:{ex.Message}"); } // ========== 场景 8: 自动调用黑名单函数 ========== Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("场景 8: 自动函数调用限制 - 黑名单函数"); Console.WriteLine("═══════════════════════════════════════\n"); kernel.Data["auth_token"] = "valid_token_admin"; try { Console.WriteLine("🤖 用户:请删除所有数据\n"); Console.Write("🤖 AI: "); await foreach (var chunk in kernel.InvokePromptStreamingAsync( "请删除所有数据", new(settings))) { Console.Write(chunk.ToString()); } Console.WriteLine(); } catch (Exception ex) { Console.WriteLine($"❌ 错误:{ex.Message}"); } Console.WriteLine("\n═══════════════════════════════════════"); Console.WriteLine("✅ 所有场景演示完成!"); Console.WriteLine("═══════════════════════════════════════\n"); } static Kernel CreateKernelWithAuth() { var builder = Kernel.CreateBuilder(); builder.AddOpenAIChatCompletion( modelId: ConstParm.modelId, apiKey: ConstParm.apiKey, endpoint: new Uri(ConstParm.endpoint)); builder.Services.AddLogging(b => b .AddConsole() .SetMinimumLevel(LogLevel.Information)); builder.Services.AddMemoryCache(); var kernel = builder.Build(); // ========== 1️⃣ 认证过滤器 ========== Func<string, Task<bool>> validateToken = async (token) => { await Task.Delay(10); return token.Contains("valid"); }; var pluginPermissions = new Dictionary<string, string[]> { ["Weather"] = new[] { "admin", "user" }, ["Email"] = new[] { "admin", "user" }, ["Admin"] = new[] { "admin" } }; kernel.FunctionInvocationFilters.Add(new AuthenticationFilter( validateToken, pluginPermissions)); // ========== 2️⃣ 日志过滤器 ========== var logger = kernel.Services.GetRequiredService<ILogger<LoggingFilter>>(); kernel.FunctionInvocationFilters.Add(new LoggingFilter(logger)); // ========== 3️⃣ Prompt 渲染过滤器 ========== var sensitiveWords = new List<string> { "密码", "password", "Token", "token", "sk-", "密钥", "secret" }; kernel.PromptRenderFilters.Add(new PromptSanitizerFilter(sensitiveWords)); // ========== 4️⃣ 缓存过滤器 ========== var memoryCache = kernel.Services.GetRequiredService<IMemoryCache>(); kernel.FunctionInvocationFilters.Add(new CacheFilter(memoryCache, TimeSpan.FromMinutes(5))); // ========== 5️⃣ 重试过滤器 ========== kernel.FunctionInvocationFilters.Add(new RetryFilter( maxRetries: 3, delay: TimeSpan.FromSeconds(1))); // ========== 6️⃣ 限流过滤器 ========== var rateLimiter = new RateLimitingFilter(requestsPerMinute: 5); rateLimiter.AddPluginLimit("Weather", 5); kernel.FunctionInvocationFilters.Add(rateLimiter); // ========== 7️⃣ 自动函数调用监控过滤器 ========== kernel.AutoFunctionInvocationFilters.Add(new AutoFunctionMonitoringFilter()); // ========== 8️⃣ 自动函数调用限制过滤器 ========== kernel.AutoFunctionInvocationFilters.Add(new AutoFunctionCallLimitFilter( maxCallsPerConversation: 5, blockedFunctions: new[] { "delete", "remove", "clear" } )); return kernel; } } // ============================================ // ✅ 插件类 // ============================================ public class WeatherPlugin { [KernelFunction("GetWeather"), Description("获取指定城市当天的天气预报")] public async Task<string> GetWeather([Description("城市名称")] string city) { await Task.Delay(10); return JsonConvert.SerializeObject(new { City = city, Temperature = 25.5, Condition = "晴", UpdatedAt = DateTime.Now }); } } public class EmailPlugin { [KernelFunction("SendEmail"), Description("发送邮件到指定邮箱")] public async Task<string> SendEmail( [Description("收件人邮箱")] string to, [Description("邮件主题")] string subject, [Description("邮件内容")] string body) { await Task.Delay(10); return $"✅ 邮件已发送到 {to}"; } } public class AdminPlugin { [KernelFunction("DeleteData"), Description("删除所有数据(危险操作)")] public async Task<string> DeleteData() { await Task.Delay(10); return "❌ 数据已删除(模拟)"; } [KernelFunction("GetStats"), Description("获取系统统计信息")] public async Task<string> GetStats() { await Task.Delay(10); return JsonConvert.SerializeObject(new { TotalUsers = 1000, ActiveUsers = 500, Timestamp = DateTime.Now }); } } }
4.3、所有过滤器
using HandlebarsDotNet; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Logging; using Microsoft.SemanticKernel; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net; using System.Text; using System.Threading.RateLimiting; using System.Threading.Tasks; namespace ConsoleApp4 { // ============================================ // ✅ 认证过滤器 // ============================================ public class AuthenticationFilter : IFunctionInvocationFilter { private readonly Func<string, Task<bool>> _validateToken; private readonly Dictionary<string, string[]> _pluginPermissions; public AuthenticationFilter( Func<string, Task<bool>> validateToken, Dictionary<string, string[]>? pluginPermissions = null) { _validateToken = validateToken; _pluginPermissions = pluginPermissions ?? new(); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { var token = GetTokenFromContext(context); if (string.IsNullOrEmpty(token) || !await _validateToken(token)) { throw new UnauthorizedAccessException( $"❌ 未授权的函数调用:{context.Function.PluginName}.{context.Function.Name}"); } if (string.IsNullOrEmpty(context.Function.PluginName)) { await next(context); return; } if (_pluginPermissions.Count > 0) { var userRole = GetUserRoleFromToken(token); if (_pluginPermissions.TryGetValue(context.Function.PluginName, out var requiredRoles)) { if (!requiredRoles.Contains(userRole)) { throw new UnauthorizedAccessException( $"❌ 用户角色 [{userRole}] 无权限访问插件 [{context.Function.PluginName}]"); } } } Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"🔐 认证通过:{context.Function.PluginName}.{context.Function.Name}"); Console.ResetColor(); await next(context); } private string GetTokenFromContext(FunctionInvocationContext context) { if (context.Kernel.Data.TryGetValue("auth_token", out var token)) { return token?.ToString() ?? ""; } return ""; } private string GetUserRoleFromToken(string token) { if (token.Contains("admin")) return "admin"; if (token.Contains("user")) return "user"; return "guest"; } } // ============================================ // ⭐ 自动函数调用限制过滤器 // ============================================ public class AutoFunctionCallLimitFilter : IAutoFunctionInvocationFilter { private readonly int _maxCallsPerConversation; private readonly HashSet<string> _blockedFunctions; private int _callCount = 0; public AutoFunctionCallLimitFilter( int maxCallsPerConversation = 10, string[]? blockedFunctions = null) { _maxCallsPerConversation = maxCallsPerConversation; _blockedFunctions = new HashSet<string>(blockedFunctions ?? Array.Empty<string>()); } public async Task OnAutoFunctionInvocationAsync( AutoFunctionInvocationContext context, Func<AutoFunctionInvocationContext, Task> next) { // 检查调用次数 if (_callCount >= _maxCallsPerConversation) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"⚠️ 已达到最大函数调用次数限制 ({_maxCallsPerConversation})"); Console.ResetColor(); context.Result = new FunctionResult( context.Function, "已达到最大调用次数限制,请简化请求"); return; // 阻止调用 } // 检查黑名单函数 if (_blockedFunctions.Any(f => context.Function.Name.ToLower().Contains(f))) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"⚠️ 函数 {context.Function.Name} 被禁止自动调用"); Console.ResetColor(); context.Result = new FunctionResult( context.Function, $"函数 {context.Function.Name} 需要手动调用"); return; } _callCount++; Console.ForegroundColor = ConsoleColor.DarkCyan; Console.WriteLine($"📞 自动函数调用 ({_callCount}/{_maxCallsPerConversation}): {context.Function.PluginName}.{context.Function.Name}"); Console.ResetColor(); await next(context); } } // ============================================ // ✅ 日志过滤器(彩色控制台输出) // ============================================ public class LoggingFilter : IFunctionInvocationFilter { private readonly ILogger<LoggingFilter> _logger; private readonly Stopwatch _stopwatch; public LoggingFilter(ILogger<LoggingFilter> logger) { _logger = logger; _stopwatch = new Stopwatch(); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { var functionName = $"{context.Function.PluginName}.{context.Function.Name}"; var arguments = string.Join(", ", context.Arguments.Select(kvp => $"{kvp.Key}={kvp.Value}")); // 📝 开始执行函数 - 彩色输出 _logger.LogInformation("📝 开始执行函数:{FunctionName}({Arguments})", functionName, arguments); Console.ForegroundColor = ConsoleColor.Cyan; Console.Write($"📝 开始执行函数:{functionName}("); Console.ForegroundColor = ConsoleColor.White; Console.Write($"{arguments}"); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(")"); Console.ResetColor(); _stopwatch.Restart(); try { // 执行函数 await next(context); _stopwatch.Stop(); var result = context.Result?.GetValue<object>()?.ToString() ?? "null"; // ✅ 函数执行成功 - 彩色输出 _logger.LogInformation( "✅ 函数执行成功:{FunctionName},耗时 {ElapsedMs}ms,结果:{Result}", functionName, _stopwatch.ElapsedMilliseconds, Truncate(result, 100)); Console.ForegroundColor = ConsoleColor.Green; Console.Write($"✅ 函数执行成功:{functionName},"); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.Write($"耗时 {_stopwatch.ElapsedMilliseconds}ms,"); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine($"结果:{Truncate(result, 100)}"); Console.ResetColor(); } catch (Exception ex) { _stopwatch.Stop(); // ❌ 函数执行失败 - 彩色输出 _logger.LogError( ex, "❌ 函数执行失败:{FunctionName},耗时 {ElapsedMs}ms,错误:{Error}", functionName, _stopwatch.ElapsedMilliseconds, ex.Message); Console.ForegroundColor = ConsoleColor.Red; Console.Write($"❌ 函数执行失败:{functionName},"); Console.ForegroundColor = ConsoleColor.DarkRed; Console.Write($"耗时 {_stopwatch.ElapsedMilliseconds}ms,"); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"错误:{ex.Message}"); Console.ResetColor(); throw; } } private string Truncate(string text, int maxLength) { if (string.IsNullOrEmpty(text) || text.Length <= maxLength) return text; return text[..maxLength] + "..."; } } // ============================================ // ✅ 限流过滤器 // ============================================ public class RateLimitingFilter : IFunctionInvocationFilter { private readonly RateLimiter _rateLimiter; private readonly Dictionary<string, RateLimiter> _pluginLimiters; /// <summary> /// 全局限流 /// </summary> public RateLimitingFilter(int requestsPerMinute = 60) { _rateLimiter = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions { PermitLimit = requestsPerMinute, Window = TimeSpan.FromMinutes(1), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 0 // ⚠️ 关键:队列限制设为 0,不等待 }); _pluginLimiters = new(); } /// <summary> /// 按插件限流 /// </summary> public void AddPluginLimit(string pluginName, int requestsPerMinute) { _pluginLimiters[pluginName] = new FixedWindowRateLimiter(new FixedWindowRateLimiterOptions { PermitLimit = requestsPerMinute, Window = TimeSpan.FromMinutes(1), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 0 // ⚠️ 关键:队列限制设为 0,不等待 }); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { // ⭐ 修复:跳过没有 PluginName 的函数 if (string.IsNullOrEmpty(context.Function.PluginName)) { await next(context); return; } // 选择限流器(插件级别优先) var limiter = _pluginLimiters.TryGetValue(context.Function.PluginName, out var pluginLimiter) ? pluginLimiter : _rateLimiter; // ✅ 修复:使用 AttemptAcquire 立即返回,不等待 using var lease = limiter.AttemptAcquire(1); if (!lease.IsAcquired) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"🚫 触发限流:{context.Function.PluginName}.{context.Function.Name}"); Console.ResetColor(); throw new RateLimitExceededException( $"🚫 函数调用频率超限:{context.Function.PluginName}.{context.Function.Name}"); } Console.ForegroundColor = ConsoleColor.DarkYellow; Console.WriteLine($"🚦 限流通过:{context.Function.Name}"); Console.ResetColor(); await next(context); } } /// <summary> /// 限流异常 /// </summary> public class RateLimitExceededException : Exception { public RateLimitExceededException(string message) : base(message) { } } // ============================================ // ✅ 重试过滤器 // ============================================ public class RetryFilter : IFunctionInvocationFilter { private readonly int _maxRetries; private readonly TimeSpan _delay; public RetryFilter(int maxRetries = 3, TimeSpan? delay = null) { _maxRetries = maxRetries; _delay = delay ?? TimeSpan.FromSeconds(1); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { Exception? lastException = null; for (int i = 0; i < _maxRetries; i++) { try { Console.ForegroundColor = ConsoleColor.DarkCyan; Console.WriteLine($"🔄 重试 {i + 1}/{_maxRetries}"); Console.ResetColor(); await next(context); return; // 成功,退出 } catch (HttpOperationException ex) when (IsRetryable(ex)) { lastException = ex; Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"⚠️ 可重试错误:{ex.StatusCode},等待 {_delay.TotalSeconds * (i + 1)}s 后重试..."); Console.ResetColor(); if (i < _maxRetries - 1) { // 指数退避 await Task.Delay(_delay * (i + 1)); } } } throw lastException!; } private bool IsRetryable(HttpOperationException ex) { return ex.StatusCode is HttpStatusCode.TooManyRequests or // 429 限流 HttpStatusCode.InternalServerError or // 500 服务器错误 HttpStatusCode.ServiceUnavailable; // 503 服务不可用 } } // ============================================ // ⭐ Prompt 渲染过滤器(敏感词过滤) // ============================================ public class PromptSanitizerFilter : IPromptRenderFilter { private readonly List<string> _sensitiveWords; public PromptSanitizerFilter(List<string> sensitiveWords) { _sensitiveWords = sensitiveWords; } public async Task OnPromptRenderAsync( PromptRenderContext context, Func<PromptRenderContext, Task> next) { // 渲染 Prompt await next(context); // 处理渲染后的 Prompt var originalPrompt = context.RenderedPrompt; var sanitizedPrompt = Sanitize(originalPrompt); if (originalPrompt != sanitizedPrompt) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine($"\n⚠️ Prompt 已净化,移除敏感词"); Console.WriteLine($" 原始:{Truncate(originalPrompt, 100)}"); Console.WriteLine($" 净化:{Truncate(sanitizedPrompt, 100)}"); Console.ResetColor(); context.RenderedPrompt = sanitizedPrompt; } } private string Sanitize(string prompt) { var result = prompt; foreach (var word in _sensitiveWords) { result = result.Replace(word, new string('*', word.Length), StringComparison.OrdinalIgnoreCase); } return result; } private string Truncate(string text, int maxLength) { if (string.IsNullOrEmpty(text) || text.Length <= maxLength) return text; return text[..maxLength] + "..."; } } // ============================================ // ⭐ 自动函数调用监控过滤器 // ============================================ public class AutoFunctionMonitoringFilter : IAutoFunctionInvocationFilter { private int _invocationCount = 0; public async Task OnAutoFunctionInvocationAsync( AutoFunctionInvocationContext context, Func<AutoFunctionInvocationContext, Task> next) { _invocationCount++; Console.ForegroundColor = ConsoleColor.DarkCyan; Console.WriteLine($"\n🤖 [监控 {_invocationCount}] LLM 选择调用函数:{context.Function.PluginName}.{context.Function.Name}"); Console.ResetColor(); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine($" 参数:{string.Join(", ", context.Arguments.Select(kvp => $"{kvp.Key}={kvp.Value}"))}"); Console.ResetColor(); // 记录开始时间 var stopwatch = Stopwatch.StartNew(); await next(context); stopwatch.Stop(); // 记录结果和耗时 Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine($" 结果:{context.Result?.GetValue<object>()?.ToString() ?? "null"}"); Console.WriteLine($" 耗时:{stopwatch.ElapsedMilliseconds}ms"); Console.ResetColor(); } } /// <summary> /// 缓存过滤器 /// </summary> public class CacheFilter : IFunctionInvocationFilter { private readonly IMemoryCache _cache; private readonly TimeSpan _expiration; public CacheFilter(IMemoryCache cache, TimeSpan? expiration = null) { _cache = cache; _expiration = expiration ?? TimeSpan.FromMinutes(5); } public async Task OnFunctionInvocationAsync( FunctionInvocationContext context, Func<FunctionInvocationContext, Task> next) { // ⭐ 修复:跳过没有 PluginName 的函数 if (string.IsNullOrEmpty(context.Function.PluginName)) { await next(context); return; } // 生成缓存键 var cacheKey = GenerateCacheKey(context); // 尝试从缓存获取 if (_cache.TryGetValue<string>(cacheKey, out var cachedResult)) { Console.WriteLine($"💾 缓存命中:{cacheKey}"); context.Result = new FunctionResult(context.Function, cachedResult); return; // 跳过实际执行 } // 执行函数 await next(context); // 缓存结果 if (context.Result != null) { _cache.Set(cacheKey, context.Result.ToString(), _expiration); } } private string GenerateCacheKey(FunctionInvocationContext context) { var args = string.Join("_", context.Arguments.Select(a => $"{a.Key}={a.Value}")); return $"{context.Function.PluginName}_{context.Function.Name}_{args}"; } } }
4.4、运行效果
4.4.1、Token认证

4.4.2、日志过滤器

4.4.3、缓存过滤器

4.4.4、Prompt 过滤器

4.4.5、限流过滤器

4.4.6、LLM调用监控

4.4.7、LLM黑名单函数

4.5、总结
8 个过滤器注册及触发场景总结
| 序号 | 过滤器 | 注册位置 | 触发场景 |
|---|---|---|---|
| 1 | AuthenticationFilter | FunctionInvocationFilters |
每次函数调用,验证 Token 和权限 |
| 2 | LoggingFilter | FunctionInvocationFilters |
每次函数调用,记录日志和耗时 |
| 3 | CacheFilter | FunctionInvocationFilters |
相同参数调用,缓存命中跳过执行 |
| 4 | PromptSanitizerFilter | PromptRenderFilters |
InvokePromptAsync 调用,过滤敏感词 |
| 5 | RetryFilter | FunctionInvocationFilters |
429/500/503 错误,自动重试 3 次 |
| 6 | RateLimitingFilter | FunctionInvocationFilters |
超过频率限制,抛出 429 异常 |
| 7 | AutoFunctionMonitoringFilter | AutoFunctionInvocationFilters |
LLM 自动选择函数,记录调用详情 |
| 8 | AutoFunctionCallLimitFilter | AutoFunctionInvocationFilters |
LLM 自动选择函数,限制次数和黑名单 |

浙公网安备 33010602011771号