跳到正文

上下文工程与压缩卸载 ​

一句话:上下文不是"塞材料",是每轮调用前编译出来的:按权限分层、按预算裁剪、超窗时先提交事实再压缩。本篇合并讲完整链路——工程管线(Builder/Manifest/预算/知识包装/治理)与压缩卸载(四维触发/八级策略/四道防线/缓存代价)。

上篇 · 上下文工程管线 ​

为什么需要 ​

任务跑到几十轮后,常见三类退化:目标被最近一句话淹没;已确认的订单号、金额、审批结果被挤出窗口;工具结果越堆越多,延迟和账单一起涨。全部塞入会稀释注意力,只留最近几轮又丢目标和事实,所以需要一层独立机制:编译本轮视图、压缩卸载历史、把事实放到窗口外、把知识与记忆按权限供给。这是上下文工程;把单轮任务说清楚是 Prompt 工程,见 Prompt 工程。

核心机制 ​

一、Context 是编译出来的,不是拼接出来的 ​

系统侧按约束强度分层,冲突时高层覆盖低层,而不是“谁排在后面听谁的”:

层内容变化频率冲突规则
平台策略安全、合规、租户边界几乎不变不可被项目文档覆盖
Agent 契约 / 租户规则 / 运行时提醒职责、工具、术语、流程;阶段、预算、等待状态版本级 / 每轮低于平台策略,只能补充不能改目标
选中的 Skill某类任务的做法与工具描述任务级可覆盖风格,不覆盖权限;工具只声明能力

任务侧再放目标、计划/待办、近期交互、压缩历史、检索到的记忆与知识、Workspace 引用。平台策略与用户要求冲突时,用户可改方向但不能突破安全边界;Memory 的历史偏好不能替代当前业务事实;工具返回的内容不能自动升级为系统指令。

二、Context Builder:一条确定性管线 ​

text
读 Task State → 解析身份与作用域 → 收集候选
→ 权限过滤 → 排序去重 → 分配预算
→ 压缩/引用化 → 按层编译 → 生成 Manifest

两个顺序最容易写反:先过滤再排序,先跨租户召回再提醒“别泄漏”,泄漏已经发生;先提交事实再压缩,先压缩再想“刚才决定了什么”,摘要里已经没有可提交的事实。

Manifest 记录每个片段的 source / scope / version / trust / permission_basis / selected_reason / token_count / transform / content_hash。它用来解释“模型为什么没看到”、回放“当时看到的是哪一版”、审计“敏感内容为什么进了上下文”。

三、预算与压缩:先零成本,后模型 ​

预算要分区:平台规则、当前目标、未决审批设固定下限;检索证据、工具结果、历史用动态额度;再给输出和下一轮 Observation 预留空间。压缩按成本从低到高:

级别动作代价丢什么
L1 机械清理清旧工具结果、滑动窗口零细节,可能反复读取
L2 结果落盘大结果写 Workspace,只留摘要与引用零窗口内不可见,需按需取回
L3 结构化摘要用固定字段替换旧叙述一次模型调用措辞与部分因果

完整的策略谱、触发条件与熔断见 上下文压缩与卸载。压缩不是删除,而是 Commit → Compact → Rebuild → Validate:先把结构化事实提交到 Task State/Workspace,再把叙述性历史压成带来源的摘要,用摘要、状态和最近消息重建视图,最后校验目标、未决项、权限、验收证据是否还在;不通过就回到 Commit。关键事实最终写入外部状态(L4),不占窗口,但要处理过期与删除。

“最近”不只按时间:目标的最新修改、未解决的错误、待审批动作、验收失败的证据,即使更早也必须保持活动。规划状态、权限记录、子 Agent 状态永不压缩;压缩切点不能切断工具调用与结果的配对。

四、窗口外:Call / Session / Task 与 Workspace ​

概念边界存什么
Call一次请求,秒到分钟本轮 messages、工具结果
Session交互边界,分钟到数天会话历史、用户偏好
Task可验收目标,跨请求/进程/节点目标、计划、状态、产物、证据

把 Task 绑死在消息线程上,后台执行、多人协作、跨渠道续接都会被限制。状态也要分三种表示:Event Log 记过程、Snapshot 看当前、Checkpoint 是安全恢复点,三者不能互相替代。

Workspace 是窗口外可寻址的工作记忆,通常分 inputs / scratch / state / artifacts / evidence / manifest;大结果放这里,活动上下文只留摘要和引用。模型写出文件不等于产物完成,还要经过校验、发布与归档。

五、业务知识怎么包装进上下文 ​

先分清三种资产的权威:Knowledge 提供组织事实(制度、产品、数据字典,带来源与时效)、Memory 提供经验(过去怎么处理、效果如何,带可信度)、Skill 提供方法(这类任务怎么做,经评审的步骤与脚本)。三者都能进上下文,但权威责任不同。

包装形态按问题类型选,不要一律切块:

业务知识推荐形态为什么
政策/条款条款卡片:编号、生效期、适用范围、原文引用需要精确引用与时效判断
产品/实体事实卡:主键、别名、属性、关系需要消歧与多跳查询
操作流程SOP:前置条件、步骤、失败分支、验收模型要按顺序执行
规则判断决策表:条件组合 → 结论/动作比自然语言更少歧义
术语/案例词表:定义、同义词;案例卡:场景、做法、结果、边界统一口径,经验不冒充事实

注入方式有四种,按“需不需要、急不急、贵不贵”选:常驻注入(平台规则、术语表、当前目标,放前缀)、检索注入(按问题召回条款/案例,带来源与分数)、指针注入(只给摘要 + 文档 ID/章节/行号,需要时取原文)、工具获取(把知识做成查询接口,适合结构化数据与权限控制)。Context Package 就是“摘要 + 关键事实 + 指针”的组合:摘要维持全局理解,事实保证精确,指针指向原文。

json
{
  "task": {"goal": "判断订单能否退款", "stage": "eligibility_check"},
  "facts": {"order_id": "A-1024", "amount": 399, "status": "paid"},
  "knowledge": [{"id": "policy-3.2", "effective_from": "2026-07-01", "scope": "tenant-a",
    "summary": "签收后 7 天内可无理由退款", "pointer": "kb://policy/3.2#L40-L58"}],
  "memory": [{"claim": "上次因缺少物流单号被拒", "confidence": 0.8}], "open_questions": ["是否已签收", "物流单号是否上传"]
}

四条硬规则:权威优先于相似(制度条款高于历史案例,当前版本高于旧版本);时效显式化(带生效期与失效期,过期内容不注入或明确标注);作用域先于检索(租户、项目、密级在召回前过滤);引用可回溯(结论能指回原文位置,没有指针的摘要只能当提示)。

六、治理:作用域、版本、删除与反退化 ​

所有资产都要有作用域(Global/Tenant/Project/User/Session/Task)、版本和删除语义。三个污染要单独防:记忆污染(错误推测被当经验写回)、指令污染(外部文本被提升为行为规则)、跨租户污染(索引/缓存/摘要把一租户信息带进另一租户)。

“删除”不是只删向量:从稳定资产 ID 追派生链,禁读取、删原文、清索引/缓存/摘要、更新 Manifest 与 Skill,保留最小审计记录,再跑验证。资产变更与代码走同一条发布链:结构校验 → 受影响分析 → 离线回放 → 灰度 → 观察成本/延迟/引用准确性/泄漏 → 保留、修订或回滚。

图里有两个闭环:模型侧“编译 → 调用 → 提交 → 压缩 → 重建”,以及数据侧“大结果卸载 → 按需取回”。Validate 不通过回到 Commit,而不是硬着头皮继续调用,这是压缩能不能长期跑下去的分水岭。

最小上手 ​

yaml
stable: [platform_policy, agent_contract, glossary]
semi_stable: [tool_manifest, project_rules]
dynamic: [task_state, retrieved_knowledge, memory_hits, recent_turns]
budget: {reserve_output: 8000, compact_at: 0.80, unload_single_over: 20000}
never_compress: [plan_state, permissions, pending_approvals]
knowledge: {mode: pointer_first, require: [source, version, effective_from, scope]}

跑通判据:连续 20 轮不溢出;压缩后模型仍能说出目标、未决项和下一步;把一条知识的作用域改错,系统能在召回前拦住;任取一条结论,能通过 Manifest 追到原文与版本。

生产动作 ​

  • 监控四个量:窗口占用率、缓存命中率、单轮输入 token、压缩/卸载触发次数;命中率骤降先查前缀与工具清单是否变化。
  • 固定与动态物理分开:时间戳、随机数、每轮变化的记忆不要写进系统前缀;会变的内容放消息侧或附件。
  • 大结果先落盘:日志、长文档、生成代码写 Workspace,只留头尾摘要和引用;卸载阈值太低会导致反复读取,反而更贵。
  • 知识入库带元数据:来源、版本、生效期、作用域、密级、owner 缺一不可,否则无法做冲突裁决和真删除。
  • 压缩后做续行验证:用固定用例检查目标、权限、证据、下一步是否还在;只测摘要长度,不测可继续执行,等于没测。每轮实际发送的 system/messages/tools 与 Manifest 都留,排障先看模型实际看到了什么。

边界 ​

  • 不是所有场景都要分层:单轮问答、固定模板、几轮内结束的任务,过度设计只会增加维护成本。
  • 不替代 RAG:RAG 解决“从哪里找”,上下文工程解决“找到后怎么装、装多少、以什么权威装”。
  • 摘要必然有损,窗口外状态不是免费:原始记录必须保留,关键事实不能只活在摘要里;Workspace、Checkpoint、Manifest 都增加存储与一致性成本,没有恢复需求时先别引入。
  • 权限不能靠提示词:上下文里写“不要泄露”不是边界;真正的隔离在召回前的作用域过滤与工具网关。

讲清楚 ​

上下文工程的命题是“窗口有限,任务世界持续增长”。解法是每次调用前编译本轮视图:系统侧按平台策略、Agent 契约、租户规则、运行时提醒、Skill、工具描述分层,任务侧放目标、计划、历史、检索结果和 Workspace 引用;冲突按权威裁决,不按排列顺序。编译是一条确定性管线:先权限过滤再排序,再分预算、压缩、生成 Manifest;历史走 Commit→Compact→Rebuild→Validate,关键事实先进权威状态再出窗口,大结果卸载到 Workspace。

业务知识按条款卡、事实卡、SOP、决策表、词表、案例卡包装,在常驻、检索、指针、工具获取之间选注入方式。

Knowledge 供事实、Memory 供经验、Skill 供方法,都要带作用域、版本和删除语义。判断上下文工程做得好不好,不看塞了多少,而看模型能否用最小充分集完成任务、结论能否回溯、压缩后能否继续执行。

适用:2026 年主流模型与 Agent 工具链 | 初稿 2026-10

下篇 · 压缩与卸载 ​

为什么需要 ​

上下文工程决定“这一轮该看见什么”,压缩决定“看不见的部分怎么处理”。不做压缩,长任务会在三个地方坏掉:

  • 溢出:工具结果越堆越多,某一轮直接超窗,请求失败;
  • 稀释:历史越长,当前目标在注意力里的权重越低,模型开始复述旧计划;
  • 成本:每一轮都把全部历史重发一遍,token 账单随轮次线性上涨。

直觉方案是“超过阈值就摘要”。但摘要本身会引入新故障:把未决审批压没了、把工具调用和结果切成两半、把“已确认的订单号”写成“用户提供了订单信息”、摘要越压越偏。所以压缩不是一次模型调用,而是一条带校验和熔断的流水线。

核心机制 ​

一、先分预算,再谈压缩 ​

压缩的对手不是“历史”,是四类内容在抢同一个窗口:

内容特点预算策略
系统前缀(平台策略、Agent 契约、工具清单)几乎不变,可缓存固定下限,不参与压缩
当前目标、计划、未决项短但关键固定下限,永不压缩
证据与工具结果体积大、时效强动态额度,优先卸载
历史叙述(多轮对话)可摘要、可丢弃细节唯一的“可压缩池”

一条可用的算式是:可用上下文 = 窗口 − 输出预留 − 安全余量。例如 200K 窗口、输出预留 20K、工具与下一轮观察余量 13K,可用约 167K。先把这个数算出来,再决定各类内容的额度;不先算账,压缩就会变成“哪段看起来长就删哪段”。

二、触发:一个阈值不够 ​

只按“占比超过 80%”触发,会被两种流量绕过:一次返回 5 万 token 的工具结果,瞬间越过阈值;或者每轮很短但轮次极多,占比一直不高,缓存却越来越贵。生产上通常四个维度同时看:

  • 占比:50% 预警、80% 触发压缩;
  • 绝对量:单条结果超过 20K 先卸载,不看总占比;
  • 轮次/时长:超过 N 轮或 T 分钟,主动整理一次;
  • 成本:单会话累计成本超过预算,降级到更便宜的模型或更激进的压缩。

四个维度各自解决一种绕过方式,缺一个就会在特定流量下失效。

三、策略谱:从零成本到高成本 ​

压缩策略不是越高级越好,而是按“代价 / 丢什么 / 什么时候用”排序,能用便宜的就不用贵的:

策略代价丢什么适用
机械清理(删旧工具结果、去重复)零细节,需要时可重取默认第一步
滑动窗口(只留最近 N 轮)零远期目标与结论短任务、闲聊
结果落盘(大结果写 Workspace,留摘要+引用)零窗口内不可见日志、长文档、生成代码
结构化摘要(固定字段替换叙述)一次模型调用措辞与部分因果长任务的主手段
事实抽取进状态(订单号、金额、决定)一次抽取上下文中的原话有权威状态的系统
检索式回顾(旧历史进索引,按需召回)索引与查询成本不召回就不可见超长项目、跨会话
子 Agent 隔离(把长过程放到独立上下文)多一次调度中间推理细节调研、批处理
重放重建(从状态和证据重建视图)最高全部叙述压缩已不可信时

顺序上有一个原则:先零成本的,后模型的;先卸载,后摘要。先摘要再卸载,等于花一次模型调用去概括一堆马上要扔掉的内容。

四、四道防线 ​

压缩之所以危险,是因为它不可逆。四道防线缺一不可:

  1. 先提交后压缩:把结构化事实(目标、决定、参数、未决项)写进 Task State 或 Workspace,再动历史。顺序反了,摘要里就没有可提交的事实。
  2. 切点安全:不能把工具调用与它的结果切开,不能把一次审批的请求与批复切开;切点选在“步骤边界”,不是 token 边界。
  3. 压缩后校验:用固定问题检查目标、未决项、权限、验收证据是否还在;不通过就回退到压缩前。
  4. 熔断:连续 N 次压缩失败或校验失败,停止自动压缩,转人工或降级到“只卸载不摘要”。

图里的关键在 先提交事实 这个判断:它是压缩与“删聊天记录”的分界线。校验失败回退而不是硬撑,是这套机制能长期跑下去的前提。右侧虚线是另一条出路:卸载只把大结果换出窗口、留下摘要与稳定指针,需要时再按区段取回,和压缩共用同一次视图重建。

五、压缩会打掉缓存 ​

前缀缓存按“前缀是否逐字节一致”命中。压缩一旦改动系统前缀或早期消息,缓存立刻失效,下一轮全量 prefill——省下的窗口可能还不够付这次重算的钱。

所以工程上要分开处理:会变的内容放消息侧,不放系统前缀;压缩优先在消息侧进行;压缩完接受一次缓存冷启动,并把这次冷启动计入成本模型。反过来,如果压缩频率高于缓存有效期,说明预算分配有问题,应该先调额度而不是更频繁地压。

六、卸载与按需取回 ​

卸载是把内容移出窗口,不是删除。最小实现是“摘要 + 指针”:窗口里留一段 200 字的结论和 workspace://logs/run-42 这样的地址,模型需要细节时用工具取回指定区段。

三个约束决定卸载是否可用:取回要便宜(一次工具调用能拿到,不要多层翻找);指针要稳定(文件被覆盖就再也取不回来);要有容量上限(Workspace 无限增长,磁盘和检索成本会反过来咬人)。

最小上手 ​

yaml
compression:
  budget: {reserve_output: 20000, safety: 13000, compact_at: 0.80, warn_at: 0.50}
  unload_single_over: 20000
  order: [mechanical, offload, summarize, extract_state]
  cut: step_boundary
  never_compress: [goal, plan_state, pending_approvals, permissions]
  validate: [goal_present, open_items_present, evidence_present]
  circuit_breaker: {max_consecutive_failures: 3, on_open: unload_only}

验收不看“压到多少 token”,看四件事:连续 20 轮不溢出;压缩后模型仍能说出目标、未决项和下一步;工具调用与结果没有被切断;缓存命中率没有因为压缩长期掉到阈值以下。

生产动作 ​

  • 保留原始记录:压缩结果只是视图,原始消息、工具返回、压缩前后快照都要留;排障时先看模型实际收到了什么。
  • 摘要带来源:每条摘要标注它概括了哪几轮、依据哪些工具结果;没有来源的摘要无法复核,也无法追责。
  • 按任务类型配策略:代码任务重“文件与 diff”,调研任务重“结论与引用”,客服任务重“事实与承诺”;一套阈值打天下必然有一类任务吃亏。
  • 监控三个转化率:触发→实际压缩、压缩→校验通过、卸载→被取回。取回率过高说明卸载太激进,过低说明卸载的内容根本没用到。
  • 把压缩当发布物管理:摘要模板、字段、阈值都版本化;改模板要跑固定用例,不能凭“感觉更简洁了”上线。

边界 ​

  • 短会话不需要这套:几轮内结束、窗口用不到一半的任务,加压缩只会增加故障面。
  • 摘要不能替代权威状态:关键事实必须落在外部状态里;只活在摘要里的信息,下一次压缩就可能消失。
  • 压缩不是记忆系统:记忆解决“跨会话记住什么”,压缩解决“本次会话窗口怎么腾”;两者共用存储,但策略不同。
  • 过度压缩比不压缩更贵:压得太狠会反复取回、反复重读,总成本反而上升;用取回率和续行成功率判断。
  • 阈值没有普适值:50%/80% 只是起点,模型窗口、工具结果大小、任务长度都会改变最优点。

实战场景 ​

场景:排障“模型没看到关键信息”——客服 Agent 误判退款资格 ​

现象:退款工单会话里,用户第 6 轮明确说过“已经签收三天了”,Agent 在第 22 轮仍回复“无法确认签收状态,请补充物流信息”。像是模型变笨,但要先回答:这一轮模型到底看见了什么。

排查路径(上下文装配清单法,逐段对 Manifest):导出该轮实际发送的 messages 与 Manifest,按片段核对 source / scope / permission_basis / selected_reason / transform:

片段Manifest 记录结论
平台策略 / 当前目标常驻注入,token_count 正常没丢
用户消息“已签收三天”本轮 Manifest 中无此片段,装配日志显示被 L1 机械清理滑窗裁掉关键事实只活在叙述历史里,从未 Commit 到 Task State
物流查询工具结果scope: logistics-service,permission_basis: 空召回前被作用域过滤拦下:工单角色无物流明细注入权限,日志里只有一次静默 drop

两类根因对应两类修法:

yaml
commit_before_compact:
  required_fields: [delivery_status, delivered_at, refund_amount, open_items]
  target: task_state            # 签收确认一出现就落权威状态,不允许只存在于消息流
manifest_audit:
  on_scope_drop: log_with_reason  # 每 drop 一段都记原因与依据;「没注入」必须可查询,否则排障只能靠猜

案例:压缩顺序事故——报销助手把“仅限/除外”压没了 ​

现象:差旅报销助手在压缩后把“仅限高铁不可达时打车可报;节假日出行除外”执行成“出差打车都能报”,一笔违规报销入账。“仅限”“除外”两处限定条件整体丢失,结论反转。

排查路径:对比压缩前后快照。规则原文一直在叙述历史里,L3 结构化摘要按“决定/理由/下一步”三字段模板概括——限定条件寄生在“理由”字段,概括时被当成措辞丢掉;压缩后校验通过,因为校验项只查目标与未决项,没查规则限定的完整性。事故现场每一步都合流程,但流程顺序是反的:先压了叙述,事后已无可提交的结构化事实。

修法:把“先提交事实、再压缩”落成四步实操顺序:

  1. 规则句抽取为决策表字段 condition → action → exception,写入 Task State(权威层),压缩不再接触这些字段;
  2. 压缩只作用于剩余叙述,产物以字段 ID 引用事实,不复述原文;
  3. 校验做字段级断言:压缩后模型必须能回答“哪些情形不可报、上限金额多少”,答不出即失败;
  4. 压缩熔断:连续 3 次校验失败(经验值,需校准)停止自动摘要,降级为“只卸载不摘要”并转人工复核。
python
facts = extract_facts(turns, schema=DECISION_TABLE)     # condition/action/exception 字段级提交
task_state.commit(facts)                                # 必须先于 compact
summary = compact(narrative_excluding(facts), cite=[f.id for f in facts])
if not validate(summary + task_state.view(), probes=["哪些情形不可报", "上限金额"]):
    rollback(pre_compact_snapshot); breaker.record()    # 连续 ≥3 → circuit_breaker: unload_only

设计依据:“仅限”“除外”“上限”类限定词是压缩中最脆弱的一层——短、低频、摘要视角下“可省”。所以提交粒度必须是字段级而非段落级;校验必须问结论边界,不能只问“目标还在不在”。

场景:卸载与回读——Coding Agent 面对 5 万 token 的构建日志 ​

现象:一次 CI 构建失败返回约 50K token 日志。留在窗口里,占比骤升、其余片段被注意力稀释,后续可用轮次大幅缩水;无脑卸载只留摘要,模型就基于摘要猜错误原因,修复方向反复跑偏。两种失败形态都真实出现过。

决策依据:不是“后面还会不会用到”,而是下一步决策是否直接依赖具体区段。按这个口径分三档:

情形动作为什么
错误唯一,摘要已含文件+行号卸载,留摘要+指针,不回读决策所需信息已被摘要覆盖,回读白花一次工具调用
多因交织,或模型假设与摘要冲突卸载+read_range 按指定区段回读回读成本只花在决策真正发生的地方
同一区段回读 ≥3 次(经验值,需校准)把该区段 pin 回活动上下文取回率过高说明卸载过激,呼应正文“三个转化率”监控
yaml
offload:
  single_result_over: 20000                        # 经验值,需校准;低于此值直接留窗口
  summary: {head: 40, tail: 40, error_lines: all}  # 头尾加全部报错行,其余段落不留
  pointer: workspace://logs/build-1187             # 指针稳定:构建日志不可变,禁止同名覆盖
  read_back: {tool: read_range, max_per_round: 2}  # 取回一次要便宜;限流防多层翻找
  repin_after_reads: 3

回读时机还受压缩牵制:pin 回的区段属于“证据”,走动态额度、永不摘要;一旦阶段推进、该证据被已提交的决定取代,就随下一次机械清理退出窗口。卸载与回读是同一闭环的两半,不是两个独立开关。

讲清楚 ​

压缩的命题是“信息必然有损,但任务必须能继续”。做法是先算可用上下文,再给四类内容分额度;从机械清理、卸载、摘要到状态抽取,按成本从低到高选,先零成本后模型。触发要看占比、绝对量、轮次和成本四个维度,单一阈值会被长工具结果或高频短轮次绕过。

真正的保障是四道防线:先提交事实再压缩、在步骤边界切、压缩后校验目标与未决项、连续失败就熔断。压缩会打掉前缀缓存,所以会变的内容要放消息侧,并把缓存冷启动计入成本。卸载不是删除,靠“摘要 + 稳定指针 + 取回成本可控”才成立。

判断压缩做得好不好,不看压掉了多少 token,而看三件事:压缩后还能不能继续执行、结论能不能回溯、总成本有没有真的下降。

适用:2026 年长会话 Agent;阈值与比例需按模型窗口和任务分布校准 | 初稿 2026-10

把 AI 工程讲到既懂也会用