核心思想就一句话:模型永远看不到"完整历史",完整历史只存在于存储层,喂给模型的是一份"被精心裁剪过的上下文"。这是所有商用产品(ChatGPT、Claude、客服机器人、企业知识助手)的共识,只是裁剪策略不同。
一、商用系统的三层记忆模型
几乎所有成熟产品都把"历史"拆成三层,职责完全分离:
关键区别:Layer 0 是"给你看的、可回放的",Layer 1 才是"给模型算的"。两者数据结构不同、存储不同、生命周期不同。Layer2 其实可以理解成:skills、用户画像、系统的全局设置等等。
二、长对话具体怎么处理:滚动摘要(Rolling Summary)
这是商用系统应对"上万条"的标准手法:
- 对话初期:直接把最近 N 轮塞进上下文。
- 当对话变长、逼近上下文窗口上限时:把最旧的一批轮次交给 LLM 压缩成一段紧凑摘要,钉在 prompt 顶部(常作为一条 SystemMessage 或特殊标记)。
- 之后每轮:上下文 = "一段滚动摘要" + "最近若干轮",旧原始消息持续被吞进摘要里。
- 模型对整段对话的"记忆"= 摘要 + 近期窗口,而非原始全量。
ChatGPT 的"记忆"和 Claude 的会话压缩,本质上都是这个机制;差别只是触发阈值和摘要粒度。
三、几个真实产品的做法对照
| 产品 | 完整历史 | 工作上下文 | 跨会话长期记忆 |
|---|---|---|---|
| ChatGPT | 全量存 DB,UI 可回看 | 近期窗口 + 滚动摘要 | 独立 "Memory" 功能,提取用户事实,用户可查看/编辑/删除 |
| Claude | 会话存库 | 窗口极大(20 万 token)+ 超长时摘要 | "Projects" 挂载文件作为持久上下文 |
| 客服机器人(银行/电商) | 按工单归档 | 仅本次会话窗口(20~50 条) | 从 CRM 检索身份/订单,不靠聊天历史 |
| 企业知识助手(RAG) | 几乎不用聊天历史 | 近期窗口做连贯性 | 知识从向量库检索,历史只是"对话粘性" |
共性:没有一家会把完整历史直接塞给模型——那会超 context、烧钱、且噪声大。
四、生产环境必补的运营考量
商用和 demo 的差距,往往不在"能对话",而在这些:
- 成本控制:大段摘要 + System Prompt 每轮重复发送很贵。生产会用 Prompt Caching / KV Cache(OpenAI、Anthropic 都支持)把不变的"摘要+系统提示"缓存,只增量发新消息。
- 隐私与合规:留存 TTL、用户删除权("被遗忘权")、PII 隔离、多租户行级安全。你代码里 ttl-seconds: 0(永不过期)在生产必须改。
- 延迟:绝不在每轮请求里序列化/反序列化一个巨大的 blob;存储层要支持分页和增量追加。
- 可观测性:token 用量、摘要触发次数、超窗口丢弃条数都要有指标。
五、给你的生产级落地路线
- 加滚动摘要(Rolling Summary) —— 在现有 Redis 上最快的"长期记忆"升级:消息超阈值就把旧的压成摘要,Redis 恒定有界且能记住更早上下文。
- 拆存储与上下文 —— 完整历史移到支持分页的存储(Redis 也可用独立 key + 列表结构,或 Postgres),工作上下文只取窗口+摘要。
- 加 Prompt Caching + TTL —— 直接省成本 + 合规。零算法难度。
- (可选)跨会话语义记忆 —— 抽取用户事实存独立表,每次注入 System。对应 Layer 2。
六、两套历史L1、L2的理解
| L0 全量历史 | L1 工作上下文(模型看到的) | |
| 是什么 | 你说的每一句话,原封不动 | 窗口 + 摘要 + 画像 |
| 给谁用 | 给人回看/审计 | 给大模型 |
| 会进 prompt 吗 | 永远不进 | 就是 prompt 本身 |
| 有多大 | 可以上万条 | 恒定有界(≤16 条 + 一段摘要) |
| 存哪 | chat:history:{convId} | chat:memory: + chat:summary: + chat:facts: |
用户说 10000 句,L0 存 10000 句,但模型每轮只看到"一段摘要 + 最近十来句"。 你在 UI 点"查看历史"看的是 L0;模型永远不会读到那么长。
七、五个 Redis key 各存什么
八、一次对话流程图
关键顺序:先落 L0(①③),再动 L1(④)。L0 是"记账",L1 是"做题时手里那几张纸"。
九、摘要生成标准
配置:maxMessages=10、summaryTrigger=6,所以上限 = 10 + 6 = 16 条。
规则只有一条:窗口里的消息一旦超过 16 条 → 一次性把最旧的 (超出部分) 挤出去,保留最近 10 条,并把挤出去的那些交给 LLM 压成摘要。
用"每轮加 2 条"简化走一遍:
所以摘要不是每轮都生成,而是"攒够一批才生成一次"。这就是为什么它是有成本的、也是可控的。
- 摘要是"很久以前发生过什么"的浓缩。
- 画像是"这个用户是谁、偏好什么"。
- 窗口是"刚才几轮的原话"。
三者拼起来,模型既有近期细节,又有远期记忆,还不用把上万条塞进去。
类比:历史是会议全程录像,摘要是会议纪要。模型只看纪要 + 最近十分钟。