ReducingChatClient——精减对话历史又不丢失基本语义
利用ReducingChatClient摘要对话内容如下的程序演示了如何利用ReducingChatClient来部分对话内容进行摘要保证在不丢失基本语义的前提下腾出更多的上下文窗口。如代码片段所示我们基于OpenAIClient创建了一个IChatClient对象并在此基础上利用ChatClientBuilder注册了ReducingChatClient中间件并指定了一个SummarizingChatReducer对象来提供基于摘要的队对话精减功能。我们在创建SummarizingChatReducer对象的时候传入了一个用于对摘要进行生成的ChatClient对象该对象依然是基于OpenAIClient创建的并且使用了相同的模型来生成摘要。我们还为SummarizingChatReducer对象指定了targetCount和threshold两个参数前者表示我们希望在摘要之后保留多少条消息后者则是一个阈值用于触发摘要操作的阈值超过targetCountthreshold。using Azure;using dotenv.net;using Microsoft.Extensions.AI;using OpenAI;DotEnv.Load();var apiKey Environment.GetEnvironmentVariable(“API_KEY”)!;var endpoint Environment.GetEnvironmentVariable(“OPENAI_URL”)!;var summaryClient new OpenAIClient(credential: new AzureKeyCredential(apiKey),options: new OpenAIClientOptions { Endpoint new Uri(endpoint) }).GetChatClient(model: “DeepSeek-V4-Pro”).AsIChatClient();var client new OpenAIClient(credential: new AzureKeyCredential(apiKey),options: new OpenAIClientOptions { Endpoint new Uri(endpoint) }).GetChatClient(model: “gpt-5.2-chat”).AsIChatClient().AsBuilder().UseChatReducer(reducer: new SummarizingChatReducer(chatClient:summaryClient, targetCount: 3, threshold:1)).Use((messages,options, next, cancelToken) {Console.WriteLine(KaTeX parse error: Expected }, got EOF at end of input: …sole.WriteLine(“{index}. {message}”);}return next(messages, options, cancelToken);}).Build();ChatMessage[] messages [new ChatMessage(ChatRole.User, “今天苏州的天气怎么样”),new ChatMessage(ChatRole.Assistant, “苏州今天是晴天。”),new ChatMessage(ChatRole.User, “气温多少。”),new ChatMessage(ChatRole.Assistant, “室外温度25度。”),new ChatMessage(ChatRole.User, “有风吗”),new ChatMessage(ChatRole.Assistant, “西北风4级。”),new ChatMessage(ChatRole.User, “根据天气给我一些着装建议。”)];var response await client.GetResponseAsync(messages);Console.WriteLine($“\n\n{response}”);为了查看经过ReducingChatClient精减之后的对话历史我们在ChatClientBuilder中注册了一个简单的中间件来输出当前传入的消息列表。IChatClient管道构建成功之后我们调用GetResponseAsync方法并指定了一组消息共7条来模拟一段对话的历史。由于我们在ReducingChatClient中指定了targetCount为3并且threshold为1必然会触发摘要操作。摘要完成后保留了最后三条消息只对对前4条消息进行了摘要这一切体现在如下的输出中请求消息共计4条用户询问了今天苏州的天气情况助手回答为晴天。随后用户进一步询问气温助手回答室外温度为25度。对话围绕苏州当日的天气状况和具体气温展开内容简洁明确。有风吗西北风4级。根据天气给我一些着装建议。今天苏州晴天25℃西北风4级体感会比较清爽风稍微有点明显。给你一些穿搭建议 上衣短袖T恤、薄衬衫都可以如果怕风建议带一件薄外套/防风夹克 下装牛仔裤、休闲裤都合适不怕冷的话也可以穿薄款长裙/半裙 鞋子运动鞋、休闲鞋都很舒服风有点大尽量避免太轻薄易飘的穿搭 其他建议晴天紫外线可能偏强出门可以戴太阳镜、涂防晒风力4级骑车会有点顶风注意安全整体来说是舒适偏清爽型天气穿得轻松一点就好 2. IChatReducerReducingChatClient的核心是IChatReducer接口我们可以称之为精简器。它定义了一个ReduceAsync方法用于对传入的消息列表进行精减处理。我们可以通过实现IChatReducer接口来定义自己的消息精减策略从而满足不同场景下的需求。public interface IChatReducer{TaskIEnumerable ReduceAsync(IEnumerable messages,CancellationToken cancellationToken);}2.1 SummarizingChatReducerSummarizingChatReducer是IChatReducer接口的一个实现它通过生成摘要的方式来对消息列表进行精减。我们在创建SummarizingChatReducer对象的时候需要传入一个用于生成摘要的IChatClient对象以及targetCount和threshold两个参数。targetCount表示我们希望在摘要之后保留多少条消息threshold表示触发摘要的阈值具体来说当总消息数量targetCount threshold时摘要会被触发。理想状态下系统会尝试保留最新的targetCount条消息不被摘要将其余的旧消息进行压缩。public sealed class SummarizingChatReducer : IChatReducer{public string SummarizationPrompt{ get; set;}public SummarizingChatReducer(IChatClient chatClient, int targetCount, int? threshold);public async TaskIEnumerable ReduceAsync(IEnumerable messages, CancellationToken cancellationToken);}为了防止对话上下文被生硬切断系统在确定从哪条消息开始保留时有两条关键的边界保护规则保持工具调用完整性如果切分点刚好处于工具函数调用或返回结果的中间切分点会向前更旧的消息移动确保函数调用消息包含FunctionCallContext与其响应结果消息包含FunctionResultContent完整保留在同一个作用域内不被摘要拆散避免用户问题孤立在缓冲阈值窗口threshold内系统会向前更旧的消息寻找角色为User的消息。一旦找到就会在用户消息之前切断。这样可以确保用户的提问与其后续的LLM回复、工具调用保存在一起避免问题被摘要但答案被保留的孤立现象。我们可以利用SummarizationPrompt属性来指定一个自定义的提示词来控制摘要的生成。默认情况下SummarizingChatReducer会使用一个预定义的提示词来生成摘要这个提示词会指导ChatClient如何对消息列表进行摘要处理从而保证在不丢失基本语义的前提下尽可能地精简消息列表。如下所示的是默认的提示词。Generate a clear and complete summary of the entire conversation in no more than five sentences.The summary must always:Reflect contributions from both the user and the assistantPreserve context to support ongoing dialogueIncorporate any previously provided summaryEmphasize the most relevant and meaningful pointsThe summary must never:Offer critique, correction, interpretation, or speculationHighlight errors, misunderstandings, or judgments of accuracyComment on events or ideas not present in the conversationOmit any details included in an earlier summary2.2 MessageCountingChatReducer与SummarizingChatReducer不同MessageCountingChatReducer是一个纯轻量级、零AI消耗、基于消息数量进行滑动窗口裁剪Sliding Window的精简器。MessageCountingChatReducer的精简策略简单粗暴直接保留最近的N条消息其中N由targetCount参数指定。public sealed class MessageCountingChatReducer : IChatReducer{public MessageCountingChatReducer(int targetCount);public TaskIEnumerable ReduceAsync(IEnumerable messages, CancellationToken cancellationToken);}两者选择保留消息的策略会不一样MessageCountingChatReducer它会保留最近的targetCount条消息但不包含FunctionCallContent 或FunctionResultContent的消息。整个消息列表包含系统消息第一条最旧的那条系统消息会被保留并置于保留消息的最前端后续的系统消息会被直接抹除。系统消息不占用targetCount的配额也就说最多会有targetCount 1条消息被保留SummarizingChatReducer它不会丢弃工具消息。相反它通过向前更旧的消息移动寻找边界确保只要最新的上下文里触发了工具调用整个工具调用链调用 结果就完整地保留在未摘要的消息列表中对于前面的实例如果我们将ReducingChatClient中使用的精简器从SummarizingChatReducer换成MessageCountingChatReducer那么在输出当前传入的消息列表的时候我们会发现它直接保留了最后的三条消息而没有对前面的消息进行任何摘要处理。using Azure;using dotenv.net;using Microsoft.Extensions.AI;using OpenAI;DotEnv.Load();var apiKey Environment.GetEnvironmentVariable(“API_KEY”)!;var endpoint Environment.GetEnvironmentVariable(“OPENAI_URL”)!;var summaryClient new OpenAIClient(credential: new AzureKeyCredential(apiKey),options: new OpenAIClientOptions { Endpoint new Uri(endpoint) }).GetChatClient(model: “gpt-5.2-chat”).AsIChatClient();var client new OpenAIClient(credential: new AzureKeyCredential(apiKey),options: new OpenAIClientOptions { Endpoint new Uri(endpoint) }).GetChatClient(model: “gpt-5.2-chat”).AsIChatClient().AsBuilder().UseChatReducer(reducer: new MessageCountingChatReducer(targetCount: 3)).Use((messages,options, next, cancelToken) {Console.WriteLine(KaTeX parse error: Expected }, got EOF at end of input: …sole.WriteLine(“{index}. {message}”);}return next(messages, options, cancelToken);}).Build();ChatMessage[] messages [new ChatMessage(ChatRole.User, “今天苏州的天气怎么样”),new ChatMessage(ChatRole.Assistant, “苏州今天是晴天。”),new ChatMessage(ChatRole.User, “气温多少。”),new ChatMessage(ChatRole.Assistant, “室外温度25度。”),new ChatMessage(ChatRole.User, “有风吗”),new ChatMessage(ChatRole.Assistant, “西北风4级。”),new ChatMessage(ChatRole.User, “根据天气给我一些着装建议。”)];var response await client.GetResponseAsync(messages);Console.WriteLine($“\n\n{response}”);请求消息共计3条有风吗西北风4级。根据天气给我一些着装建议。目前是西北风4级风力算是比较明显的体感温度可能会比实际温度低一些。给你一些穿衣建议✅外套必备建议穿一件防风外套、风衣或薄款夹克。✅内搭可叠穿长袖T恤或薄针织衫比较合适方便根据冷热增减。✅下装长裤更舒适避免被风吹得发凉。✅怕冷的话可以加一条薄围巾尤其是西北风通常偏干偏凉。如果你告诉我现在的气温我可以给你更具体的搭配建议 3. ReducingChatClientReducingChatClient中间件的实现非常简单它在接收到消息列表之后会调用IChatReducer的ReduceAsync方法来对消息列表进行精减处理然后将精减后的消息列表传递给管道中的下一个中间件或者最终的IChatClient来生成响应。通过这种方式ReducingChatClient能够帮助我们精简对话内容从而腾出更多的上下文窗口来保证LLM推理的质量。public sealed class ReducingChatClient : DelegatingChatClient{public ReducingChatClient(IChatClient innerClient, IChatReducer reducer);public override async Task GetResponseAsync(IEnumerable messages,ChatOptions? options null,CancellationToken cancellationToken default);public override async IAsyncEnumerable GetStreamingResponseAsync(IEnumerable messages,ChatOptions? options null,CancellationToken cancellationToken default);}4. UseChatReducer扩展方法UseChatReducer是一个ChatClientBuilder的扩展方法它提供了一种简便的方式来注册ReducingChatClient中间件。我们只需要在构建IChatClient对象的时候调用UseChatReducer方法并传入一个IChatReducer对象来指定我们想要使用的精简器就可以轻松地将ReducingChatClient中间件添加到我们的IChatClient对象中了。除此之外UseChatReducer方法还提供了一个可选的configure参数它允许我们在注册ReducingChatClient中间件的时候对其进行一些额外的配置。public static class ReducingChatClientBuilderExtensions{public static ChatClientBuilder UseChatReducer(this ChatClientBuilder builder,IChatReducer? reducer null,Action? configure null);

相关新闻

Pytify入门指南:5分钟快速掌握命令行Spotify控制技巧

Pytify入门指南:5分钟快速掌握命令行Spotify控制技巧

Pytify入门指南:5分钟快速掌握命令行Spotify控制技巧 【免费下载链接】Pytify A CLI application for controlling Spotify 项目地址: https://gitcode.com/gh_mirrors/py/Pytify 想要在终端中优雅地控制Spotify音乐播放吗?Pytify就是你的终极解决…

2026/7/10 18:23:14阅读更多 →
OpenAI、Anthropic、智谱一月内接连落子,模型工程价值重心正在转移!

OpenAI、Anthropic、智谱一月内接连落子,模型工程价值重心正在转移!

一个月内三家前沿模型落子,开发方式将如何改变?一个月内,OpenAI、Anthropic、智谱先后落子。热闹之外,真正值得工程师关心的是:当模型能连续跑几个小时不掉线,我们的开发方式会变成什么样?本文先…

2026/7/10 18:23:14阅读更多 →
如何快速实施CTOSecurityChecklist:SaaS安全防护的5个简单起点

如何快速实施CTOSecurityChecklist:SaaS安全防护的5个简单起点

如何快速实施CTOSecurityChecklist:SaaS安全防护的5个简单起点 【免费下载链接】CTOSecurityChecklist The SaaS CTO Security Checklist 项目地址: https://gitcode.com/gh_mirrors/ct/CTOSecurityChecklist CTOSecurityChecklist是专为SaaS企业打造的安全防…

2026/7/10 18:23:14阅读更多 →
ADS1015L与PIC24FJ128GA310构建高精度模拟信号采集系统

ADS1015L与PIC24FJ128GA310构建高精度模拟信号采集系统

1. 项目背景与核心需求在嵌入式系统开发中,模拟信号采集是一个基础但至关重要的环节。无论是工业传感器数据读取、环境参数监测还是医疗设备信号处理,都需要将现实世界中的连续模拟信号转换为数字系统能够处理的离散数值。这个转换过程的精度和稳定性直接…

2026/7/10 19:13:18阅读更多 →
三步解锁WeMod专业版:Wand-Enhancer免费游戏修改终极指南

三步解锁WeMod专业版:Wand-Enhancer免费游戏修改终极指南

三步解锁WeMod专业版:Wand-Enhancer免费游戏修改终极指南 【免费下载链接】Wand-Enhancer Advanced UX and interoperability extension for Wand (WeMod) app 项目地址: https://gitcode.com/gh_mirrors/we/Wand-Enhancer 还在为游戏修改工具的专业版订阅费…

2026/7/10 19:13:18阅读更多 →
Java-AES-Crypto未来展望:加密技术发展趋势与路线图

Java-AES-Crypto未来展望:加密技术发展趋势与路线图

Java-AES-Crypto未来展望:加密技术发展趋势与路线图 【免费下载链接】java-aes-crypto A simple Android class for encrypting & decrypting strings, aiming to avoid the classic mistakes that most such classes suffer from. 项目地址: https://gitcode…

2026/7/10 19:13:18阅读更多 →
新员工入职第一天,我差点因为一台电脑辞职了

新员工入职第一天,我差点因为一台电脑辞职了

去年夏天,我入职了一家两百多人的公司。HR通知我早上九点半报到,领电脑、装环境、十点参加新人培训。结果呢?九点半到了,行政说“负责IT的同事今天请假了,你等一下吧”。等到十点半,IT同事远程发来一个账号…

2026/7/10 19:13:18阅读更多 →
PIC32与ADS1015L的嵌入式ADC系统设计与优化

PIC32与ADS1015L的嵌入式ADC系统设计与优化

1. 项目背景与核心需求 在嵌入式系统开发中,模拟信号采集与数字化处理是最基础也最关键的环节之一。无论是工业传感器数据采集、医疗设备信号处理,还是消费电子中的环境监测,都需要将现实世界中的连续模拟信号转换为数字系统能够处理的离散数…

2026/7/10 19:13:18阅读更多 →
生成式推荐系统HSTU模型:突破万亿参数序列建模的性能瓶颈

生成式推荐系统HSTU模型:突破万亿参数序列建模的性能瓶颈

生成式推荐系统HSTU模型:突破万亿参数序列建模的性能瓶颈 【免费下载链接】generative-recommenders Repository hosting code for "Actions Speak Louder than Words: Trillion-Parameter Sequential Transducers for Generative Recommendations" (http…

2026/7/10 19:08:18阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述:从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目,叫 skills4/skills ,它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景:一个旨在展示或教授某种技能的仓库,本身却成了安…

2026/7/10 12:10:00阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示:因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战:从“黑箱预测”到“可信推理”2026年6月,第7届机器学习与趋势国际会议(MLT 2026)将在悉尼召开。会议议程中,“因果与可解释机器学习…

2026/7/10 12:29:21阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时,通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中,是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/10 4:59:05阅读更多 →
浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比

浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比

浏览器缓存行为深度解析:Chrome/Firefox/Safari 对 304 响应的 5 种触发场景对比当你在浏览器地址栏敲入一个网址时,背后可能隐藏着一场关于"要不要重新下载资源"的精密博弈。这场博弈的裁判是HTTP缓存机制,而304状态码则是这场博弈…

2026/7/10 0:00:01阅读更多 →
RoboWits:面向创造性问题求解的双臂机器人认知推理基准

RoboWits:面向创造性问题求解的双臂机器人认知推理基准

1. 项目概述:这不是又一个机器人抓取数据集,而是一次对“思考力”的压力测试 RoboWits——这个名字里藏着两个关键信号:“Robo”直指物理世界中的具身智能体,“Wits”则毫不掩饰地指向人类最核心的认知能力:机敏、判断…

2026/7/10 0:00:01阅读更多 →
5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能

5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能

5分钟完全指南:如何使用TegraRcmGUI图形化工具解锁Switch无限可能 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI TegraRcmGUI是一款专为Windows…

2026/7/10 0:00:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/10 13:39:09阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/9 15:50:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/10 17:29:22阅读更多 →