👁 65 次阅读
Semantic Kernel 聊天历史摘要器为何不生效?
来源链接:https://github.com/microsoft/semantic-kernel/issues/12303现象描述
在microsoft/semantic-kernel 的 Python 实现中,开发者把 ChatHistorySummarizationReducer 传入 ChatCompletionAgent 的 KernelArguments,期望多轮对话中历史被自动摘要压缩后再发送给 LLM。实际观察到的是:每次调用 LLM 的请求体里仍然带着完整的聊天历史,摘要器没有产生任何效果。
该 issue 的复现描述是:用 2 到 3 个 agent 构建多轮协作,在 Semantic Kernel 发起 OpenAI 兼容的 chat completion 调用时,通过 debug log 观察请求内容,可以看到完整历史被原样传递。提问者使用的模型是 gpt-4o Azure,语言为 Python,代码取自仓库 main 分支。
提问者贴出的构造方式是把 reducer 作为 chat_history 参数注入:
summarization_reducer = ChatHistorySummarizationReducer(
service=kernel.get_service(),
target_count=3,
threshold_count=2,
auto_reduce=True,
include_function_content_in_summary=True,
)
system_message = """
Summarize the chat history to reduce its size while retaining key information.
The summary should be concise, capturing the essence of the conversation without losing important details.
"""
summarization_reducer.add_system_message(system_message)
return ChatCompletionAgent(
kernel=kernel,
name='agent1',
prompt_template_config=prompt_config,
arguments=KernelArguments(
chat_history=summarization_reducer,
settings=PromptExecutionSettings(
temperature=temperature
),
),
)
期望行为是历史先被摘要,缩短后的内容再进入 LLM 请求体。issue 目前状态为 open,关闭时间与关闭原因均为空,说明官方尚未合入修复方案。
背景原因
多位参与讨论的开发者把根因指向抽象层错配,而不是某个单点代码缺陷。 维护者 TaoChenOSU 在 2025-05-30 的解释是:ChatHistorySummarizationReducer 只作用于 ChatCompletionAgent,而 orchestration 依赖的是更高层的抽象 AgentThread。当时的表述是「We will see how to make it possible to specify a thread for an agent in an orchestration」,即需要研究如何在 orchestration 中为 agent 指定 thread。
moonbox3 在 2025-05-29 说明了 reducer 的两条使用路径:调用方显式处理历史缩减;或者配置 auto_reduce=True,并且消息通过 add_message_async() 添加时自动触发。当时的结论是新的 orchestration 模式里没有处理逻辑允许传入 chat history reducer。
lawcontinue 在 2026-04-17 的分析进一步明确:ChatHistorySummarizationReducer 操作的是 ChatHistory,它在消息被添加时拦截并按阈值触发摘要;但 GroupChatOrchestration 等编排模式并不直接使用 ChatHistory,而是走 AgentThread,因此 orchestrator 管理自己的一份消息列表,从不会回调挂在 agent 的 ChatHistory 上的 reducer。
ErenAta16 在 2026-07-22 给出了一条可核实的静态检查结论:auto_reduce 只在一个方法里被兑现,而仓库中没有任何地方调用那个方法。其给出的 grep 结果显示,add_message_async 只出现在 python/semantic_kernel/contents/history_reducer/chat_history_reducer.py 中,作为 auto_reduce 的描述文本出现,而没有真正的调用点。讨论中引用的原文片段为:
$ grep -rn "add_message_async" python/semantic_kernel/ python/semantic_kernel/contents/history_reducer/chat_history_reducer.py:28: description="Whether to automatically reduce the chat history, this happens when using add_message_async.", python/sema
这意味着即便以最理想的方式配置,auto_reduce 依赖的触发路径本身没有被走到。
排查或量化方法
agent-morrow 在 2026-03-29 的评论给出了一条无需改框架的诊断思路:对比 reducer 挂载前后每次 API 调用的 token 计数。如果ChatHistorySummarizationReducer 是静默地没有触发(fall through),那么在历史长度已经超过阈值的情况下,token 计数仍然完全一致。这条对比能直接区分「reducer 生效但压缩不足」和「reducer 根本没被调用」两种情形,是定位该问题成本最低的观测方式。
该评论还提到,同一现象在 2026 年初的多 agent group chat 场景中依然存在:reducer 接受配置时不报错,实际 API 调用仍包含完整历史。
从抽象层角度切入的排查方式是确认消息写入路径。若编排模式不是通过被 reducer 拦截的那条消息添加路径写入历史,那么无论阈值设成多少,摘要逻辑都不会被触发。ErenAta16 的 grep 结论可以直接用于这一判断:如果代码路径中不存在对 add_message_async 的调用,auto_reduce=True 就没有生效条件。
完整解决方案(含必要代码)
该问题截至末条实质评论仍为 open,官方未合入修复。以下内容区分「社区已提出的路径」与「社区实际采用的规避方案」,均标注来源。 moonbox3 在 2025-05-29 给出的两条路径是框架侧已有的设计:- 调用方在把历史交给 LLM 之前自行显式缩减;
- 配置
auto_reduce=True,并通过add_message_async()添加消息,让 reducer 自动触发。
add_message_async,因此第二条路径在当前代码里不具备触发条件。这是「自动」这一选项名义存在、实际不可用的原因。
agent-morrow 在 2026-03-29 提出了一种包裹方案:包装 SK 的 reducer,在每次压缩前后快照行为状态。其公开的 MonitoredChatHistory 可作为 ChatHistory 的 drop-in 替代,并配 BehavioralSummaryReducer / BehavioralTruncationReducer 包装器。讨论中给出的代码开头为:
MonitoredChatHistory
这段代码在 issue 中的摘录不完整,完整实现见其仓库链接(事实依据中写出的为 https://github.com/agent-morrow/morrow/blob/main/tools/compression-monitor/semantic_kernel_integration.py)。据 issue 讨论,该方案的作用是把 reducer 的实际触发状态暴露出来,而不是修复 orchestration 绕过 reducer 的问题。
ferhimedamine 在 2026-06-12 给出的生产环境做法是把记忆彻底移出聊天历史:使用外部 memory service 管理上下文。其观察是编排层管理自己的消息列表,从不回调挂在 agent ChatHistory 上的 reducer,所以基于摘要的 reducer 操作的是一个被 orchestrator 完全绕过的数据结构。
renezander030 在 2026-07-28 指出了一个更根本的约束:讨论中提出的两个修复方向(async add_message 和 _on_new_message 中的切换)都让缩减停留在「建议性」而非「强制性」——缩减始终是历史变更路径上的钩子,任何以其他方式组装消息的编排都会再次静默跳过。其后果是 reducer 空转时不报错,第一个信号往往出现在账单或上下文长度失败上,可能滞后数周。
moonbox3 在 2026-04-29 的回复是邀请提交 PR:Please feel free to raise a PR with a fix, and we'll review it.截至目前 issue 仍为 open,没有合并记录。
参数或对比表格
| 方案 | 提出者与时间 | 是否能修复编排绕过 reducer 的问题 | 局限 |
| 调用方显式缩减历史 | moonbox3 2025-05-29 | 取决于调用方实现 | 每条调用路径都要自己处理,容易遗漏 |
auto_reduce=True + add_message_async() | moonbox3 2025-05-29 | 名义上可自动触发 | 据 ErenAta16 2026-07-22 的 grep,仓库中无任何方法调用 add_message_async |
| 包装 reducer 并监控压缩前后状态 | agent-morrow 2026-03-29 | 不修复,仅可观测 | 用于确认 reducer 是否静默跳过,不改变编排行为 |
| 外置 memory service | ferhimedamine 2026-06-12 | 绕开聊天历史这一数据结构 | 属于架构层改动,不再是框架内配置 |
| 让缩减从建议性变为强制性 | renezander030 2026-07-28 | 方向上可覆盖所有编排 | 属于设计层讨论,尚无对应实现 |
常见错误排查
配置了auto_reduce=True 但请求体仍是完整历史。据 ErenAta16 2026-07-22 的 grep 结果,auto_reduce 只在一个方法中被兑现,而仓库里没有该方法的调用点,因此这个布尔值当前不产生触发。
reducer 接受配置且不抛错。agent-morrow 2026-03-29 指出,静默 fall through 的表现就是如此,token 计数不会发生任何变化。需要按上述方式对比挂载前后的 token 计数来确认。
在 GroupChatOrchestration 中挂 reducer 无效。lawcontinue 2026-04-17 的分析说明,orchestration 不直接使用 ChatHistory,而是走 AgentThread,挂在 agent ChatHistory 上的 reducer 不会被回调。
认为通过配置 thread 即可解决。TaoChenOSU 2025-05-30 表示需要在 orchestration 中为 agent 指定 thread 的能力,措辞是「will see how to make it possible」,属于待研究事项,不是已可用配置。
只在单个 ChatCompletionAgent 上使用。据 TaoChenOSU 的同一条评论,reducer 当前只对 ChatCompletionAgent 起作用,单 agent 场景与多 agent 编排场景的行为不同。
实践建议
在官方合入修复之前,把上下文压缩从 reducer 配置改为调用前的显式步骤,是最直接的做法。moonbox3 描述的「调用方显式处理」这条路径在当前代码里仍然成立。 如果使用多 agent 编排,不要依赖挂在 agentChatHistory 上的 reducer。lawcontinue 与 ferhimedamine 的分析都指向同一个事实:编排层走 AgentThread 并管理自己的消息列表,这个数据结构与 reducer 拦截的数据结构不是同一个。
需要用观测手段替代信任。agent-morrow 给出的 token 计数对比方法成本很低,可以在接入任何 reducer 配置后先做一次验证,避免 reducer 空转数周后才由账单或上下文长度错误暴露。
把「缩减是否被强制执行」纳入选型判断。renezander030 的评论指出,钩子式的缩减方案要求每一个未来的编排都记得走同一条消息变更路径,漏掉时没有任何报错。依赖外部 memory service 的做法(ferhimedamine 2026-06-12)牺牲的是框架内配置的简洁性,换来的是不依赖编排实现的确定性。
FAQ
这个 bug 修复了吗? 没有。issue 状态为 open,关闭时间与关闭原因均为空。末条实质评论仍在讨论建议性与强制性缩减的设计差异,官方尚无合入记录。auto_reduce=True 为什么没有触发摘要?
据 ErenAta16 2026-07-22 在 main 分支上的 grep 结论,auto_reduce 只在一个方法里被兑现,而仓库中没有任何地方调用那个方法。触发路径不存在,配置自然不会产生效果。
为什么单个 agent 能用,放进 GroupChatOrchestration 就不行?
据 TaoChenOSU 2025-05-30 与 lawcontinue 2026-04-17 的分析,reducer 只作用于 ChatCompletionAgent,而 orchestration 依赖 AgentThread 这一更高层抽象,orchestrator 管理自己的消息列表,不会回调 reducer。
怎么确认自己的场景是不是同一个问题?
按 agent-morrow 2026-03-29 的方法,对比 reducer 挂载前后每次 API 调用的 token 计数。如果历史已超阈值而 token 计数完全一致,说明 reducer 被静默跳过。
官方打算怎么修?
moonbox3 2026-04-29 的回复是欢迎提交 PR 并会 review。TaoChenOSU 2025-05-30 提到需要研究在 orchestration 中为 agent 指定 thread 的能力,属于方向性描述,没有给出时间表。提问者 pintuiitbhi 在 2025-06-10 追问过 ETA 并表示被阻塞,未获得具体排期。
🔗 相关阅读(你可能还想看)
📄 Outlook 节点创建草稿添加附件为何报 UnableToDeserializePostBody?
📄 Flowise 点击市场或聊天页为何整页白屏?
📄 如何解决 Azure AI Search 向量存储的 $filter 递归深度超限错误?
📄 如何解决 shadcn/ui Sidebar 与 Header 重叠的问题?
📄 Next.js 开发服务器为何会 JavaScript heap out of memory?
📄 Nuxt 偶发 Cannot read properties of undefined 报错是怎么回事
📄 如何解决「Visual Studio Code for ipad」相关的问题?
📄 如何在AutoGen中实现企业级代理治理的加密行动收据(AAR)?
📄 记忆内容未清洗直接注入系统提示词,如何防范间接提示注入?
crewAI LiteAgent 将记忆内容直接拼接到系统提示词,存在间接提示注入风险。社区提出分层防御方案,但 iss