.NET+AI | MEAI | 上下文压缩(6)
目录
1. MessageCountingChatReducer(计数压缩器)
2. SummarizingChatReducer(摘要压缩器)

一句话简介
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. 参数调优建议
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 |
对话简短,不需复杂摘要 |
性能与成本对比
|
对比项 |
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<ChatMessage>();
var user2Messages = new List<ChatMessage>();
3. 自定义 Reducer
实现 IChatReducer 接口创建自定义压缩逻辑:
public classCustomReducer : 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
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)