news 2026/4/16 12:37:45

使用 IChatReducer 进行聊天记录缩减

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 IChatReducer 进行聊天记录缩减

序言

在多轮对话场景中,随着聊天次数增加,发送给大语言模型(LLM)的上下文会持续膨胀,带来 Token 成本上升与上下文溢出风险。 Microsoft Agent Framework 将这一问题抽象为 Chat Reduction(聊天记录缩减),并通过IChatReducer策略对聊天历史进行统一治理,而不是在业务代码中零散地裁剪或拼接历史消息。

本文基于“客户端本地存储聊天记录(Client-side history)”的典型场景,演示如何使用MessageCountingChatReducer自动限制历史长度,防止上下文无限增长,并观察在“历史被遗忘”后 Agent 行为的变化。


1. 代码关键实现步骤

引入必要的依赖

  • Microsoft.Extensions.AI:提供统一的 AI 抽象(ChatMessageReducer等)

  • Azure.AI.OpenAI:用于连接 Azure OpenAI 服务

  • Microsoft.Agents.AI:Agent Framework 核心能力

配置 Agent 与缩减策略(Reducer)

AIAgent agent = new AzureOpenAIClient( new Uri(endpoint), new AzureCliCredential()) .GetChatClient(deploymentName) .CreateAIAgent(new ChatClientAgentOptions { ChatOptions = new() { Instructions = "你是一位江湖说书人,擅长用幽默、接地气的方式讲笑话和故事。" }, Name = "Joker", // 关键点:自定义 ChatMessageStoreFactory ChatMessageStoreFactory = ctx => new InMemoryChatMessageStore( new MessageCountingChatReducer(2), // 仅保留最近 2 条 非 System 的 ChatMessage ctx.SerializedState, ctx.JsonSerializerOptions) });

组件说明

  • InMemoryChatMessageStore

    • 聊天记录保存在客户端内存中

    • 适用于 Chat Completion / 本地上下文管理场景

  • MessageCountingChatReducer(2)

    • 基于“消息数量”的缩减策略

    • 参数 2 表示仅保留最近 2 条非系统消息( 非 System 的 ChatMessage)

    • 超出部分的历史消息会被自动移除,而不是无限累积


2. 验证缩减效果

通过多轮连续对话,观察聊天记录在 Reducer 作用下的变化。在每一轮调用agent.RunAsync(...)后,读取当前线程中实际保留的聊天历史数量:

AgentThread thread = agent.GetNewThread(); Console.WriteLine(await agent.RunAsync("给我讲一个发生在茶馆里的段子,轻松一点的那种。", thread)); IList<ChatMessage>? chatHistory = thread.GetService<IList<ChatMessage>>(); Console.WriteLine($"\n 聊天有 {chatHistory?.Count} 消息.\n"); // Invoke the agent a few more times. Console.WriteLine(await agent.RunAsync("现在把这个段子加上一些表情符号,并用说书人的语气再讲一遍。", thread)); Console.WriteLine($"\n 聊天有 {chatHistory?.Count} 消息.\n"); Console.WriteLine(await agent.RunAsync("保持刚才的语气,讲一个关于健忘冒险者的轻松小故事,像是在讲笑话一样。", thread)); Console.WriteLine($"\n 聊天有 {chatHistory?.Count} 消息.\n"); // At this point, the chat history has exceeded the limit and the original message will not exist anymore, // so asking a follow up question about it will not work as expected. Console.WriteLine(await agent.RunAsync("接着刚才的氛围,讲一个发生在日常生活里的小乌龙事件,轻松随意一点。", thread)); Console.WriteLine($"\n 聊天有 {chatHistory?.Count} 消息.\n");

对话过程说明

  • 第一轮对话:「给我讲一个发生在茶馆里的段子,轻松一点的那种。」

    • 聊天历史较短,Reducer 尚未触发,历史消息正常累积

  • 第二轮对话:「现在把这个段子加上一些表情符号,并用说书人的语气再讲一遍。」

    • 新消息加入,历史仍在阈值范围内,早期消息仍可访问

  • 第三轮及之后:「保持刚才的语气,讲一个关于健忘冒险者的轻松小故事。」

    • 聊天记录达到缩减条件,MessageCountingChatReducer开始生效

    • 最早的消息被自动移除,chatHistory.Count保持在稳定范围内


3. 演示结果

结果一

结果二

结果三

结果四


4. 技术总结与适用场景

适用场景

  • Client-side history:聊天历史由客户端或应用自身维护(如 OpenAI / Azure OpenAI Chat Completion API)

不适用场景

  • Server-side history(如 Azure Foundry Agents):聊天历史由服务端统一管理,客户端无法直接干预裁剪策略

可扩展性

IChatReducer只是一个策略接口,可扩展更复杂的上下文治理逻辑:

  • TokenCountingChatReducer:按 Token 数量而非消息条数进行缩减

  • SummaryChatReducer:将旧消息压缩为摘要,而非直接删除

小结

  • 聊天历史不应无限增长

  • “遗忘”是一种主动、可控的系统设计

  • 上下文治理应以策略形式存在,而非散落在业务代码中

  • 合理使用 Chat Reduction,可在成本、稳定性与对话效果之间取得更好平衡

源代码地址

https://github.com/bingbing-gui/aspnetcore-developer/tree/master/src/09-AI-Agent/Agent-Framework/15-ChatReduction

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/2 4:55:35

工业自动化中图像资源管理:LCD Image Converter实用技巧

工业HMI图像优化实战&#xff1a;用好LCD Image Converter&#xff0c;让嵌入式显示更高效你有没有遇到过这样的场景&#xff1f;精心设计的HMI界面在PC上预览效果惊艳&#xff0c;烧录到STM32或类似MCU后却卡顿明显&#xff0c;甚至因为几幅背景图导致Flash爆满、编译失败。更…

作者头像 李华
网站建设 2026/4/11 21:39:19

Altium中如何创建原理图符号:零基础手把手教学

在Altium中创建原理图符号&#xff1a;从零开始的实战指南你有没有遇到过这样的情况&#xff1f;手头有个新买的传感器&#xff0c;型号是SHT45&#xff0c;项目急着要画板子&#xff0c;打开Altium Designer准备建个元件——结果搜了一圈&#xff0c;官方库、第三方库全都没有…

作者头像 李华
网站建设 2026/4/9 19:21:43

手把手教你搭建vh6501测试busoff实验平台

手把手搭建vH6501 Bus-Off测试平台&#xff1a;从原理到实战你有没有遇到过这样的场景&#xff1f;某款ECU在实车路试中突然“失联”&#xff0c;仪表盘亮起通信故障灯&#xff0c;但返厂复现却一切正常。排查数周后才发现&#xff0c;是某个节点短暂进入Bus-Off状态导致的瞬时…

作者头像 李华
网站建设 2026/4/12 12:25:20

从零实现aarch64中断控制器配置(GICv3)实战案例

手把手实现 aarch64 平台 GICv3 中断控制器配置 从一个“无中断可用”的裸机困境说起 你有没有遇到过这样的场景&#xff1a;刚写完一段 aarch64 裸机启动代码&#xff0c;UART 已经能打印 Hello World &#xff0c;但外设一触发中断——系统毫无反应&#xff1f;调试器里看寄…

作者头像 李华