惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
N
News and Events Feed by Topic
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
The Cloudflare Blog
H
Heimdal Security Blog
Schneier on Security
Schneier on Security
Engineering at Meta
Engineering at Meta
Google Online Security Blog
Google Online Security Blog
宝玉的分享
宝玉的分享
AI
AI
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
The Last Watchdog
The Last Watchdog
T
Troy Hunt's Blog
S
Security @ Cisco Blogs
H
Hacker News: Front Page
F
Fortinet All Blogs
博客园_首页
S
Secure Thoughts
N
News and Events Feed by Topic
P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
I
InfoQ
Spread Privacy
Spread Privacy
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Hugging Face - Blog
Hugging Face - Blog
Hacker News: Ask HN
Hacker News: Ask HN
C
CXSECURITY Database RSS Feed - CXSecurity.com
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
L
LINUX DO - 最新话题
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Schneier on Security
Know Your Adversary
Know Your Adversary
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
P
Privacy & Cybersecurity Law Blog
S
Securelist
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
O
OpenAI News
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
PCI Perspectives
PCI Perspectives
L
LangChain Blog
雷峰网
雷峰网
Security Archives - TechRepublic
Security Archives - TechRepublic
V2EX - 技术
V2EX - 技术

博客园 - 「圣杰」

.NET+AI | Harness | MAF 1.4 发布,Harness Engineering 如约而至,智能体工程化更进一步 Microsoft Agent Framework 1.0 正式发布:Agent Skills 补齐后,.NET AI Agent 开发真正进入工程化时代 .NET+AI | Workflow | 核心概念速通(1) .NET+AI | 基于 Microsoft Agent Framework 一步步集成 Agent Skills,让你的 AI Agent 更智能 未来已来 | 写给 .NET 开发者的 2025 年度总结 从 MCP 到 Agent Skills,AI Ready 的 .NET 10 正当时 .NET+AI | Agent | 人机协作(9) .NET+AI | MEAI | 上下文压缩(7) .NET+AI | MEAI | 会话缓存(6) .NET+AI | MEAI | ChatOptions 详解(5) AI 时代,.NET 开发者是向左还是向右? .NET+AI | MEAI | Function Caling 实操(4) .NET+AI | MEAI | Function Calling 基础(3) .NET+AI | MEAI | 提示工程基础(2) .NET+AI | MEAI | .NET 平台的 AI 底座 (1) .NET 搞 AI 不行? ​微软 AI Agent三剑客:AutoGen、Semantic Kernel与MEAI的协同演进 .NET+AI | eShopSupport 知多少 .NET + AI | Semantic Kernel vs Microsoft.Extensions.AI 极客时间上新 .NET + AI 体系课
.NET+AI | MEAI | 自定义中间件(8)
「圣杰」 · 2025-11-30 · via 博客园 - 「圣杰」

DelegatingChatClient:构建企业级 AI 中间件的利器

一句话简介

通过 Microsoft.Extensions.AI 的 DelegatingChatClient 基类,轻松创建自定义中间件,实现限流、重试、安全过滤等企业级功能,让 AI 应用更安全、更稳定。


🎯 核心价值

  • 简单易用:只需继承基类并重写需要的方法
  • 灵活组合:多个中间件可以管道式串联
  • 企业就绪:实现限流、安全、监控等生产级功能
  • 标准化:遵循统一的 IChatClient 接口规范

📝 为什么需要自定义中间件?

在实际应用中,我们经常需要对 AI 服务进行增强和控制:

场景 挑战 中间件方案
API 限流 超出调用频率限制 RateLimitingChatClient
网络故障 临时性错误导致失败 RetryingChatClient
内容安全 敏感信息泄露风险 ContentFilteringChatClient
性能监控 无法追踪响应时间 PerformanceMonitoringClient
合规审计 需要记录所有交互 AuditLoggingChatClient

🏗️ DelegatingChatClient 核心概念

核心特性:

  • 🔧 透明转发:默认将所有调用转发到内部客户端
  • 🔧 可选重写:只需重写需要定制的方法
  • 🔧 管道友好:支持多个中间件串联组合

可重写方法:

  • GetResponseAsync:处理完整响应
  • GetStreamingResponseAsync:处理流式响应
  • Dispose:清理资源

💻 快速开始

1. 实现限流中间件

保护 API 免受过载,控制调用频率:

using Microsoft.Extensions.AI;
using System.Threading.RateLimiting;

public sealed class RateLimitingChatClient : DelegatingChatClient
{
    private readonly RateLimiter _rateLimiter;

    public RateLimitingChatClient(IChatClient innerClient, RateLimiter rateLimiter)
        : base(innerClient)
    {
        _rateLimiter = rateLimiter;
    }

    public override async Task<ChatResponse> GetResponseAsync(
        IEnumerable<ChatMessage> messages,
        ChatOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        // 获取限流许可
        using var lease = await _rateLimiter.AcquireAsync(1, cancellationToken);
        
        if (!lease.IsAcquired)
            throw new InvalidOperationException("请求被限流拒绝");

        // 转发到内部客户端
        return await base.GetResponseAsync(messages, options, cancellationToken);
    }
}

使用方式:

var limiter = new ConcurrencyLimiter(new() { PermitLimit = 2 });
var client = new RateLimitingChatClient(baseClient, limiter);

2. 实现安全过滤中间件

过滤敏感信息,保护数据安全:

public sealed class ContentFilteringChatClient : DelegatingChatClient
{
    private readonly HashSet<string> _sensitiveWords;

    public ContentFilteringChatClient(
        IChatClient innerClient, 
        IEnumerable<string> sensitiveWords)
        : base(innerClient)
    {
        _sensitiveWords = new HashSet<string>(
            sensitiveWords, 
            StringComparer.OrdinalIgnoreCase);
    }

    public override async Task<ChatResponse> GetResponseAsync(
        IEnumerable<ChatMessage> messages,
        ChatOptions? options = null,
        CancellationToken cancellationToken = default)
    {
        // 过滤输入消息
        var filteredMessages = FilterMessages(messages);
        
        // 调用底层客户端
        return await base.GetResponseAsync(
            filteredMessages, 
            options, 
            cancellationToken);
    }

    private List<ChatMessage> FilterMessages(IEnumerable<ChatMessage> messages)
    {
        return messages.Select(m => 
        {
            if (m.Text != null && ContainsSensitiveWords(m.Text))
            {
                return new ChatMessage(m.Role, MaskSensitiveWords(m.Text));
            }
            return m;
        }).ToList();
    }
}

3. 使用 ChatClientBuilder.Use 简化开发

除了继承 DelegatingChatClient,还可以使用内联方式:

var client = baseClient.AsBuilder()
    // 添加日志中间件
    .Use(async (messages, options, innerClient, cancellationToken) =>
    {
        Console.WriteLine($"[日志] 收到 {messages.Count()} 条消息");
        var sw = Stopwatch.StartNew();
        
        var response = await innerClient.GetResponseAsync(
            messages, options, cancellationToken);
        
        Console.WriteLine($"[日志] 耗时: {sw.ElapsedMilliseconds}ms");
        return response;
    })
    // 添加重试中间件
    .Use(async (messages, options, innerClient, cancellationToken) =>
    {
        for (int i = 0; i < 3; i++)
        {
            try
            {
                return await innerClient.GetResponseAsync(
                    messages, options, cancellationToken);
            }
            catch (Exception ex) when (i < 2)
            {
                Console.WriteLine($"[重试] 第 {i + 1} 次失败,准备重试...");
                await Task.Delay(1000 * (i + 1));
            }
        }
        throw new Exception("重试失败");
    })
    .Build();

优势对比:

方式 适用场景 优势
继承方式 复杂逻辑、资源管理 完全控制、可复用
内联方式 简单场景、快速开发 代码简洁、灵活

🔧 创建可复用扩展方法

将中间件封装为扩展方法,提高复用性:

public static class ChatClientExtensions
{
    public static ChatClientBuilder UseRateLimiting(
        this ChatClientBuilder builder,
        RateLimiter rateLimiter)
    {
        return builder.Use(innerClient => 
            new RateLimitingChatClient(innerClient, rateLimiter));
    }

    public static ChatClientBuilder UseContentFiltering(
        this ChatClientBuilder builder,
        IEnumerable<string> sensitiveWords)
    {
        return builder.Use(innerClient => 
            new ContentFilteringChatClient(innerClient, sensitiveWords));
    }

    public static ChatClientBuilder UsePerformanceMonitoring(
        this ChatClientBuilder builder)
    {
        return builder.Use(async (messages, options, innerClient, ct) =>
        {
            var sw = Stopwatch.StartNew();
            var response = await innerClient.GetResponseAsync(messages, options, ct);
            Console.WriteLine($"[性能] {sw.ElapsedMilliseconds}ms");
            return response;
        });
    }
}

使用扩展方法:

var client = baseClient.AsBuilder()
    .UsePerformanceMonitoring()
    .UseContentFiltering(new[] { "密码", "账号" })
    .UseRateLimiting(rateLimiter)
    .Build();

🏢 企业级最佳实践

1. 中间件执行顺序(洋葱模型)

请求: 外层 → 内层 → AI 模型
响应: AI 模型 → 内层 → 外层

推荐顺序:

层级 中间件类型 原因
最外层 日志、监控 记录所有请求和响应
中间层 安全过滤 在消耗资源前拦截
内层 限流、缓存 减少 API 调用

示例配置:

var client = baseClient.AsBuilder()
    .UsePerformanceMonitoring()  // 外层:监控
    .UseContentFiltering(words)  // 中层:安全
    .UseRateLimiting(limiter)    // 内层:限流
    .Build();

2. 处理流式和非流式响应

需要同时支持两种模式:

// 非流式响应
public override async Task<ChatResponse> GetResponseAsync(...)
{
    var response = await base.GetResponseAsync(...);
    ProcessFullContent(response.Text);
    return response;
}

// 流式响应
public override async IAsyncEnumerable<ChatResponseUpdate> 
    GetStreamingResponseAsync(...)
{
    StringBuilder accumulated = new();
    await foreach (var update in base.GetStreamingResponseAsync(...))
    {
        accumulated.Append(update.Text);
        yield return update;
    }
    ProcessFullContent(accumulated.ToString());
}

3. 资源管理和生命周期

正确实现资源释放:

public sealed class MyCustomChatClient : DelegatingChatClient
{
    private readonly IDisposable _resource;
    
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _resource?.Dispose();
        }
        base.Dispose(disposing);
    }
}

最佳实践:

  • ✅ 始终调用 base.Dispose(disposing)
  • ✅ 在 disposing == true 时释放托管资源
  • ✅ 使用 using 语句确保释放

4. 依赖注入集成

在 ASP.NET Core 中使用:

// Program.cs
builder.Services.AddSingleton<RateLimiter>(_ => 
    new ConcurrencyLimiter(new() { PermitLimit = 10 }));

builder.Services.AddChatClient(services =>
{
    var baseClient = /* 创建基础客户端 */;
    
    return baseClient
        .AsBuilder()
        .UsePerformanceMonitoring()
        .UseContentFiltering(new[] { "敏感词" })
        .UseRateLimiting(services.GetRequiredService<RateLimiter>())
        .Build();
});

// 在服务中注入使用
public class MyService
{
    private readonly IChatClient _chatClient;
    
    public MyService(IChatClient chatClient)
    {
        _chatClient = chatClient;
    }
}

🎯 总结

  • 三种实现方式:继承 DelegatingChatClient、Use 内联、扩展方法
  • 常见场景:限流、安全、监控、重试、审计
  • 洋葱模型:外层监控、中层安全、内层限流
  • 生产就绪:资源管理、依赖注入、错误处理

选择建议:

  • 💻 复杂逻辑、需要资源管理 → 继承 DelegatingChatClient
  • 💻 简单场景、快速开发 → Use 内联方式
  • 💻 可复用组件 → 扩展方法

下一步: 探索 MEAI ChatClient中间件和Function Invoker的区别