
本文主要介绍上下文管理问题知识点比较多将分为两篇文档介绍。1、上下文窗口context window、上下文context、记忆memory三者是什么关系一句话概括上下文窗口是容器上下文是容器里当前装的东西记忆是容器外面那个仓库。记忆必须经过召回 筛选 注入才能变成上下文的一部分没被召回的记忆对本次推理等于不存在。概念本质边界谁决定上下文窗口context window模型单次前向推理能接收的 token 上限硬性物理约束如 128k / 200k模型架构 位置编码上下文context本次推理实际拼进去的全部内容必须 ≤ 窗口上限你的 prompt 组装逻辑记忆memory落在外部存储、跨会话持久化的信息不受窗口限制可无限增长你的存储与检索设计2、上下文压缩和记忆压缩区别在哪上下文压缩把当前上下文中“占地方但当前用不上”的内容换成更紧凑的表示。记忆压缩对已写入外部存储的记忆做合并、去重、抽象、淘汰让他在规模增长后仍然可检索。上下文压缩发生在本次推理的输入侧目标是让当前上下文窗口能放得下记忆压缩发生在外部存储目标是防止记忆膨胀让长期积累的信息可检索等。2.1、上下文压缩和记忆压缩本质对比维度上下文压缩记忆压缩作用对象本次推理的输入 token外部存储中的记忆条目触发时机运行中token 水位超阈值写入时 / 定期离线任务约束硬约束必须 ≤ 窗口软约束容量、检索精度、成本目标塞得下 保住当前任务关键信息去重、合并、抽象、防噪声淹没丢失后果本轮任务失败 / 质量下降未来某次召回失败延迟暴露可逆性通常可逆原文落盘可回溯往往不可逆合并后原始细节消失评价指标压缩后任务成功率保持率召回准确率 / 召回覆盖率2.2、上下文压缩常见手段丢弃冗余工具返回原文保留摘要 引用id合并同类消息连续多轮工具调用合并为一条早期对话滚动摘要结构化裁剪大json只留关键字段外置句柄内容落盘上下文中只放“内容摘要文件路径查询接口”红线永不压缩任务目标、用户显示约束、已确认的关键事实、当前正在处理的任务等关键特性有明确的当前任务作为判断基准——什么该留、什么该丢取决于这轮要干什么可回溯压缩 ≠ 删除原文要能按 ID 取回高频发生长会话里可能每几轮就触发一次2.3、记忆压缩常见手段去重同一事实多次写入 → 覆盖而非追加保留最新 时间戳合并多条碎片化记忆 → 合成一条完整事实抽象/升华多次具体事件 → 提炼为一条规律“用户三次要求用中文回答” → “用户偏好中文”淘汰过期、低价值、长期未被召回的条目降权或删除分层原始日志 / 摘要 / 抽象结论 分层存储检索时按需下钻关键特性没有当前任务作为基准——判断标准是未来是否可能有用这是预测性问题比上下文压缩难得多往往不可逆合并后原始细节就没了低频但重通常是离线批处理或写入时触发3、 为什么说上下文管理是 Agent 工程里最容易出事故的地方因为它的失败是静默降质——不报错、不崩溃、不告警只是慢慢变笨。这是它区别于其他工程问题的根本特征。3.1、原因如下失败模式是静默的最根本原因模型不会说我缺信息它会用残缺信息自信地编一个答案。格式完整、语气笃定、逻辑通顺——只有对照事实才能发现它错了错误会连锁放大Agent 特有Agent 的每一步输出都是下一步的输入 一次污染不是错一次而是被反复消费、逐层放大污染源极多且不可控上下文是多方拼装的产物比如工具返回、检索结果、历史对话、压缩摘要每个来源都可能是污染源且大部分无法控制约束是硬性的但需求是软的上下文窗口有限必须对进入上下文的信息进行选择3.2、解决方案层面措施可观测每轮记录上下文快照各分区 token 占比、压缩记录、召回列表、工具返回摘要可回溯压缩不删除原文落盘 ID 引用支持按需取回防御性工具返回设上限、格式校验、异常隔离召回结果做相关性阈值过滤状态外置任务目标、关键事实用结构化字段维护不依赖模型从历史里自己找分级告警token 水位、压缩次数、截断事件、召回命中率 全部埋点回归测试建立上下文污染场景的测试集验证压缩后任务成功率保持率3.3、具体原因分析如下原因1:失败模式是静默的最根本原因对比一下其他类型的故障故障类型表现可发现性代码 bug抛异常、崩溃立刻有堆栈接口超时报错、重试立刻有日志模型能力不足答错、答不出较快可感知上下文管理出错答得像模像样但不对极慢且常被误判上下文出问题时模型不会说我缺信息它会用残缺的信息自信地编一个答案。你看到的是一个格式完整、语气笃定、逻辑通顺的回复——只有对照事实才能发现它错了。更糟的是这类问题常被归因为模型不行“幻觉”于是团队去换模型、调 prompt方向完全错了。原因2:错误会连锁放大Agent 特有单轮对话里上下文错了就是这一轮错。但 Agent 是多步循环上下文污染 → 模型基于错误信息决策 → 调用错误的工具 → 工具返回错误结果 → 写回上下文 → 下一轮基于更错的信息决策 → ...雪崩关键点Agent 的每一步输出都会成为下一步的输入。一次污染不是错一次而是被反复消费、逐层放大。而且越到后面错误越难追溯——你看到的是第 15 步的荒谬结果根因在第 3 步的一条脏数据。原因3:污染源极多且不可控上下文是多方拼装的产物每个来源都可能是污染源来源典型污染工具返回格式错乱、超大JSON、报错信息混入、过期缓存检索/召回召回了不相关或过时的记忆、相似但错误的条目历史对话用户前后矛盾、模型自己之前的错误结论被当事实压缩过程摘要丢关键细节、摘要引入幻觉多 Agent 协作子 Agent 的错误结论被主 Agent 采信并发/重试重复注入、顺序错乱、旧版本覆盖新版本你无法控制外部工具返回什么也无法保证检索永远精准。这意味着上下文管理本质上是在不可信输入上做防御性工程。原因4:约束是硬性的但需求是软的硬约束token 上限是物理的超了就是跑不了软需求这轮到底该保留哪些信息没有标准答案于是必须做取舍而取舍没有 ground truth压太狠 → 关键信息丢失任务失败压太松 → 塞不下或注意力被稀释丢错了 → 当时看不出几轮后才暴露这是一个持续在做的、无法验证正确性的决策。而且同一个策略在不同任务上最优解完全不同。4、一个 Agent 的上下文里通常有哪些组成部分请按稳定性排序并说明理由。Agent 的上下文的组成部分如下序组成部分稳定性变化频率典型占比1System prompt角色、规则、ReAct 格式约束极高几乎不变2%~5%2工具定义含检索工具、业务工具极高版本级5%~15%3长期记忆注入用户偏好、项目事实高天/周级2%~8%4会话历史中→低—25%~55%4a├ 会话摘要压缩点之前有损索引中压缩时重写5%~15%4b└ 增量原文压缩点之后保真记录低每轮追加20%~40%5本轮新增工具返回含检索结果极低每次调用变10%~40%6本轮新增用户输入极低每轮变循环中常为空1%排序理由:KV Cache 只能前缀命中——易变内容放前面后面全部失效成本差一个数量级——缓存命中价约为未命中的 1/10稳定部分可离线优化易变部分只能在线控制5、 上下文快满了你的压缩策略是什么请给出具体触发条件和执行顺序。核心原则先切输出、再留工具、剩下才是输入分级触发、按价值密度从低到高依次丢弃压缩不等于删除。5.1、触发条件上下文窗口使用率以可用预算 窗口 − 安全垫(15%)为分母5.2、执行顺序检测水位 ↓ 60% → 监控结束 ↓ 60%~70%(预警) → ① 清理工具返回原文 → ② 合并同类消息 → 结束 ↓ 70%~85%(压缩) → ③ 滚动摘要增量区 → 摘要原文落盘 ↓ 85%~95%(强制) → ④ 结构化裁剪 → ⑤ 外置句柄 → 只留目标事实近3轮 ↓ 95% → 熔断拆分子任务 / 提示用户问题为什么 70% 就开始压压缩本身要消耗 token调 LLM 做摘要且压缩后还要留空间给后续轮次。等到 90% 才动手已经没有空间做压缩这件事了。5.3、永不压缩的红线内容理由任务目标丢了就不知道在干什么用户显式约束丢了会违反要求已确认的关键事实丢了会重复询问或基于错误信息决策当前正在处理的实体ID、文件名、错误码丢了会答非所问安全边界丢了可能产生违规输出5.4、执行操作说明外置工具返回收益最大、损失最小旧工具返回原文 → 摘要 引用id 取回方法原文落盘合并同类消息连续多轮工具调用 → 合成一条调用了 A/B/C结果摘要如下 重复的 Thought → 只保留结论滚动摘要增量原文 → 摘要[增量原文区] → 压入摘要 → 摘要重写 → 增量区清空 边界以上次压缩点为界摘要与增量原文严格互斥 摘要要求结构化字段任务目标、已确认事实、待办、关键结论不用自由文本 保留锚点摘要里标注第 3 轮用户提到接口超时 3s需要细节可回查原文结构化裁剪大 JSON → 只留当前任务需要的字段 长列表 → 只留前 N 条 总数system prompt 和任务目标这两项是红线原则上不压缩只在 85% 以上极端情况做精简措辞绝不删除约束。5.4、问题1、为什么先动工具返回而不是先摘要对话工具返回价值密度最低、体积最大常占 30%~60%清理它收益最高、损失最小。摘要对话会丢语义细节代价更高。按价值密度从低到高排序不是按体积从大到小。2、压缩用 LLM 做摘要有什么风险① 丢细节且模型自己不知道丢了什么② 多轮累积衰减③ 可能引入幻觉。对策结构化摘要 事实校验 原文可回溯。3、怎么衡量这套方案有效衡量压缩效果不能看单点结果指标无基线、无法归因要做对照实验离线 A/B 或灰度 探针日常看可归因信号探针保真率、约束违反率、重复询问率、回查有效率。指标衡量什么怎么算探针事实保真率压缩后关键事实是否还在埋探针 → 压缩后提问 → 答对率答案一致性压缩前后同一问题答案是否一致离线 A/B同问题对比约束违反率结构化状态是否生效规则校验输出是否违反已知约束重复询问率已知信息是否丢失检测模型是否重问 state 里已有的信息压缩比 vs 保真率曲线压得越狠保真掉多少不同压缩比下测保真率找拐点回查有效率不是次数回查是否解决了问题回查后任务是否推进 / 是否还需再查6、工具返回结果特别大比如一个 200KB 的 JSON你怎么处理核心思想把塞进上下文换成让模型能按需取。核心思路入口先设限源头能裁就裁列表能分就分剩下的一律外化——但外化必须配目录式摘要 能读回来的工具。6.1、解决方案层手段适用优先级0. 入口防护设响应大小上限超限直接走外化所有工具调用必做1. 源头裁剪服务方改 / 适配层裁剪字段能改或能包一层最优2. 分页按 token 分页 告知总数列表型数据优先3. 结构化裁剪只取需要的字段/区块已知结构优先4. 外化落盘 目录式摘要 查询接口兜底通用必做执行顺序0 永远先执行 → 然后按 1→2→3→4 依次尝试前一层能解决就不进下一层。外化说明落盘 摘要 查询接口三件套要素作用常见错误落盘原文完整保留可回溯只存摘要原文丢了摘要让模型知道里面有什么摘要太笼统模型无法判断相关性查询接口让模型能按需深挖只给路径模型没有读取手段摘要要写成目录而不是概述❌ 差这是一个包含用户信息的 JSON 文件200KB ✅ 好用户数据含 3 个区块 - profile基本信息12 字段 - orders订单列表340 条字段id/amount/status/date - logs操作日志1200 条按时间倒序 可用 read_file 或 query_json 按区块读取外化之后模型必须有能力把需要的信息取回来否则外化等于丢弃。查询接口的三种实现给工具read_file(path, offset, limit) 或 query_json(path, jq_expr)给路径 说明告诉模型文件在哪、什么格式、怎么读预置常用查询把高频查询封装成工具如 get_user_orders(user_id, status)6.2、问题1、为什么不直接截断可能截掉关键字段且截断后 JSON 不合法模型不知道数据残缺会基于不完整信息下结论无法回溯。截断只适合确定后面无关的场景。2、外化后模型不去读怎么办原因摘要没写清里面有什么模型不知道值得读没有读取工具。对策摘要写成目录式列区块和字段确保有 read_file/query_json 可用必要时在摘要里给行动建议“如需订单明细请读取 orders 区块”。3、落盘文件什么时候清理按会话生命周期清理或设 TTL如 24h。清理前确认任务已结束否则模型后续想回溯就找不到了。4、如果模型需要的数据分散在多个大文件里先给所有文件的目录式摘要每个约 100 token让模型规划读哪几个、读哪部分再逐个按需读取。不要一次性全读进来。7、 多轮对话里用户前面说的约束被后面淹没了模型开始违反怎么解核心解法约束不能靠写在历史里等模型自己找要提成结构化状态、放稳定前缀区每轮固定注入再用程序化校验兜底——能代码拦住的绝不靠模型自觉。关键认知约束在上下文里≠被模型用上。所以解法不是把约束写得更醒目而是改变它的存在形式。7.1、问题本质约束被淹没是三个机制叠加的结果机制表现位置衰减lost in the middle早期内容在长上下文中注意力权重低稀释效应大量无关内容淹没关键信号覆盖效应后出现的弱指令覆盖前面的强约束7.2、解决方案完整方案流程如下用户消息到达 ↓ ① 约束提取规则/小模型→ 更新约束状态覆盖而非追加 ↓ ② 组装上下文[system][约束清单][tools][memory][历史][工具返回][用户输入] ↓ ③ 模型生成system 里含输出前逐条自检指令 ↓ ④ 程序化校验格式/单位/禁用词 ↓ 失败 ⑤ 带错误信息重试最多 2 次 ↓ 成功 输出下面具体进行说明1、约束外置为结构化状态核心做法对话历史自然语言会衰减 ↓ 提取 约束清单结构化每轮固定注入约束清单示例{ hard_constraints: [ {id: C1, rule: 输出必须是 JSON不含 markdown 代码块, source: 第1轮用户, priority: P0}, {id: C2, rule: 金额单位统一用人民币元, source: 第3轮用户, priority: P0}, {id: C3, rule: 不要调用外部网络工具, source: 第2轮用户, priority: P0} ], soft_preferences: [ {id: P1, rule: 回答尽量简洁, source: 第5轮用户, priority: P2} ] }2、注入位置放在稳定前缀区system prompt 之后、会话历史之前每轮原样注入[system][约束清单][tools][memory][摘要][增量原文][工具返回][用户输入] ↑ 每轮固定位置靠前不参与压缩为什么放这里位置靠前 → 不受中间衰减影响每轮固定 → 不依赖模型从历史里检索不参与压缩 → 不会被摘要丢掉内容稳定 → 缓存友好只有约束变化时才失效3、约束的提取与维护提取时机时机做法用户消息到达时轻量规则/小模型判断这句话里有没有新约束压缩前强制检查增量原文里有没有未提取的约束定期每 N 轮复核一次防止遗漏提取规则什么算约束显式否定“不要…”、“禁止…”、“别…”显式要求“必须…”、“一定要…”、“只能…”格式要求“用 JSON”、“不超过 100 字”、“分三点”范围限制“只考虑…”、“排除…”什么不算一次性指令“这次帮我查下…”可协商的偏好“如果能…更好”→ 归入 soft_preferences冲突处理因为约束清单是“当前有效状态”对话历史是“时间序列”。两者天然存在“旧约束 vs 新约束”的冲突。关键不是避免冲突而是明确“谁赢”和“怎么处理旧约束”。冲突的四种来源:冲突类型例子本质约束被更新第 3 轮说“输出 JSON”第 8 轮说“改成 Markdown”同一约束的新旧版本并存约束被撤销第 3 轮说“超时 5s”第 10 轮说“超时不用管了”旧约束已失效但仍在历史里约束被细化/例外“输出 JSON” → “输出 JSON但错误信息用纯文本”一般规则与例外并存约束本身矛盾用户前后说法不一致(可能忘了自己说过)用户侧不一致这四种在对话历史里都会留下痕迹而约束清单只保留“当前有效”的那一版——冲突就发生在这里。解决原则约束清单是唯一真相源历史必须标注失效原则 1约束清单优先级最高且必须带版本constraints { output_format: { value: Markdown, since_turn: 8, supersedes: [JSON (turn 3)], # 显式记录被替代的旧值 status: active } }关键supersedes 字段让“旧约束已失效”这件事本身成为结构化信息。原则 2历史中的旧约束必须被标记不能裸奔做法说明摘要生成时标注“用户曾要求 JSONturn 3后于 turn 8 改为 Markdown”或直接不写入摘要已被替代的约束摘要里只留最新版 一句“曾变更”禁止摘要里只写旧约束、不写变更 → 模型必然选错原则 3注入时显式声明优先级【当前有效约束】以此为准 - 输出格式Markdownturn 8 起 【历史变更记录】仅供参考勿执行 - turn 3 曾要求 JSON已于 turn 8 废止用格式把“谁赢”写死不靠模型自己推断。原则 4矛盾无法消解时问用户用户前后说法不一致类型④系统无法判断哪个是真实意图 → 必须澄清不能猜。注意事项约束的作用域不是所有约束都全局有效作用域例子处理全局“输出用中文”一直有效直到被更新阶段“先只做分析不要改代码”阶段结束即失效单次“这次回答简短点”只对下一轮有效约束清单必须带作用域否则“单次约束”会被误当成“全局约束”长期生效——这也是一种冲突。4、三层防护不只靠注入层手段作用1. 注入层约束清单每轮固定注入保证模型看得到2. 生成层输出前自检逐条核对约束保证模型用得上3. 校验层程序化校验格式、单位、禁用词保证违反能被拦住输出前自检在 system prompt 里加一段固定指令输出前逐条核对以下约束任一不满足则修正后重新输出 - C1: 输出必须是 JSON不含 markdown 代码块 - C2: 金额单位统一用美元 - C3: 不要调用外部网络工具注意自检清单要动态生成从约束状态渲染不能写死在 prompt 里。程序化校验最可靠能程序化校验的绝不靠模型自觉def validate(output, constraints): errors [] if C1 in constraints and not is_valid_json(output): errors.append(C1 违反输出不是合法 JSON) if C2 in constraints and ¥ in output: errors.append(C2 违反出现人民币符号) return errors校验失败 → 带着错误信息重试比让模型自己发现有效得多。分两类可程序化的格式、单位、禁用词、长度走代码校验不可程序化的语气、逻辑一致性走模型自检 人工抽检。优先把约束设计成可程序化校验的形式——这是 prompt 工程的一部分。7.3、问题1、为什么不能只靠把约束写得更醒目常见但无效的做法做法为什么无效在 system prompt 里加粗、加非常重要位置仍在前面长上下文下照样衰减把约束重复写三遍占用 token且重复内容本身会被稀释每轮在用户消息里重述约束污染对话历史且用户不会配合让模型记住之前的约束依赖模型自觉不可靠根本问题这些做法都在自然语言层面加强而衰减是注意力机制层面的问题。必须改变存在形式结构化 固定位置 程序校验。2、为什么不把约束放在用户消息里每轮重述污染对话历史让历史变长且重复用户不会配合位置仍在每轮变的区域缓存不友好。放稳定前缀区每轮固定注入才是正解。3、约束太多比如 20 条怎么办分级P0 硬约束每轮注入P2 软偏好按需注入合并同类把可程序化的约束从 prompt 里移出只留校验代码模型不需要看到只需要被拦住。4、为什么不能“只留约束清单、删掉历史里的约束”看似干净实则有害问题说明丢失“为什么改”用户改约束往往有原因如发现 JSON 不好读这个原因影响后续决策模型可能“重新发现”旧约束如果历史里完全没痕迹模型可能从更早的对话里推断出旧约束审计需要合规场景要能还原“约束变更史”正确做法清单存“当前值 变更链”历史存“变更记录”两者一致。后续内容敬请期待下一篇文章。