过滤器 #
概述 #
过滤器(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);
}
下一步 #
最后更新:2026-04-04