Agent-snapshot-text-重复拼接根因
## 1. 问题背景
by 于成季
·
知与行
CloudBase
1. 问题背景
Case 001 测试发现:confirm 两轮流程跑通后,snapshot_text_join(events) 返回的内容重复了 3 次:
[工具成功] create_insight\n{"success": true, ...}\n✅ 感悟已保存!\n[工具成功] create_insight\n{"success": true, ...}\n✅ 感悟已保存!\n[工具成功] create_insight\n{"success": true, ...}\n✅ 感悟已保存!
实际 SSE 中 MESSAGES_SNAPSHOT 只有 1 条、最终文本正确,但 snapshot_text_join 拼接了多个 STATE_SNAPSHOT 导致重复。
2. 问题列表
2.1 snapshot_text_join 遍历所有 STATE_SNAPSHOT 导致内容重复
根因(链式证据):
- why1(直接):
snapshot_text_join用for ev in events遍历所有STATE_SNAPSHOT事件,将每条消息 content 拼接到 parts 列表。 - why2(机制):confirm 流程经历
intent_recognition → execute_tools → finalize → END,LangGraph SDK 在每个节点出口都会打一次STATE_SNAPSHOT(共 4 个)。 - why3(触发点):SDK 源码
ag_ui_langgraph_patch.py:566-576:节点切换且状态有差异时触发 STATE_SNAPSHOT;ag_ui_langgraph_patch.py:614-615:END 后额外打一次最终快照。 - why4(内容相同):4 个快照中前 3 个 messages 逐条递增(0 → 1 → 2),最后一个与第三个内容完全相同。遍历全部导致同一文本出现多次。
方案:
- 短期方案:只取最后一个 STATE_SNAPSHOT 的消息内容。
snap = snaps[-1].get("snapshot"),加 docstring 说明"过程中会产生多个 STATE_SNAPSHOT"。 - 长远方案:重新设计 SSE 事件结构,由 SDK 提供唯一最终快照事件,而非依赖客户端过滤。
采纳方案:
sse_util.py 中 snapshot_text_join 改为取 snaps[-1](最后一个 STATE_SNAPSHOT),并补充 docstring 说明原因。
2.2 测试未覆盖 SSE 事件数量场景
根因:
测试用例 test_create_insight_two_round 断言了 TOOL_CALL_RESULT 存在和最终文本非空,但未验证 snapshot_text_join 的返回格式正确性,无法发现重复拼接问题。
方案:
- 短期方案:直接修复
snapshot_text_join实现,对齐正确行为。 - 长远方案:增加 SSE 事件结构层面的断言(如
MESSAGES_SNAPSHOT唯一性)。
补充用例:
现有用例已足够覆盖(修复后再次运行 test_create_insight_two_round 通过)。
3. 小结
- 核心修复:
snapshot_text_join只取最后一个 STATE_SNAPSHOT,避免多快照拼接导致内容重复。 - 根因理解:LangGraph confirm 流程产生多节点 STATE_SNAPSHOT 是正常行为(每步出口各一 + END 后最终快照),客户端必须正确取最终快照而非遍历全部。
- 参考:
tests/sse_util.py、cloudbase_agent/langgraph/ag_ui_langgraph_patch.py