跳转至

第 3 章 上下文工程

模型每轮只能看见请求里的那点内容,而请求每轮都要重发。于是"上下文"既是模型的工作内存,也是 harness 最大的一笔开销:装什么、装多少、什么时候压缩、什么内容不许信,直接决定一次运行能不能收敛。本章把上下文当成有限预算来管,给出分配、压缩、检索、外置与信任分级五组判据。

1. 上下文是有限预算

窗口是输入加输出的硬上限,但可用额度小于名义窗口:系统提示、工具定义、检索内容、历史、输出预留都要从同一份额度里出。更关键的是,历史每轮重发,单次任务的输入量随轮数近似线性增长,成本与延迟随之上升。上下文因此不是存储,而是工作内存——存长期事实的地方在窗口之外。

重发也意味着“改动历史就是改成本”:把一段已读过的工具输出从历史里换成摘要,后续每一轮都在省这笔 token;反之,把原文一直留着,它的成本会随剩余轮数倍增。前缀缓存能降低重发成本,但不是免费:命中要求前缀逐字稳定,因此系统提示与工具定义要放在最前且不随意改动,动态内容全部后置。

一条成本判据:单任务开销约等于轮数乘以平均上下文规模。轮数由任务决定,平均规模由上下文工程决定;后者是可控项,也是压缩与检索存在的理由。

2. 预算分配与预留

把额度分成五份,按"固定项 → 输出预留 → 可变项"的顺序分配:

份额 性质 处理
系统提示 固定,承载不变量 不压缩、不截断
工具定义 固定,随工具数量增长 超限时懒加载或分组(第 4 章)
输出预留 固定下限 先留足,再分配其余
历史 增长 压缩的主要对象
检索与观测 可变 按需注入,带引用

先算固定项与输出预留,剩下的才是历史与检索的可用额。顺序颠倒的典型症状是:历史塞满窗口,最后一步结构化输出被截断,报错却显示为"JSON 不完整"。

输出预留按最坏情况算:结构化输出(JSON、补丁、表格)往往比自然语言回答长数倍,留少了会在最后一步失败,而前面所有轮的成本已经花掉。工具定义的 token 要按实际序列化结果估,不能按字符数拍脑袋;工具增多时优先减工具,而不是压系统提示——不变量被截掉后,后面每一轮都会跑偏。

def budget(window, system_tokens, tool_tokens, reserve_out=2048, reserve_retrieval=1024):
    fixed = system_tokens + tool_tokens
    free = window - fixed - reserve_out - reserve_retrieval
    if free <= 0:                       # 固定项已超窗:先减工具,不是减系统提示
        raise ValueError(f"fixed={fixed} exceeds window={window}")
    return {"history": int(free * 0.7), "retrieval": int(free * 0.3)}

比例不是定值,按任务调:长对话任务给历史多些,检索密集任务给检索多些。但"输出预留"与"系统提示"两项不应参与比例分配。

3. 压缩、摘要与 compaction

压缩由三类事件触发:剩余额度低于阈值、单条工具结果超限、轮数超过阶段上限。手段按代价从低到高排列:截断单条结果、结构化摘要历史、丢弃中间轮、外置到文件后只留引用。

触发阈值要提前于上限:可用额低于两成时开始压缩,低于一成时强制压缩并拒绝再开新工具调用。压缩本身也消耗一次模型调用,因此不应每轮都做;按阶段压缩(每完成一个子任务)比按轮数压缩更自然。丢弃中间轮只丢“已论证过、结论已入摘要”的部分,保留工具结果的引用,否则无法回溯。

结构化摘要比自由摘要可靠,因为它保留了后续判断需要的槽位:目标是什么、已完成什么、关键证据在哪、还有什么未决。自由摘要容易把"为什么这样走"压掉,下一轮模型就会重复已经否掉的路径。

压缩的硬约束有两条:系统提示与用户的原始目标不压缩;被压缩掉的证据要留下可回溯的引用(文件路径与行号、命令原文、错误的关键行)。判据是压缩后仍能回答三个问题:做到哪一步、为什么这样走、下一步是什么。

4. 检索注入的判据

检索的目的是补事实,不是补指令。按优先级排序,相关性、时效性与权威性缺一不可;三者都不满足的片段不进上下文,哪怕它很长、看起来很全面。

条数要设上限并按位置排序:单次注入不超过预算份额的七成,留出空白给判断;同一来源的相邻片段先合并再去重,否则同一段文档会以多个切片反复占额。每段注入带来源与取用日期,过期内容默认降权,而不是默认删掉后让模型凭记忆补。

注入有三个常见故障:篇幅挤占历史、内容互相冲突、夹带指令。对应的判据是:每段注入都要能回答"为什么在这里",答不上就删;冲突片段要么并列并标注来源,要么只留权威一方;检索内容一律按数据对待(见「信任分级」)。

位置也有讲究:当前目标与最新观测放在靠后位置,参考资料放在中段,系统不变量放在最前。这样模型在生成本轮动作时,最近的目标与观测处于注意力较强的位置。

5. 外置状态:文件与任务列表

窗口之外最可靠的存储是文件系统。把计划、任务列表、中间结论写成文件,每轮从磁盘读取需要的那部分,而不是从历史里"回忆"。好处有四条:可审计(人能直接看)、可恢复(进程重启后接着跑)、不占窗口(只读需要的片段)、可并行(多个执行者读写不同文件)。

文件要分两层:一个索引文件写“有哪些文件、各自用途、何时更新”,其余内容分文件存;读时先读索引再按需读取片段(行区间、章节)。并发写靠“单一写者”或临时文件加原子重命名,不做多进程互相覆盖。

判据很直接:状态要么在窗口里(短、当前),要么在盘上(带路径),不能只存在于"模型记得"。只靠历史维持状态,压缩一次就丢;只靠文件维持状态,每轮都要付读取成本。两者按"当前轮用窗口、跨轮用文件"分工。

机器人任务天然适合外置:任务清单、地图、标定参数、上次执行结果都在外部系统里,harness 的职责是读取与引用,而不是把它们复述进上下文。

6. 记忆分层

按生命周期分三层:工作记忆是当前轮的上下文;会话记忆是压缩后的摘要与关键文件引用;长期记忆跨会话,需要显式写入与检索。

写入走“候选 → 确认 → 入库”三步:运行时只记候选(带来源与时间),阶段结束或任务完成后再筛选入库,入库时去重与合并同主题条目。长期记忆的检索按“时间近、使用频率高、与当前任务同域”排序,不把所有旧记忆一律注入。

长期记忆的写入判据有四条:稳定(不会很快过期)、可复用(下次任务用得上)、非敏感(不含凭据与隐私)、有来源(能追溯到哪次运行)。四条不全时宁可不写——错误的长期记忆比没有记忆更难纠正。

冲突处理要显式:新旧记忆矛盾时,保留时间戳与来源,不自动合并;由检索时的排序或人工确认决定采用哪条。把冲突藏在"最后写入获胜"里,等于把错误固化。

三层落到存储的常见分工:会话与检查点放关系库(PostgreSQL,事务与断点续跑都靠它);情景记忆的嵌入与元数据放向量库(Qdrant 一类,按相似度检索);实体关系放图库(Neo4j,多跳查询如"A 部件属于 B 设备");大对象与轨迹文件放对象存储(MinIO 一类)。判据是查询形态而不是流行度:要事务与一致性用关系库,要相似度用向量库,要多跳关系用图库——一个库硬扛三种查询,最后三种都做不好。

7. 位置效应与长上下文衰减

模型对上下文不同位置的利用并不均匀:开头与结尾通常更可靠,中段容易被忽略;干扰项越多,关键信息的召回越不稳定。窗口变大不会消除这个效应,只会让"塞进去但没用上"更难察觉。

位置效应不能靠“重要事情说三遍”硬碰:同一约束在系统提示与任务末尾各出现一次足够,重复多了反而挤占预算。判断内容是否真的被用上,看后续轮次是否引用了它——没被引用的大段资料,下一轮应该压成引用而不是继续留着。

工程对策是结构化放置:不变量放系统提示,当前目标放最近一条用户消息,历史摘要放中段,最新观测放末尾。另一个对策是少而准:能用检索取回的事实,不要用长历史维持;能用文件引用的内容,不要整段复制。

判据:长上下文是成本项而非能力项。上下文变长时,先问"这段内容是否必须在窗口里",再问"能否压缩成引用"。

8. 信任分级:数据不是指令

上下文里的内容来源不同,权限必须不同。系统提示与用户消息可以携带指令;工具输出、文件内容、网页文本只能作为数据。把三者混在一起,等于让任意一段被读取的文本获得改规则的权力——这就是提示注入(prompt injection)的入口。

信任等级要写进消息结构,而不只写在提示词里:不同来源用不同的角色或内容块类型承载,来源元数据随内容一起传到下一层;执行层按“这条动作请求最初来自哪类消息”判定,而不是按最终文本里写了什么。

落地手段有三层:显式分隔与标注(把外部内容包在带来源的块里)、指令来源校验(只接受系统与用户消息里的动作请求)、权限门兜底(由不可信内容触发的动作仍要确认,第 5 章)。分隔与标注降低误判概率,权限门保证误判不会直接造成不可逆后果。

判据:数据可以影响内容,不能改变权限。一条来自文件或网页的"建议"可以进入模型推理,但不能自动提升工具权限、不能改写系统提示、不能跳过确认门。

9. 要点

  • 上下文是工作内存,历史每轮重发;成本约等于轮数乘平均规模。
  • 预算按固定项、输出预留、可变项的顺序分配;系统提示不参与压缩。
  • 压缩保留槽位与引用:目标、已完成、关键证据、未决项。
  • 检索补事实不补指令;每段注入都要能回答"为什么在这里"。
  • 状态要么在窗口里,要么在盘上带路径,不能只靠模型记得。
  • 长期记忆按稳定、可复用、非敏感、有来源四条写入;冲突显式保留。
  • 长上下文是成本项;关键不变量前置,最新观测后置。
  • 数据可以影响内容,不能改变权限。

参考文献与延伸阅读

  • Liu, N. F., Lin, K., Hewitt, J., et al. Lost in the Middle: How Language Models Use Long Contexts. TACL, 2024. arXiv
  • Packer, C., Wooders, S., Lin, K., et al. MemGPT: Towards LLMs as Operating Systems. arXiv:2310.08560, 2023. arXiv
  • Model Context Protocol. 规范与文档. 规范