.NET+AI | MEAI | 上下文压缩(7)

Chat Reducer:让 AI 对话突破上下文限制

一句话简介

Microsoft.Extensions.AI 的 Chat Reducer 通过智能压缩策略,在保持对话质量的前提下,有效控制上下文长度、降低成本并提升性能。


🎯 核心价值

  • 突破限制:解决 LLM 上下文窗口限制(如 GPT-4 的 8K/32K tokens)
  • 成本优化:减少输入 token,显著降低 API 调用成本
  • 性能提升:缩短上下文长度,加快模型推理速度
  • 智能压缩:保留关键信息,自动过滤冗余历史消息

📝 为什么需要 Chat Reducer?

在多轮对话场景中,我们面临三大挑战:

挑战问题Chat Reducer 方案
上下文限制超出模型限制导致请求失败智能压缩到安全范围
成本失控输入 token 越多费用越高过滤冗余,只保留必要信息
性能下降过长上下文增加推理时间减少处理负担,提升响应速度

典型场景:

  • 💬 长时间客服对话(用户反复咨询)
  • 🏥 医疗咨询(需要完整病史)
  • ⚖️ 法律咨询(案情细节不能丢失)
  • 🎓 教育辅导(需要追踪学习进度)

🏗️ 两种压缩策略

1. MessageCountingChatReducer(计数压缩器)

通过限制消息数量来控制对话长度。

核心特性:

  • 🔧 始终保留第一条系统消息
  • 🔧 保留最近 N 条用户/助手消息
  • 🔧 自动排除函数调用相关消息
  • 🔧 零延迟,无额外 API 成本

适用场景:

  • 客服机器人(只关注最近几轮)
  • 快速问答系统
  • 技术支持(问题独立,不需长期上下文)

2. SummarizingChatReducer(摘要压缩器)

利用 AI 自动生成摘要压缩历史对话。

核心特性:

  • 🔧 超过阈值时自动调用 AI 生成摘要
  • 🔧 摘要存储在 AdditionalProperties
  • 🔧 渐进式压缩(新摘要包含旧摘要)
  • 🔧 保留完整语义上下文

适用场景:

  • 医疗咨询(完整病史重要)
  • 法律咨询(案情细节关键)
  • 教育辅导(长期进度追踪)

💻 快速开始

1. 使用计数压缩器

/* by 01022.hk - online tools website : 01022.hk/zh/endecodejs.html */
using Microsoft.Extensions.AI;

// 创建压缩器,保留最近 3 条消息
var countingReducer = new MessageCountingChatReducer(targetCount: 3);

// 集成到 Chat Client
var client = baseChatClient.AsBuilder()
    .UseChatReducer(reducer: countingReducer)
    .Build();

// 正常使用,自动压缩
var response = await client.GetResponseAsync(messages);

工作原理:

原始消息(13条)              压缩后(4条)
[System] 你是助手           [System] 你是助手
[User] 问题1                
[Assistant] 回答1           
[User] 问题2               
[Assistant] 回答2           
...                        [User] 问题5
[User] 问题5                [Assistant] 回答5
[Assistant] 回答5           [User] 问题6
[User] 问题6

2. 使用摘要压缩器

/* by 01022.hk - online tools website : 01022.hk/zh/endecodejs.html */
// 创建摘要压缩器
// targetCount: 保留最近 2 条消息
// threshold: 超过 targetCount + threshold 时触发摘要
var summarizingReducer = new SummarizingChatReducer(
    chatClient: baseChatClient,
    targetCount: 2,
    threshold: 1  // 超过 3 条时触发
);

// 集成到 Chat Client
var client = baseChatClient.AsBuilder()
    .UseChatReducer(reducer: summarizingReducer)
    .Build();

工作原理:

原始消息(7条)                    压缩后(4条)
[System] 你是医疗助手            [System] 你是医疗助手
[User] 我头痛                    [Summary] 患者主诉头痛,
[Assistant] 可能是压力...                  睡眠不足,已建议休息
[User] 我睡眠不足                [User] 我眼睛干涩
[Assistant] 建议保证睡眠          [Assistant] 使用人工泪液...
[User] 我眼睛干涩
[Assistant] 使用人工泪液...

🔧 高级配置

1. 自定义摘要提示词

var reducer = new SummarizingChatReducer(baseChatClient, targetCount: 2);

// 设置领域专用摘要提示词
reducer.SummarizationPrompt = """
请为以下医疗咨询对话生成简洁的临床摘要(不超过3句话):

要求:
- 提取患者主诉症状和时长
- 记录已提供的初步建议
- 保留关键医学信息
- 使用专业医学术语

格式: 【患者主诉】症状 | 【已知信息】背景 | 【初步建议】建议
""";

2. 参数调优建议

MessageCountingChatReducer:

策略参数配置适用场景
保守策略targetCount: 10上下文敏感场景
均衡策略targetCount: 5一般对话
激进策略targetCount: 2成本优先

SummarizingChatReducer:

策略参数配置效果
频繁摘要threshold: 0每次超过立即摘要
延迟摘要threshold: 3减少 API 调用

3. 与其他中间件组合

var client = baseChatClient.AsBuilder()
    .UseChatReducer(reducer: summarizingReducer)  // 先压缩
    .UseFunctionInvocation()                       // 再处理函数
    .Build();

⚠️ 注意: Reducer 应放在管道前端,确保在调用 API 前完成压缩。


🏢 选择策略指南

场景对比表

场景推荐 Reducer原因
客服机器人MessageCounting只需最近几轮,历史价值低
技术支持MessageCounting问题独立,不需长期上下文
医疗咨询Summarizing需完整病史,摘要保证连续性
法律咨询Summarizing案情细节重要,不能丢失
教育辅导Summarizing学习进度需长期追踪
快速问答MessageCounting对话简短,不需复杂摘要

性能与成本对比

对比项MessageCountingSummarizing
额外 API 调用✅ 无❌ 每次摘要 1 次
延迟✅ 0ms⚠️ 1-3 秒
语义完整性⚠️ 可能丢失✅ 保留
成本✅ 低⚠️ 中等
适用场景短期对话长期对话

💡 优化技巧: 使用较小模型(如 GPT-3.5)专门用于摘要生成,降低成本。


💡 最佳实践

1. 函数调用消息自动保护

两种 Reducer 都会自动排除函数调用相关消息,避免破坏上下文:

// 这些消息会被自动跳过,不计入 targetCount
- FunctionCallContent
- FunctionResultContent

2. 多用户场景

为每个用户会话创建独立消息列表,共享 Reducer 实例:

// 全局共享的 Reducer(无状态)
var sharedReducer = new MessageCountingChatReducer(5);

// 每个用户独立的消息历史
var user1Messages = new List<ChatMessage>();
var user2Messages = new List<ChatMessage>();

3. 自定义 Reducer

实现 IChatReducer 接口创建自定义压缩逻辑:

public class CustomReducer : IChatReducer
{
    public Task<IEnumerable<ChatMessage>> ReduceAsync(
        IEnumerable<ChatMessage> messages, 
        CancellationToken cancellationToken)
    {
        // 自定义压缩逻辑
        var reduced = messages
            .Where(m => /* 自定义条件 */)
            .TakeLast(5);
        
        return Task.FromResult(reduced);
    }
}

⚠️ 注意事项

1. 原始消息不会被修改

ReduceAsync() 返回新列表,原始列表保持不变。如需审计,可在本地保留完整历史:

var allMessages = new List<ChatMessage>();      // 完整历史
var reducedMessages = await reducer.ReduceAsync(allMessages);
// allMessages 仍包含所有消息

2. 摘要压缩的信息损失

摘要依赖 LLM 理解能力,可能会:

  • ✅ 保留主要事实和语义
  • ⚠️ 丢失细微情感、口语化表达
  • ⚠️ 潜在的理解偏差

建议: 关键信息(订单号、金额)结合数据库存储,不完全依赖摘要。


3. 流式响应支持

Reducer 完全支持流式场景,在开始传输前自动完成压缩:

await foreach (var update in client.GetStreamingResponseAsync(messages))
{
    Console.Write(update.Text);
}

🎯 总结

  • 两种策略: MessageCounting(快速简单) vs Summarizing(语义完整)
  • 一行集成: 通过 UseChatReducer() 轻松启用
  • 灵活配置: 支持自定义提示词、参数调优、自定义实现
  • 生产就绪: 自动处理函数调用、支持流式、多用户安全

选择建议:

  • 💬 短期对话、成本敏感 → MessageCountingChatReducer
  • 🏥 长期咨询、语义重要 → SummarizingChatReducer
👆面向.NET开发者的AI Agent 开发课程【.NET+AI | 智能体开发进阶】已上线,欢迎扫码加入学习。👆
关注我的公众号『向 AI 而行』,我们微信不见不散。
阅罢此文,如果您觉得本文不错并有所收获,请【打赏】或【推荐】,也可【评论】留下您的问题或建议与我交流。 你的支持是我不断创作和分享的不竭动力!
作者:『圣杰』
出处:http://www.cnblogs.com/sheng-jie/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值