过滤器 #

概述 #

过滤器(Filter)是 Semantic Kernel 的中间件机制,允许在函数调用的前后执行自定义逻辑,用于日志记录、监控、安全控制等。

过滤器类型 #

text
┌─────────────────────────────────────────────────────────────┐
│                    过滤器类型                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  IFunctionFilter(函数过滤器)                               │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  拦截所有函数调用                                    │   │
│  │  - 日志记录                                         │   │
│  │  - 性能监控                                         │   │
│  │  - 错误处理                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  IPromptRenderFilter(提示词渲染过滤器)                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  拦截提示词渲染                                      │   │
│  │  - 提示词修改                                       │   │
│  │  - 敏感信息过滤                                     │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  IAutoFunctionInvocationFilter(自动函数调用过滤器)         │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  拦截自动函数调用                                    │   │
│  │  - 权限检查                                         │   │
│  │  - 调用限制                                         │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

函数过滤器 #

创建函数过滤器 #

csharp
using Microsoft.SemanticKernel;

public class LoggingFilter : IFunctionFilter
{
    private readonly ILogger<LoggingFilter> _logger;

    public LoggingFilter(ILogger<LoggingFilter> logger)
    {
        _logger = logger;
    }

    public async Task OnFunctionInvocationAsync(
        FunctionInvocationContext context,
        Func<FunctionInvocationContext, Task> next)
    {
        var functionName = $"{context.Function.PluginName}.{context.Function.Name}";
        
        _logger.LogInformation("开始执行: {Function}", functionName);
        _logger.LogDebug("参数: {Arguments}", 
            string.Join(", ", context.Arguments.Select(a => $"{a.Key}={a.Value}")));

        var stopwatch = Stopwatch.StartNew();

        try
        {
            await next(context);
            
            stopwatch.Stop();
            _logger.LogInformation("执行完成: {Function}, 耗时: {Ms}ms", 
                functionName, stopwatch.ElapsedMilliseconds);
        }
        catch (Exception ex)
        {
            stopwatch.Stop();
            _logger.LogError(ex, "执行失败: {Function}, 耗时: {Ms}ms", 
                functionName, stopwatch.ElapsedMilliseconds);
            throw;
        }
    }
}

注册过滤器 #

csharp
kernel.FunctionFilters.Add(new LoggingFilter(logger));

提示词渲染过滤器 #

创建提示词过滤器 #

csharp
public class PromptFilter : IPromptRenderFilter
{
    public async Task OnPromptRenderAsync(
        PromptRenderContext context,
        Func<PromptRenderContext, Task> next)
    {
        // 渲染前
        Console.WriteLine($"渲染提示词: {context.Function.Name}");
        
        await next(context);
        
        // 渲染后
        var renderedPrompt = context.RenderedPrompt;
        
        // 敏感信息过滤
        renderedPrompt = FilterSensitiveInfo(renderedPrompt);
        
        context.RenderedPrompt = renderedPrompt;
    }

    private string FilterSensitiveInfo(string prompt)
    {
        // 过滤敏感信息
        return prompt;
    }
}

注册提示词过滤器 #

csharp
kernel.PromptRenderFilters.Add(new PromptFilter());

自动函数调用过滤器 #

创建自动调用过滤器 #

csharp
public class AutoFunctionFilter : IAutoFunctionInvocationFilter
{
    private readonly int _maxCalls;
    private int _callCount = 0;

    public AutoFunctionFilter(int maxCalls = 10)
    {
        _maxCalls = maxCalls;
    }

    public async Task OnAutoFunctionInvocationAsync(
        AutoFunctionInvocationContext context,
        Func<AutoFunctionInvocationContext, Task> next)
    {
        // 检查调用次数
        if (_callCount >= _maxCalls)
        {
            context.Result = new FunctionResult(
                context.Function, 
                "已达到最大调用次数限制"
            );
            return;
        }

        // 权限检查
        if (!IsAllowed(context.Function))
        {
            context.Result = new FunctionResult(
                context.Function,
                "无权限调用此函数"
            );
            return;
        }

        _callCount++;
        await next(context);
    }

    private bool IsAllowed(KernelFunction function)
    {
        // 检查函数是否允许自动调用
        var sensitiveFunctions = new[] { "delete", "remove", "send" };
        return !sensitiveFunctions.Any(f => 
            function.Name.ToLowerInvariant().Contains(f));
    }
}

注册自动调用过滤器 #

csharp
kernel.AutoFunctionInvocationFilters.Add(new AutoFunctionFilter());

实用过滤器示例 #

性能监控过滤器 #

csharp
public class PerformanceFilter : IFunctionFilter
{
    private readonly IMetricsService _metrics;

    public PerformanceFilter(IMetricsService metrics)
    {
        _metrics = metrics;
    }

    public async Task OnFunctionInvocationAsync(
        FunctionInvocationContext context,
        Func<FunctionInvocationContext, Task> next)
    {
        var stopwatch = Stopwatch.StartNew();
        
        await next(context);
        
        stopwatch.Stop();
        
        _metrics.RecordDuration(
            context.Function.Name,
            stopwatch.ElapsedMilliseconds
        );
        
        // 记录 token 使用
        var usage = context.Result.Metadata?.GetValue<Dictionary<string, object>>("Usage");
        if (usage != null)
        {
            _metrics.RecordTokens(
                context.Function.Name,
                (int)usage["PromptTokens"],
                (int)usage["CompletionTokens"]
            );
        }
    }
}

重试过滤器 #

csharp
public class RetryFilter : IFunctionFilter
{
    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
            {
                await next(context);
                return;
            }
            catch (HttpOperationException ex) when (IsRetryable(ex))
            {
                lastException = ex;
                
                if (i < _maxRetries - 1)
                {
                    await Task.Delay(_delay * (i + 1));
                }
            }
        }

        throw lastException!;
    }

    private bool IsRetryable(HttpOperationException ex)
    {
        return ex.StatusCode is 
            System.Net.HttpStatusCode.TooManyRequests or
            System.Net.HttpStatusCode.InternalServerError or
            System.Net.HttpStatusCode.ServiceUnavailable;
    }
}

缓存过滤器 #

csharp
public class CacheFilter : IFunctionFilter
{
    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)
    {
        // 生成缓存键
        var cacheKey = GenerateCacheKey(context);
        
        // 尝试从缓存获取
        if (_cache.TryGetValue<string>(cacheKey, out var cachedResult))
        {
            context.Result = new FunctionResult(context.Function, cachedResult);
            return;
        }

        await next(context);

        // 缓存结果
        if (context.Result.HasValue)
        {
            _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}";
    }
}

安全过滤器 #

csharp
public class SecurityFilter : IFunctionFilter
{
    private readonly IUserContext _userContext;
    private readonly Dictionary<string, string[]> _permissions;

    public SecurityFilter(IUserContext userContext)
    {
        _userContext = userContext;
        _permissions = new Dictionary<string, string[]>
        {
            ["AdminPlugin"] = new[] { "admin" },
            ["UserPlugin"] = new[] { "admin", "user" }
        };
    }

    public async Task OnFunctionInvocationAsync(
        FunctionInvocationContext context,
        Func<FunctionInvocationContext, Task> next)
    {
        var pluginName = context.Function.PluginName;
        
        if (_permissions.TryGetValue(pluginName, out var requiredRoles))
        {
            if (!requiredRoles.Any(role => _userContext.HasRole(role)))
            {
                throw new UnauthorizedAccessException(
                    $"无权限访问 {pluginName}"
                );
            }
        }

        await next(context);
    }
}

过滤器执行顺序 #

csharp
// 过滤器按注册顺序执行
kernel.FunctionFilters.Add(new LoggingFilter());      // 第1个执行
kernel.FunctionFilters.Add(new PerformanceFilter());  // 第2个执行
kernel.FunctionFilters.Add(new CacheFilter());        // 第3个执行

// 执行顺序:
// 1. LoggingFilter - 前
// 2. PerformanceFilter - 前
// 3. CacheFilter - 前
// 4. 实际函数执行
// 5. CacheFilter - 后
// 6. PerformanceFilter - 后
// 7. LoggingFilter - 后

全局过滤器 #

依赖注入注册 #

csharp
services.AddSingleton<IFunctionFilter, LoggingFilter>();
services.AddSingleton<IFunctionFilter, PerformanceFilter>();
services.AddSingleton<IFunctionFilter, CacheFilter>();

services.AddKernel()
    .AddOpenAIChatCompletion("gpt-4", "api-key");

最佳实践 #

1. 单一职责 #

csharp
// 好的做法:每个过滤器只做一件事
public class LoggingFilter : IFunctionFilter { }
public class PerformanceFilter : IFunctionFilter { }
public class SecurityFilter : IFunctionFilter { }

// 避免:一个过滤器做太多事
public class DoEverythingFilter : IFunctionFilter { }

2. 异常处理 #

csharp
public async Task OnFunctionInvocationAsync(
    FunctionInvocationContext context,
    Func<FunctionInvocationContext, Task> next)
{
    try
    {
        await next(context);
    }
    catch (Exception ex)
    {
        // 记录但不吞掉异常
        _logger.LogError(ex, "过滤器捕获异常");
        throw;
    }
}

3. 条件执行 #

csharp
public async Task OnFunctionInvocationAsync(
    FunctionInvocationContext context,
    Func<FunctionInvocationContext, Task> next)
{
    // 只对特定函数应用
    if (context.Function.PluginName == "SensitivePlugin")
    {
        // 执行安全检查
    }
    
    await next(context);
}

下一步 #

现在你已经掌握了过滤器,接下来学习 依赖注入,了解如何与 ASP.NET Core 集成!

最后更新:2026-04-04