Skip to content

perf: prevent context bloat and improve cache hit rate#389

Open
Duskydream wants to merge 1 commit into
TencentCloud:mainfrom
Duskydream:fix-context-bloat-and-cache
Open

perf: prevent context bloat and improve cache hit rate#389
Duskydream wants to merge 1 commit into
TencentCloud:mainfrom
Duskydream:fix-context-bloat-and-cache

Conversation

@Duskydream

Copy link
Copy Markdown

• 将全量记忆标签替换为轻量级指针,防止历史记录膨胀,同时保留追溯痕迹。
• 增加 Session 级的系统提示词去重逻辑,最大化前缀缓存(Prefix Cache)的命中率。

Description | 描述

本 PR 引入了两个核心优化,旨在彻底解决由于记忆叠加导致的上下文膨胀问题,以及由此引发的大模型前缀缓存(Prefix Cache)失效和打穿 Token 上限的恶性 Bug。

  1. 轻量级记忆指针 (Lightweight Memory Pointers)
    我们在 before_message_write 钩子中,不再简单粗暴地将记忆替换为空字符串,而是将其替换为极短的追踪标签
    。这既防止了历史记录因为数千字的记忆片段被不断固化而呈指数级膨胀,又在历史对话中保留了“这里曾发生过记忆召回”的痕迹,方便后期调试与回溯。

  2. Session 级系统提示去重 (System Prompt Deduplication)
    在多轮对话中,重复注入的系统消息(如 )会打乱文本序列并浪费 Token。本 PR 在 before_prompt_build 钩子阶段,自动扫描 event.messages 数组,利用 Set 对 role: 'system' 的消息进行内容级去重过滤,保证大模型收到的前缀永远是最标准且无冗余的。

Related Issue | 关联 Issue

Fix #120

Change Type | 修改类型

[✓] Bug fix | Bug 修复
[ ] New feature | 新功能
[ ] Documentation update | 文档更新
[✓] Code optimization | 代码优化

Self-test Checklist | 自测清单

[✓] Verified locally | 本地验证通过
[✓] No existing features affected | 无影响现有功能

Additional Notes | 其他说明

本地实验与监控数据对比 (基于 DeepSeek API + 代理打点监控):

为了验证此 PR 的有效性,我们在本地编写了 Node.js 代理劫持流量,对大模型实际的 Cache 命中率和 Token 消耗进行了对照实验:

❌ 优化前(Bug 状态 / 记忆被固化至历史):

• 上下文爆炸:由于每轮长达 1500+ Token 的 被当作用户真实输入写入历史,多轮对话后上下文呈现 O(N²) 级膨胀。
• 缓存雪崩:动态变化的记忆片段打断了 LLM 的前缀匹配。
• 实测数据 1: [单次请求] Prompt: 17229 | 缓存命中: 0 (0.00%)
• 实测数据 2: [单次请求] Prompt: 8892 | 缓存命中: 0 (0.00%)
• 现象:稍长的对话就会导致历史命中率断崖式暴跌至 0%,模型被迫重新计算上万 Token,导致严重延迟甚至触发限流。

✅ 优化后(应用本 PR 的指针压缩 + 去重):

• 彻底遏制膨胀:历史记录中只保留轻量指针。
• 实测数据:连续两轮长请求的 Prompt 总量从 18482 仅微增至 18786 (单轮真实增量仅为 +304 Tokens,彻底告别每轮上千 Token 的垃圾冗余)。
• 命中率极速飙升:历史前缀序列保持绝对纯净和稳定。
• 长文本实测 1: [单次请求] Prompt: 23687 | 缓存命中: 19328 (81.60%)
• 长文本实测 2: [单次请求] Prompt: 22877 | 缓存命中: 18816 (82.25%)
• 短文本实测: [单次请求] Prompt: 5639 | 缓存命中: 5248 (93.07%)
• 最终结果:全局(超 37 万 Token 测试规模)累计命中率稳定在 75% ~ 85% 的高水位。

flowchart TD
    %% 优化前的状态
    subgraph Before["❌ 优化前:记忆固化导致缓存雪崩 (Cache Miss)"]
        direction TB
        B_Sys["System Prompt (系统设定)"]:::hit --> B_T1["Turn 1 历史\n(冻结了 1500+ 字的动态记忆 A)"]:::hit
        B_T1 --> B_T2["Turn 2 历史\n(冻结了 1500+ 字的动态记忆 B)"]:::miss

        B_T2 -. "🚨 前缀发生突变!\n大模型发现记忆 B 与前缀不符\n匹配在此断裂" .-> B_T3["Turn 3 历史\n(无限膨胀)"]:::miss
        B_T3 -. "🔥 彻底阻断!\n后续几万 Token 全部需要重新计算" .-> B_Curr["当前轮问题"]:::miss
    end

    %% 优化后的状态
    subgraph After["✅ 优化后本 PR:轻量指针 + 去重 (Cache Hit = 85%+)"]
        direction TB
        A_Sys["System Prompt\n(经过 Session 级去重,绝对纯净)"]:::hit --> A_T1["Turn 1 历史\n<memory-omitted />"]:::hit
        A_T1 --> A_T2["Turn 2 历史\n<memory-omitted />"]:::hit
        A_T2 --> A_T3["Turn 3 历史\n<memory-omitted />"]:::hit

        A_T3 -. "✅ 历史记录短小精悍且无突变\n前缀完美匹配!" .-> A_Curr["当前轮问题\n(仅在底部注入当轮最新记忆)"]:::dynamic
    end

    %% 样式定义
    classDef hit fill:#e8f5e9,stroke:#4caf50,stroke-width:2px,color:#000
    classDef miss fill:#ffebee,stroke:#f44336,stroke-width:2px,stroke-dasharray:5 5,color:#000
    classDef dynamic fill:#fff3e0,stroke:#ff9800,stroke-width:2px,color:#000
Loading

结论:
本 PR 在不牺牲记忆追溯能力的前提下,完美修复了上下文爆炸问题,大幅降低了 API 计费成本与推理延迟。

- Replace full memory tags with lightweight pointers to prevent history bloat while preserving traces.

- Add session-level deduplication for system prompts to maximize prefix cache hit rate.
@Maxwell-Code07

Copy link
Copy Markdown
Collaborator

Thank you for submitting this PR and participating in Tencent Rhino-bird Open-source Training Program!
We have successfully received your submission. The program is currently in full swing, and we will complete the Code Review for you as soon as possible. Please keep an eye on the status notifications for this PR so you can follow up promptly once the review feedback is provided.
Thanks again for your contribution and open-source spirit! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

prependContext + showInjected 导致 OpenAI-compatible provider 前缀缓存命中率退化 / Prompt cache hit rate regression

2 participants