摘要:Microsoft.Extensions.AI 的 Chat Reducer 通过智能压缩策略,在保持对话质量的前提下,有效控制上下文长度、降低成本并提升性能。
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. 使用计数压缩器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. 使用摘要压缩器// 创建摘要压缩器
// 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. 参数调优建议
策略
参数配置
适用场景
保守策略 targetCount: 10上下文敏感场景 均衡策略 targetCount: 5一般对话 激进策略 targetCount: 2成本优先策略
参数配置
效果
频繁摘要 threshold: 0每次超过立即摘要 延迟摘要 threshold: 3减少 API 调用 3. 与其他中间件组合var client = baseChatClient.AsBuilder.UseChatReducer(reducer: summarizingReducer) // 先压缩
.UseFunctionInvocation // 再处理函数
.Build;
⚠️ 注意:Reducer 应放在管道前端,确保在调用 API 前完成压缩。
场景
推荐 Reducer
原因
客服机器人MessageCounting 只需最近几轮,历史价值低 技术支持MessageCounting 问题独立,不需长期上下文 医疗咨询Summarizing 需完整病史,摘要保证连续性 法律咨询Summarizing 案情细节重要,不能丢失 教育辅导Summarizing 学习进度需长期追踪 快速问答MessageCounting 对话简短,不需复杂摘要 性能与成本对比对比项
MessageCounting
Summarizing
额外 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
var user2Messages = new List
3. 自定义 Reducer实现接口创建自定义压缩逻辑:public classCustomReducer : IChatReducer
{
public Task
IEnumerable
CancellationToken cancellationToken)
{
// 自定义压缩逻辑
var reduced = messages
.Where(m => /* 自定义条件 */)
.TakeLast(5);
return Task.FromResult(reduced);
}
}
⚠️ 注意事项1. 原始消息不会被修改ReduceAsync返回新列表,原始列表保持不变。如需审计,可在本地保留完整历史:var allMessages = new List
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
下一步:探索 MEAI 如何构建自定义IChatClient。
来源:opendotnet
