
1. 面试题背后的真实挑战为什么“分层记忆”是LLM应用的核心最近在准备大模型应用架构相关的面试看到这个“阿里二面”的题目感觉非常有意思也很有代表性。题目是“8K Token 撑住 100 轮对话你的分层记忆架构怎么设计” 这短短一句话其实包含了当前大模型应用落地的几个核心痛点有限的上下文窗口、长对话的连贯性需求、以及成本与性能的平衡。我们先来拆解一下题面。8K Token这通常指的是模型单次推理一次请求所能处理的最大上下文长度。对于GPT-3.5 Turbo、一些开源的7B/13B模型来说8K是一个常见的配置。而“100轮对话”意味着整个会话历史可能远远超过8K Token。如果我们粗暴地把所有历史对话都塞进每一次的请求里很快就会触达上下文长度上限导致最早的对话内容被“遗忘”被截断或者直接请求失败。所以这道题本质上是在考察如何在一个固定且有限的“工作内存”8K上下文中智能地管理一个远超其容量的“长期记忆”100轮对话历史以确保对话的连贯性、相关性和高效性。这绝不仅仅是简单的“历史记录截取”而是一个需要精心设计的记忆管理系统。一个好的分层记忆架构能让人机对话感觉更像和一个真正的人在聊天而不是一个每说几句就失忆的机器。在实际的产品中无论是智能客服、AI伴聊、游戏NPC还是编程助手只要涉及多轮深度交互都会遇到这个问题。设计不当用户体验会急剧下降比如AI突然忘了用户的名字、搞错了之前约定的需求、或者对几分钟前讨论过的细节一无所知。因此分层记忆架构是构建可用、好用的大模型对话系统的基石。2. 记忆分层的核心思想从“全量加载”到“智能缓存”在深入设计之前我们必须摒弃“把所有东西都记住”的朴素想法。人类的记忆本身也是分层的我们有瞬间记忆、短期记忆和长期记忆并且大脑会主动进行信息的筛选、压缩和提取。我们的架构设计也需要借鉴这种思想。一个典型的分层记忆架构通常包含以下三层原始对话记录层Archive这是最底层的全量存储。所有100轮对话的原始记录包括用户输入和AI回复都被安全地存储在数据库如PostgreSQL, MongoDB或向量数据库如Chroma, Weaviate中。这一层是完整的“事实”来源但不对模型直接可见。长期记忆/摘要层Long-term Memory / Summary这是经过加工和压缩的记忆。我们不会保存每一句话而是定期例如每10轮对话或当检测到话题转换时对之前的对话内容生成一个高度凝练的摘要。这个摘要本身只占用几十到几百个Token却承载了核心事实、用户偏好、达成的一致结论等关键信息。它是连接“原始记录”和“工作上下文”的桥梁。工作记忆/上下文层Working Memory / Context这是直接喂给大模型的8K Token窗口内的内容。它由几个动态部分拼接而成系统指令System Prompt、本轮用户问题、相关的长期记忆摘要、以及最近几轮最相关的原始对话记录Short-term Memory。这个分层结构的核心工作流是当用户发起新一轮对话时系统不是把100轮历史全部装入上下文而是执行一个“记忆检索与组装”流程从长期记忆层加载最新的全局摘要。根据用户当前的问题从原始记录层中检索出最相关的若干轮近期对话短期记忆。将这些元素连同系统指令和当前问题智能地组装进不超过8K Token的工作记忆层最后发送给大模型生成回复。这样我们用极小的上下文开销主要用来装摘要和最近几轮对话就实现了对超长对话历史的“感知”和“理解”。3. 架构设计实战组件、流程与数据流现在我们来具体设计这个架构。我会用一个相对通用的技术栈来举例你可以根据实际情况替换组件。3.1 核心组件定义记忆存储器Memory Store向量数据库如Chroma用于存储每一轮对话的向量化嵌入Embedding。我们将用户输入和AI回复作为一个“记忆单元”通过文本嵌入模型如text-embedding-3-small转换为向量。这方便我们后续进行语义检索。传统数据库如PostgreSQL用于存储对话的原始文本、元数据时间戳、会话ID、以及生成的长期记忆摘要。关系型数据库在存储结构化摘要信息和进行简单查询时更可靠。记忆管理器Memory Manager这是架构的大脑是一个后台服务可以用Python/Node.js编写。它负责所有记忆相关的逻辑生成嵌入、存储记忆、触发摘要、检索相关记忆、组装上下文。它需要暴露关键的API如add_memory(session_id, user_input, ai_response),get_context_for_query(session_id, current_query, max_tokens)。摘要生成器Summarizer可以是大模型本身如GPT-4也可以是一个专门的摘要模型。它的任务是将一段对话历史可能是原始文本也可能是上次摘要加新对话压缩成一个连贯、信息密度高的段落。指令可能是“请将以下对话总结为一个简洁的段落保留关键事实、用户决策和偏好。”3.2 单轮对话的数据处理流程让我们跟踪一轮新对话发生时系统的内部状态变化。记忆写入 用户提问AI回复后记忆管理器会创建一个记忆单元{user: “...”, assistant: “...”, timestamp: ...}。将原始文本存入PostgreSQL。将文本例如将用户问题和AI回复拼接通过嵌入模型转换为向量并存入向量数据库同时关联该向量的元数据指向PostgreSQL中的原始记录ID。摘要触发与更新异步 记忆管理器会维护一个计数器或基于规则检查是否需要更新长期摘要。规则可以是轮数驱动每N轮对话后例如N10。话题转换驱动利用嵌入向量计算最新几轮对话与当前长期摘要的余弦相似度如果相似度低于某个阈值则认为话题已转换触发摘要。关键信息驱动通过一个轻量级模型或规则如检测到用户说“记住我喜欢…”标记为重要信息立即触发摘要更新。 触发后记忆管理器会调用摘要生成器。输入通常是“旧的长期摘要 自上次摘要以来的所有新对话记录”输出是新的、更新的长期摘要。然后将这个新摘要存入PostgreSQL替换或版本化旧的摘要。3.3 上下文组装回答新问题时的检索策略当用户提出下一个问题时第101轮系统需要为模型准备上下文。这是最关键的步骤。加载基础上下文首先固定部分包括系统指令例如“你是一个有帮助的助手…”这可能占用200-500 Token。加载长期记忆从PostgreSQL中取出当前会话的最新长期记忆摘要。假设它非常精炼只占150 Token。检索相关短期记忆将用户的当前问题进行向量化。在向量数据库中针对该会话ID下的所有历史记忆单元进行相似度搜索如余弦相似度找出与当前问题最相关的K条记录例如K5。这就是基于语义的检索它能找到那些字面上不匹配但意思相关的历史对话。同时我们通常还会强制加入最近N轮对话例如N3无论语义是否相关。这是为了保证对话最直接的连贯性避免出现“你刚刚才说过A现在却好像不知道A”的断层。这称为时间邻近检索。上下文组装与裁剪现在我们有了一份候选记忆列表1份摘要 K条语义相关记忆 N条最近记忆。其中可能有重复。我们需要将它们按合理的顺序通常是系统指令 - 长期摘要 - 相关记忆和近期记忆按时间倒序排列 - 当前问题拼接成纯文本。这里有一个动态裁剪算法计算当前拼接文本的Token数使用模型的Tokenizer。如果超过8K限制则需要进行裁剪。裁剪策略不是简单的截断开头而是优先级排序最高优先级系统指令和当前问题必须保留。高优先级长期记忆摘要必须保留它是全局知识的锚点。中优先级检索到的相关记忆按相似度得分从高到低保留。低优先级按时间邻近加入的最近记忆如果必须舍弃则从最旧的开始舍弃。通过迭代移除优先级最低的记忆单元直到总Token数低于8K上限。发送请求将精心组装好的上下文严格控制在8K Token内发送给大模型获得回复然后回到步骤1开始新的循环。注意这里的Token计数一定要使用目标大模型对应的Tokenizer如tiktokenfor OpenAI,transformersfor Hugging Face模型进行精确计算不同模型的分词方式不同估算会导致溢出。4. 关键策略详解摘要、检索与裁剪的魔鬼细节分层架构的概念不难但工程上的成败取决于细节处理。下面我分享几个在实际项目中容易踩坑的关键策略。4.1 长期摘要的生成与维护不是一次性的长期摘要不能是静态的。一个常见的错误是在对话中途生成一次摘要然后就一直用它。这会导致摘要逐渐“过时”无法反映对话的最新进展。正确的做法是迭代更新。每次触发摘要生成时给摘要模型的输入应该是“这是之前的摘要[旧摘要]。以下是之后发生的对话[新的若干轮对话]。请结合旧摘要和新对话生成一个更新的、全面的摘要。” 这种方式能让摘要像“滚雪球”一样积累信息同时保持简洁。你需要决定旧摘要的信息在多大程度上可以被新信息覆盖或修正。另一个高级技巧是结构化摘要。与其生成一段自由文本不如让大模型输出一个结构化的JSON例如{ “user_profile”: {“name”: “小明”, “preference”: “喜欢简洁的答案”}, “key_facts”: [“项目截止日期是本周五”, “用户使用的是Python 3.9”], “decisions”: [“采用方案A进行下一步”, “已同意将会议改到明天”], “ongoing_topics”: [“关于XX功能的性能优化讨论”] }结构化摘要更易于程序化处理和信息检索但对其生成的质量和稳定性要求更高。4.2 混合检索策略语义相关与时间邻近的权衡单纯依靠向量检索语义相关是有风险的。假设你们聊了10分钟咖啡然后用户突然问“你刚才说的那个方案是什么” 这里的“刚才说的”具有很强的时间指向性但“方案”这个词可能在历史对话中出现过很多次。纯语义检索可能会找到更早之前讨论的另一个“方案”而不是“刚才”的那个。因此混合检索策略是必须的。我通常采用“语义检索召回时间邻近加权”的方法先用向量数据库召回相似度最高的Top M条记忆例如M10。对这些记忆不仅考虑语义相似度分数还引入一个时间衰减权重。越近的记忆时间权重越高。例如权重 语义相似度得分 * exp(-λ * 时间差)。λ是一个可调参数控制时间衰减的速度。将综合得分语义时间最高的Top K条记忆以及绝对意义上最近的N条记忆一起纳入候选池。这样既保证了相关性又保证了时间上的连贯性。4.3 动态上下文裁剪的优先级算法当候选记忆总Token数超过限制时如何裁剪我设计过一个简单的优先级打分函数效果不错记忆单元优先级分数 α * 语义相关分 β * 时间邻近分 γ * 信息重要性分语义相关分从向量检索得到的相似度得分归一化到0-1。时间邻近分1 / (1 log(1 时间差轮数))越近得分越高。信息重要性分这是一个需要预先标记或推断的分数。例如在记忆写入时如果检测到用户说“这很重要”、“请记住”或者AI回复中包含了确认性结论可以给该记忆单元一个较高的基础重要性分如0.5。也可以事后用一个轻量级模型对历史记忆进行重要性分类。α, β, γ 是超参数需要根据实际对话场景调整。例如在技术讨论中语义相关性α可能更重要在闲聊中时间连贯性β可能更重要。裁剪时按优先级分数从低到高移除记忆单元直到满足Token限制。5. 进阶考量与优化方向如果系统要投入生产还有更多问题需要思考。5.1 记忆的“遗忘”机制记忆不能只增不减。100轮对话后摘要可能也会变得冗长。我们需要设计“遗忘”逻辑。基于时间的遗忘可以设定一个对话的“时间窗口”例如只保留最近1小时内的记忆进行详细检索更早的记忆仅通过高度压缩的摘要来体现。基于重要性的遗忘在生成新摘要时主动舍弃旧摘要中被判定为过时或次要的信息。这可以通过在给摘要模型的指令中强调“只保留仍然相关和关键的信息”来实现。会话分割这是最彻底的“遗忘”。当检测到用户长时间无活动如30分钟或用户明确说“新开一个话题”系统可以主动将会话分段开启一个新的记忆存储空间之前的对话被归档为“上一次会话”其记忆被高度抽象化不再参与日常检索。5.2 成本与延迟的平衡每一次对话都涉及向量化、数据库检索、可能的摘要生成这些都是有成本和延迟的。异步处理摘要生成、向量化写入等操作可以完全异步化不阻塞主请求路径。用户发出消息后系统立即用现有的上下文可能包含稍旧的摘要进行检索和回复同时后台任务去更新记忆。缓存策略对于最近组装过的上下文如果用户连续快速提问可以短暂缓存避免重复的检索和Token计算。检索优化向量数据库的索引选择如HNSW、分区策略按会话ID分区对检索速度影响巨大。对于海量历史数据可能需要引入更复杂的两级检索先粗筛再精筛。5.3 评估与调试如何知道你的记忆架构设计得好不好光靠感觉不行。人工评估设计一些测试用例模拟超长对话检查AI在后期是否能准确回忆起早期的关键信息。自动化测试可以构建一个测试集在对话的不同阶段插入一些对前期信息的询问“我之前提到我喜欢什么颜色”然后检查AI回答的准确性。可视化调试工具开发一个内部工具能够展示对于任意一轮查询系统最终选取了哪些记忆片段组成上下文以及各自的优先级分数。这对于调试检索和裁剪策略至关重要。回到最初的面试题“8K Token撑住100轮对话”其答案不是一个简单的方案名字而是一套包含分层存储、智能检索、动态组装、迭代更新的综合体系。它考验的是工程师对LLM应用瓶颈的深刻理解以及将抽象问题转化为可落地、可优化的工程系统的能力。在实际工作中这套架构会根据业务场景、模型特性、成本预算进行无数次的调整和打磨但核心思想是相通的用有限的计算资源最大化信息的利用效率让AI看起来拥有“理解”和“记忆”。这或许就是当前阶段让大模型应用真正变得“智能”的关键一步。