有状态多轮对话基础设施:超越传递完整历史记录
每一个对话式 AI 功能的 Demo 都做同一件事:向模型传入一个消息列表,然后打印响应。这条快乐路径在 Jupyter Notebook 里运行顺畅、看起来很美,并让你顺利拿到上线许可。然后你到了生产环境——p99 延迟在高峰期开始悄悄爬升。一个月后,有客户投诉说助手"忘记"了会话早期的所有内容。六周后,你的会话存储在某次产品发布期间撞上了内存上限。
根本问题在于:「传递完整对话历史」根本不是一种会话管理策略,它是会话管理策略缺失的表现。
二次方陷阱
让大模型强大的注意力机制有其代价:计算开销随输入长度二次方增长。对话历史翻倍,推理成本大约翻四倍。在每秒 500 个请求、p95 目标为 1 秒的负载下,任何单个组件只要 p99 延迟达到 200ms,就会开始主导你的尾部延迟数字。测试阶段看起来可控的对话历史,会成为你没有规划到的瓶颈。
Token 爆炸比直觉预期的要快得多。一次 20 轮的对话,从消息内容、工具调用结果和助手推理中积累的原始 token 可达 5,000–10,000 个。而真正需要用来支撑下一次响应的信息,通常只有 500–1,000 个 token:用户当前的意图、建立上下文的几次近期交流,以及会话早期做出的任何明确承诺。其余的都是冗余,只会消耗延迟和金钱。
研究证实,性能退化遵循非线性曲线。前沿模型从约 1,000 个上下文 token 开始就出现可测量的准确率下降——远低于宣传的上下文窗口大小——有些模型在处理本可在 100 个 token 内完成的任务时,一旦将其埋在冗长历史中就会失败。"迷失在中间"效应进一步放大了这一问题:模型系统性地偏向输入窗口开头和结尾的 token,因此较早的轮次不只是在消耗金钱——它们还在主动稀释近期轮次的信号。
状态可以存放的三个地方
在选择压缩策略之前,你需要决定会话状态存放在哪里。这些选择并不等价。
内存存储(Redis) 提供亚毫秒级的读取延迟和内置的 TTL 管理。Redis 的语义会话管理器甚至可以利用嵌入相似度,只检索上下文相关的消息片段,而非完整历史。代价是持久性:如果会话存储重启,活跃对话就会消失。对于大多数聊天应用来说这是可以接受的;但对于任何涉及重要多步骤工作流(预订、购买、多日研究线程)的场景,则不然。
分布式数据库(DynamoDB、Postgres) 能持久化对话记录,比单个 Redis 实例更好地处理水平扩展。代价是 p99 延迟:DynamoDB 在正常负载下的读取没问题,但在高并发、大会话负载下,你会看到单元测试中不会出现的尾部延迟峰值。如果你在每次推理调用的热路径上执行会话状态检索,请根据 p99 而非平均性能来选择数据存储。
混合方案 将热态与冷态分离。当前会话的近期轮次存放在 Redis 以便快速访问;较早的对话历史、提取的事实和跨会话记忆存放在持久化存储中。热路径上的检索命中缓存;后台进程写穿到持久化层。这增加了运维复杂度,但这正是生产部署中既需要持久性又需要低延迟时实际采用的方案。
真正有效的历史压缩
核心决策在于截断、压缩与检索之间的选择。
截断 是大多数框架的默认行为:当上下文窗口填满时丢弃最早的消息。它简单、可预测,但对大多数应用来说是错误的。用户在对话最复杂的时候失去了上下文。模型会"忘记"会话早期做出的承诺。更糟糕的是,这种失败是无声的——模型不会告诉用户它已无法访问对话的早期部分。
滑动窗口 + 滚动摘要 是大多数团队最务实的方案。保留最后 N 轮的完整内容——通常是 8–15 次交流,取决于平均轮次长度——并维护所有更早内容的压缩摘要。一次辅助模型调用将最老的一批消息总结为 200–300 个 token 的浓缩上下文("用户正在为医疗客户构建数据管道;希望符合 HIPAA 合规的存储;因成本原因已决定不使用 Snowflake")。该摘要在每轮新对话开始时被前置到活跃窗口中。二次方爆 炸变得可控,因为窗口保持有界。
运维要注意:总结调用会在每第 N 轮增加延迟和成本。请为此做好预算。如果你的应用能容忍摘要尚未更新的短暂窗口期,可以将其设为异步。
选择性保留 采用更刻意的方式。在压缩前,按重要性对消息内容进行分类:
- 必须逐字保留: 用户明确的偏好、关键决策、助手的承诺、用户纠正过的内容
- 可以摘要: 常规的澄清交流、重复的问题、现已过时的上下文
- 可以丢弃: 确认回复、填充轮次、现在可以从来源检索的信息(如果你有 RAG,不需要存储检索到的内容块,只需存储检索键)
对于简单应用,重要性分类可以基于规则;对于复杂应用,可以基于大模型。一个异步运行的小型分类器模型,成本远低于将每个 token 都保留在上下文中。
检索增强历史 将对话状态完全移入向量存储。不再注入原始历史,而是对当前查询进行嵌入并检索语义相关的交流。好处是:有效无限的对话深度,上下文成本有界。代价是:检索延迟、嵌入基础设施,以及一个不那么明显的故障模式——向量搜索返回了错误的历史交流。用户问"我们关于截止日期的决定是什么?",结果检索回来的是关于预算截止日期的轮次,而他们说的是项目截止日期。
那个会搞垮你系统的 p99 会话
每个生产级 AI 应用都有一个没人规划到的 p99 会话。它是那 个因为用户有真正复杂的问题而进行了 200 轮的客服对话。它是那个在两小时自主运行中积累了工具调用输出的研究助手会话。它是那个三天后回来、却期望模型记住每个细节的用户。
朴素的方案会触碰硬性限制,API 返回错误。用户看到一条通用的失败消息,丢失了他们的工作。这是最糟糕的结果。
- https://redis.io/docs/latest/develop/ai/redisvl/user_guide/session_manager/
- https://agenta.ai/blog/top-6-techniques-to-manage-context-length-in-llms
- https://redis.io/blog/llm-token-optimization-speed-up-apps/
- https://www.getmaxim.ai/articles/context-window-management-strategies-for-long-context-ai-agents-and-chatbots/
- https://www.morphllm.com/context-rot
- https://mem0.ai/blog/llm-chat-history-summarization-guide-2025
- https://arxiv.org/html/2511.22729v1
- https://arxiv.org/html/2510.15152v1
- https://spring.io/blog/2026/04/15/spring-ai-session-management/
- https://dl.acm.org/doi/10.1145/3676641.3716245
- https://arxiv.org/pdf/2502.06975
- https://factory.ai/news/context-window-problem
