尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
大模型上下文管理实战:Token预算、记忆分层与检索召回
前几天我和一朋友调试他做的对话机器人本来前期测试一切正常结果聊到第 20 轮的时候模型突然开始答非所问。他问了一句“我刚才不是说过了吗”系统毫无反应。我打开日志看了一眼 Token 消耗心里大概就有数了这又是上下文管理没做好。很多人以为把大模型接口接上、Prompt 写清楚就算完事了真正跑到生产环境才发现拦住你的往往不是模型能力而是上下文该怎么存、怎么截、怎么压缩、怎么找回。这也是我写这篇东西的原因。围绕“上下文管理”这个看起来基础、实际水很深的话题把我的工程经验、踩坑记录和完整方案一次说清楚。不管你是刚接触 API 调用还是已经在做带记忆的对话应用这篇都应该能帮你省下不少试错时间。1. 上下文管理到底在管什么1.1 从代码里的 with 到 AI 应用里的记忆最早接触“上下文管理”这个词多半是在编程语言里。Python 的with open(...) as f就是典型的上下文管理器它保证进入代码块前申请资源、退出后释放资源。那里面的核心思想是资源有生命周期你得在恰当的时机做恰当的事。AI 应用里的上下文管理本质是同一件事。对话历史、系统设定、用户状态、检索结果这些东西都有生命周期。它们不是永远放在那里不动的随着对话轮次增加资源越来越紧张你必须决定哪些内容继续保留、哪些内容要压缩、哪些内容可以丢弃、哪些内容需要从更远的地方找回来。换句话说上下文管理是“对话记忆”的工程化实现。1.2 三个维度决定上下文方案的好坏我接手过的项目里几乎没有哪两个应用对上下文的需求是一样的。但无论需求怎么变评估一套上下文管理方案是否合格就看三个维度容量维度模型窗口是有限的但对话可以无限长。方案能不能在有限窗口里塞下最有价值的信息成本维度Token 就是钱每一轮请求都在烧钱。方案能不能在不明显损害体验的前提下把 Token 消耗压下来质量维度截断和压缩都会造成信息损失。方案能不能把“模型变笨”的副作用控制在一定范围内这三个维度之间存在明显的取舍。窗口塞得越满质量越好但成本越高。压缩做得越狠成本越低但模型可能丢失关键细节。纯粹做“操作层面”的上下文管理比如只写个简单的截断函数和真正做“策略层面”的上下文管理比如分层记忆加上检索召回差距就在这里体现出来。1.3 为什么长窗口解决不了所有问题现在很多模型的上下文窗口越做越大有人觉得“窗口都这么大了上下文管理可以随便做做了”。这个想法我一开始也有过后来实测下来发现完全不是这么回事。窗口大不等于你能把想塞的都塞进去。首先成本是平方级增长的每轮请求都带着全部历史重新发给模型会话稍微长一点费用涨得让你肉疼。其次模型对长上下文的注意力本来就是不均匀的前 2000 Token 里的细节到了后面很容易被稀释掉这跟人一样看长篇报告的时候开头和结尾记得清楚中间稀里糊涂。所以我的观点很明确长窗口解决的是“能不能装下”的问题但“装什么”“怎么装”“装不下的放哪里”仍然要靠上下文管理来解决。2. 算清 Token 这笔账再做设计2.1 一次请求的 Token 是怎么堆起来的一个最简单的对话请求Token 消耗由四个部分组成系统提示词通常固定不变可能占 200 到 800 Token按项目复杂度浮动。对话历史随轮次增长每轮用户输入加模型输出几百上千 Token 不等。当前输入用户这次说的话可能很短也可能很长。输出配额为模型回答预留的空间一般在 500 到 2000 Token 之间。我们来算一笔具体的账。假设你的系统提示词是 500 Token每一轮对话平均消耗 800 Token用户说 300模型回 500用户每轮新输入平均 300 Token输出配额固定 1000 Token。第 1 轮请求的时候总 Token 大概是 500 0 300 1000 1800 Token第 20 轮请求的时候呢 500 800 × 19 300 1000 17000 Token第 50 轮呢光是历史消息就积累了 800 × 49 39200 Token整次请求将近 41000 Token。我见过一个真实场景某客服机器人试运行期间平均会话长度是 40 轮上下。固定提示词加历史消息加输出配额高峰期单条会话的一轮请求消耗接近 6 万 Token。一个用户每天发起 30 到 50 条会话你自己乘一下就知道这个成本有多夸张。提示做成本预估的时候千万别只按“平均 token”来算。用户行为很不均匀长会话才是消耗大户。我一般会看 P90 和 P95 的会话长度那才是决定预算上限的指标。2.2 三种常见的截断策略对比在真正引入复杂方案之前先明确一个基础问题当对话历史超过上限你打算怎么处理截头保尾是最朴素的做法。保留最近 N 条消息超过的直接抛弃。优点是实现简单逻辑清楚缺点也很明显最早的消息通常是冷启动信息、用户偏好、前置约定一旦被丢掉模型就开始“失忆”。滑窗保留稍微讲究一点。不按条数切而是按 Token 数量滑动一个窗口比如固定保留最近 12000 Token。这比按条数截更灵活因为每轮消息的长短差异很大按条数截可能一次就丢了一堆长内容。摘要压缩是质量最好的方案。历史太长时把早期对话交给模型提炼成摘要用摘要代替原始内容继续保留。代价是每次压缩都需要额外调用一次模型有成本有时间开销。三者的特点可以简单做个对比策略信息保留质量实现成本额外开销适用场景截头保尾低容易失忆极低无短会话、原型验证滑窗保留中可能前后矛盾低无轮次少、长度可控的场景摘要压缩高信息保留完整较高需要设计触发时机每次压缩多一次模型调用长会话、生产环境2.3 生产环境里我用的混合策略单独用哪一种在真实场景里都会遇到尴尬。只做滑窗保留聊到 30 轮以后早期的用户意图就模糊了容易答非所问。只做摘要压缩每轮都先调一次模型生产摘要对话响应延迟明显变长。我现在比较推荐的做法是混合策略。系统提示词和用户核心档案永久保留不参与截断。最近的对话历史用滑窗保留保证模型对“刚才聊了什么”有精准的短期记忆。滑窗之前的早期对话定期生成摘要并且摘要继续往前滚动合并。这样模型既能看明白当前语境又不至于丢掉最开始的需求背景。在后面的章节里我会把这个混合策略具体展开包括摘要怎么生成、什么时候触发、长期记忆怎么存储和检索。3. 基于记忆分层的上下文架构3.1 三层记忆模型的设计思路在做模拟项目X的架构时我参考了认知科学里关于记忆的分类方式把上下文拆成了三层。这个设计思路后来在好几个项目里复用效果都还不错。即时记忆工作记忆相当于对话窗口里正在活跃的内容包括系统提示词和最近 N 轮消息。它直接参与模型推理容量最小但速度最快。短期记忆摘要层对较早对话的提炼摘要。不直接参与模型推理但会在需要时注入到工作记忆里。长期记忆存储层以结构化形式存储在数据库里包括用户画像、偏好、历史事实、关键结论。平时不占窗口检索匹配到相关内容时才动态注入。这三层的关系可以类比成一个会议记录员的笔记本。即时记忆是摊开在桌上的那一页短期记忆是刚刚翻过去的几页概要长期记忆是归档柜里的完整记录。记录员不会把整柜子档案都搬到桌上但当你问到某个具体问题时他能快速翻出对应的那一份。3.2 工作记忆的具体组成在每个请求发出前我会组装一份当前的工作记忆顺序和结构大概是这样的系统提示词固定不变包含角色设定和任务规则。动态注入信息包括时间、用户 ID、当前页面场景等按项目需求而定。短期摘要属于压缩后的早期对话放在对话历史之前相当于给模型一个“事情背景”铺垫。检索召回的知识片段如果本轮需要引用知识库或长期记忆把匹配到的若干条内容拼在这里。当前对话的近期消息保留最近 10 到 20 轮按 Token 预算动态调整。用户当前输入。这个顺序不是随便定的。模型对提示词不同位置的注意力权重不一样把摘要放在历史消息前、紧跟系统提示词之后能最大程度地让模型理解这些是“背景信息”而不是当前正在进行的对话流。3.3 数据存储的结构化设计与三层记忆对应数据侧我一般设计三张核心表。会话表记录会话元信息包括会话 ID、用户 ID、开始时间、状态。消息表记录每一条原始消息内容包括角色、内容、Token 数、时间戳、消息序号。记忆表则负责长期信息每一条记忆包含实体、属性、内容摘要、更新时间、召回关键词。拿 SQL 来建表的话大致是这个样子CREATE TABLE sessions ( session_id TEXT PRIMARY KEY, user_id TEXT NOT NULL, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, status TEXT DEFAULT active, summary TEXT DEFAULT ); CREATE TABLE messages ( message_id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT NOT NULL, role TEXT NOT NULL, content TEXT NOT NULL, token_count INTEGER NOT NULL, seq INTEGER NOT NULL, created_at INTEGER NOT NULL ); CREATE TABLE memories ( memory_id INTEGER PRIMARY KEY AUTOINCREMENT, session_id TEXT, user_id TEXT NOT NULL, entity TEXT, attribute TEXT, content TEXT NOT NULL, keywords TEXT, importance REAL DEFAULT 0.5, updated_at INTEGER NOT NULL );消息表里的seq字段很重要它保证了消息顺序不会因为并发写入或数据回放而错乱。token_count字段则是做预算控制的基础截断时直接按 Token 数累加不用在读出来之后再数一遍。4. 多轮对话的动态窗口与检索召回4.1 动态滑窗怎么按 Token 预算裁剪设计工作记忆时最核心的问题就是保留多少轮对话我之前的做法是固定保留 20 轮后来发现效果不稳定。用户问得简短时20 轮可能也就 4000 Token窗口还有大量空间。用户发一段长文本时20 轮直接飙到 15000 Token预算瞬间被打穿。后来改成按 Token 预算动态滑窗效果稳定很多。具体流程是给定一个工作记忆的 Token 上限比如 12000。从最新一条消息开始向前遍历每加入一条消息就累加它的 Token 数直到加上下一条会超出预算为止。这样保证每次请求都尽量把窗口塞满有效的历史信息轮次则完全由消息长度自适应。def build_working_memory(messages, max_tokens12000): selected [] total 0 for msg in reversed(messages): cost msg[token_count] if total cost max_tokens: break selected.append(msg) total cost selected.reverse() return selected这段代码简单但有几个细节值得注意。第一token_count必须在写入消息时就计算好并入库不能每次请求临时去数否则性能会很难看。第二边界情况要处理如果单条消息本身就超过预算不能直接 break 丢掉否则这条消息永远进不了上下文。我的处理方式是单条超预算时仍然强制塞进去宁可多花一点 Token 也不能让模型完全看不到用户说了什么。4.2 检索式长期记忆的落地实现工作记忆里还有一个重要来源就是长期记忆的检索结果。这里我用的是比较通用的方案向量化召回加关键词兜底。思路很简单。每条长期记忆写入时把它转成一个向量存入向量库。每次请求进来之前把用户当前的问题也转成向量在库里做相似度检索取最相关的前 k 条注入工作记忆。伪代码大致如下def recall_memories(query, k3): query_vec embed(query) candidates vector_db.search(query_vec, top_k10) deduped deduplicate_by_entity(candidates) return deduped[:k]这里有一个容易忽略的坑向量检索的质量高度依赖切分粒度。你把一整段长对话存成一条记忆向量平均化了所有语义检索时经常召回一些“看起来相关但核心信息丢失”的内容。我后来把一条记忆控制在 50 到 200 字以内并且拆成“实体 属性 内容片段”的结构化字段召回精度明显提升。至于向量库的选型小流量项目用轻量级的嵌入式向量库就够了不需要一开始就上大规模集群。迭代过程中最重要的不是基础设施而是你有没有在正确的时机做正确的召回。4.3 召回内容怎么和当前对话融合召回结果不能直接一股脑拼进对话历史这会让模型分不清哪些是实时消息、哪些是背景资料。我做了一个统一的格式化模板[背景材料] 召回的知识片段按相关度从高到低排列 [背景材料结束]为什么要在前后加标记因为模型是纯文本输入它只能靠格式来区分信息层次。给背景材料加上明确的边界模型就会把它理解为参考信息而不是把它当成对话的一部分去接话。这个模板设计看起来不起眼但对回复质量的提升非常明显。我对比过同样一组召回内容带标记和不带标记的回复前者的相关性和稳定性明显更高。注意召回结果需要控制条数和长度。一次注入超过 5 条背景材料模型经常会“吃撑”回答反而变得发散。生产环境我的经验值是控制在 3 条左右除非当前问题明确需要对比多个实体否则只取最相关的 3 条已经足够。5. 摘要压缩把历史浓缩成要点5.1 什么时候触发压缩摘要压缩不是每轮都做那样既费钱又拖慢响应。我设计了一套触发机制核心原则是当工作记忆即将溢出时才进行压缩。具体判断逻辑可以这样理解。设定一个工作记忆预算比如 12000 Token。当滑窗选完以后如果总 Token 接近预算上限同时最早那批消息还没被截断掉说明“历史已经太长必须压缩了”。这时候触发一次摘要生成把滑窗之外更早的那段对话提炼成 300 到 800 Token 的摘要。触发时机不能太频繁。压缩本身也要花钱花时间频繁触发会严重影响交互体验。我后来设置了冷却时间同一会话 5 分钟内最多触发一次压缩避免对话稍微长一点就连续触发。5.2 两级摘要结构避免二次信息损失只保留一份滚动摘要长会话反复压缩十几次以后早期信息早就变形到没法看了。这跟你用复印机反复复印一张纸一样每复印一次清晰度就下降一截。我的方案是两级摘要结构。分段摘要把会话按时间段切成若干片段每个片段单独生成摘要。比如第 1 到 10 轮生成摘要 A第 11 到 20 轮生成摘要 B以此类推。滚动总摘要基于全部分段摘要再生成一份总摘要供模型快速理解全局背景。请求时通常只注入当前最近的几个分段摘要加上滚动总摘要。如果用户明确提到一个很早之前的内容追问细节时再单独检索那个分段的原始消息把原文捞出来。这个方案的优点在于压缩造成的损失被分散到多个独立片段里任意一段摘要质量下降都不会拖累整条会话。而且“重新展开”的能力很重要靠摘要想不起来细节的时候还能回去翻原文。5.3 摘要生成的提示词模板摘要生成也是一个模型调用提示词模板直接影响摘要质量。我用的一套模板大体长这样请根据以下对话记录提炼关键信息。要求 1. 保留用户的核心诉求、明确偏好、已确认的事实。 2. 保留尚未解决的遗留问题和待办事项。 3. 删除寒暄内容、重复表达和与主题无关的信息。 4. 控制摘要长度在 300 字以内。 5. 如果对话涉及决策过程请标注最终结论。 对话记录 {history_text}这里有一个要点摘要里必须显式标记“待办事项”和“遗留问题”。原因是模型每轮回复都会受到摘要影响如果摘要里没有保留“用户要求下周给他报价”这类信息后面模型很容易忘记这个承诺。我踩过这个坑加了之后明显改善。6. 持久化与会话恢复6.1 消息落库与异步写入的取舍上下文管理的最后一道环节是持久化。一个真实可用的对话系统必须把消息和历史完整存下来原因有两个一是用户中途刷新页面或断线重连需要恢复上下文二是后续人工审计和模型优化需要查看完整记录。写入策略上有同步和异步两种选择。同步写入简单可靠消息入库成功后再返回模型回复但会拖慢接口响应。异步写入响应快但存在消息丢失的风险。我一般的折中方案是用户消息同步落库模型回复异步落库。这样至少保证了用户的输入不会丢即使系统崩溃人工也能从用户消息推断出大概语境。提示消息落库时千万别忘了记录token_count。我见到不少团队消息表只有内容和时间没有 Token 数结果做动态滑窗或者成本分析时全都要重新数一遍 Token既慢又浪费钱。6.2 会话恢复的完整流程用户重新进入会话时不能只是把历史消息一股脑塞回上下文。恢复流程应该是分层的。第一步读取会话记录拿到摘要字段和所有消息。第二步组装工作记忆用动态滑窗从最近消息往前选。第三步如果滑窗选出来的消息不足以覆盖早期关键信息就补充注入会话摘要。第四步检查该用户是否有新的长期记忆产生把相关的额外信息一并注入。这个流程实现起来不难但很实用。用户隔了一天回来继续聊模型不需要重新读一遍昨天 50 轮的全文只需要看一段摘要加上最近几轮消息就能无缝衔接。6.3 定期分析与遗忘机制记忆不能只增不减这是另一个容易忽略的点。长期记忆表里的内容如果无限增长检索时的噪音会越来越大召回的准确率也会跟着下降。我给模拟项目X设计了定期分析任务每 24 小时跑一次。对记忆表里的内容做评估长期未访问的记忆重要性权重自动降低。超过 90 天没有命中的记忆进入“可遗忘”状态不再参与召回。用户可以明确要求系统遗忘某条记忆这个需求在隐私敏感的场景里是硬性要求必须有。强调一下遗忘机制不一定要物理删除数据。我的做法是加一个active标记位逻辑上不让它参与召回数据仍然保留供审计。这样既能满足“被遗忘”的要求又不至于丢失线索。7. 常见问题与排查技巧实录7.1 高频问题速查表做上下文管理这段时间我在各种项目里反复见到一些典型问题。整理成表格方便大家直接对照排查症状可能原因排查方向对话超过 20 轮后明显变笨早期关键信息被简单截断丢弃检查是否做了摘要压缩早期内容是否还在Token 消耗异常高没有做滑窗全部历史每次重发查看每次请求的实际 Token 数确认截断逻辑模型突然回答“我不记得你说过”摘要里没保留用户核心诉求检查摘要提示词是否包含用户偏好和待办事项检索召回的内容和当前问题不相关长期记忆切分粒度过粗信息被平均化检查记忆表的分段长度按实体属性和内容片段拆分回复质量时好时坏注入工作记忆的先后顺序不稳定固定组装顺序用背景材料标记框住检索内容7.2 排查三板斧日志、快照、小窗口遇到上下文相关的问题最快定位问题的方式我总结为排查三板斧。第一板斧打印 Token 消耗日志。每个请求记录总 Token、历史 Token、当前输入 Token、输出 Token看到消耗曲线异常往往直接就能判断是截断还是压缩出了问题。第二板斧重建会话快照。把某一个时刻的完整工作记忆导出来人工检查里面到底有什么。这个问题很常见你以为模型看到的是完整内容实际打印出来发现早期的几条消息因为 Token 预算问题早就被丢掉了。第三板斧小窗口试跑。把工作记忆上限临时缩小一半如果问题的表现变得更严重说明是上下文信息不足导致。反过来如果把上限翻倍后问题消失说明信息确实不够需要优化压缩策略而不是盲目扩大窗口。7.3 一个真实的排查案例最后分享一个实际案例。某团队做的一个咨询类应用用户反馈“聊到后面模型连我一开始说过的预算金额都忘了”。第一板斧调日志发现第 30 轮的请求里历史 Token 接近 1 万其中最早 15 轮的 Token 数已经为 0说明被完全丢弃了。第二板斧重建快照发现工作记忆里只剩下最近 12 轮消息系统提示词虽然还在但没有任何关于早期内容的摘要替代。问题定位清楚了只做了滑窗截断没有任何压缩逻辑。修复方案也很直接引入会话摘要在第 15 轮左右生成第一份分段摘要内容包含用户初始需求、预算金额、偏好约束。之后滑窗保留最近消息摘要始终在上下文里模型再也没有忘记过预算信息。这个案例最有价值的地方在于问题不是模型能力的锅而是上下文管理策略缺失导致。光靠调 Prompt、换更大的模型都解决不了根本问题。8. 关于上下文管理我的一点个人体会做了这么多上下文相关的方案我的一个强烈感受是这个问题没有一劳永逸的解法本质上是容量、成本、质量三者之间的持续权衡。窗口再大也会被长对话填满摘要再精也会损失细节检索再准也有召回不到的时候。我现在的习惯是每做一个新项目先花时间把上下文管理的基础设施搭好包括消息落库、Token 统计、摘要触发、记忆检索。这套东西一开始会占用一些开发时间但它带来的收益会在项目后期越来越明显。很多团队等到线上出了问题才回头补课往往要付出更高的代价。如果你正准备给自己的对话应用加上下文管理建议从最基础的消息落库和 Token 统计做起。先把现状看清楚再逐步引入滑窗、摘要和检索。步子不用迈太大但每一层都要做扎实这样后面出问题的时候你至少知道问题出在哪一环。
RELATED

相关推荐

多模态Agent实战:Harness编排与MCP工具接入的工程架构解析

多模态Agent实战:Harness编排与MCP工具接入的工程架构解析

1. 多模态 Agent:从“能聊”到“能干”的跨越最近这半年,我几乎把所有业余时间都砸在了一个Agent开发项目上。说实话,市面上讨论Agent的声音很多,但真到了自己动手把一个大模型多模态应用和一个可编排的Harness框架、再加上MCP协议…

📅 2026/10/16 7:57:10
GitHub Actions上跑AI Agent:事件驱动自动化实战与避坑指南

GitHub Actions上跑AI Agent:事件驱动自动化实战与避坑指南

最近在做持续集成自动化相关的事情时,我发现一个特别有意思的玩法——把 AI Agent 直接跑在 GitHub Actions 上。说白了,就是用一个开源框架,把传统常驻服务器上的 Agent 拆成按次计费的临时任务,每次由事件触发拉起一个运行环境&…

📅 2026/10/16 7:52:10
家校沟通高情商话术:化解家长矛盾与冲突的实用指南

家校沟通高情商话术:化解家长矛盾与冲突的实用指南

家校沟通不用慌!高情商话术,轻松化解家长矛盾班主任也好,普通任课老师也好,干这行最累的往往不是备课上课,而是“和人打交道”。尤其是微信和电话那头,因为孩子的一点鸡毛蒜皮就情绪上头的家长,…

📅 2026/10/16 7:52:10
MORE NEWS

更多资讯

📰

有趣的Python代码:从一行回文到终端动画的编程乐趣

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Vue 是一个渐进式 JavaScript 前端框架,擅长构建响应式单页应用

Vue 作为一款渐进式 JavaScript 前端框架,专注于构建响应式单页应用;Python 则常以 Flask、FastAPI、Django 等框架提供后端服务。二者通常通过 RESTful API 或 WebSocket 通信,形成“前端负责交互与展示、后端负责数据与业务逻辑”的前后端分…

📰

逻辑回归在信贷风控中的应用:从违约预测到评分卡实践

一个客户在APP上提交了借款申请,系统要在几秒内决定批不批、批多少、利率怎么定。你可能会以为背后跑着的是某个深度学习模型,但实际上,大多数信贷机构真正跑在评分决策流里的,还是那个看起来有点“过时”的逻辑回归。我在做个人贷…

📰

Gated DeltaNet:融合Delta Rule与Gating的线性注意力机制解析与PyTorch实现

最近在调研长文本建模方案时,Gated DeltaNet 这个名字反复出现在我的视野里。它总是和 Delta Rule、Gating 绑在一起讨论,还被不少技术社区的人是下一代开源大模型可能采用的核心机制,标题里的 Qwen 3.5 相关猜测就是在这种背景下冒出来的。先…

📰

Rust RFC 2707 深度解读:让 `..` 成为一等模式(dotdot patterns)的语法统一之路

文档 【免费下载链接】rfcs RFCs for changes to Rust 项目地址: https://gitcode.com/gh_mirrors/rf/rfcs 点击查看 免费下载 Rust 的模式(pattern)语法中存在一个长期的特殊情况:.. 虽然可以在元组、切片、结构体等“列表式”模…

📰

基于SSM的宿舍供电管理系统毕业设计全攻略

每年到了毕业季,私信里问得最多的就是:毕设选题到底怎么定。技术栈要稳、业务场景要真实、论文还得能写够字数。宿舍供电管理系统这个方向,我觉得非常值得展开聊聊。它基于SSMJava这套经典组合,属于典型的后勤管理信息化场景&…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬