上下文工程

本页目录

输出契约明确后,下一步是决定本轮给模型哪些材料。上下文工程管理当前可见的信息:保留约束与必要证据,给输出留空间,复用稳定前缀,并把长历史整理为能回读原始产物的摘要。它与持久化、检索和模型权重各有边界。

知识与状态的三个层次

模型的语言能力和大量知识来自训练后的权重;上下文则提供当前问题、约束和可用证据。不能把“请求之间不自动记住聊天内容”解释成“模型全部知识都在这次输入里”。另一个独立层次是应用维护的会话历史、文件和数据库:它们可以长期存在,但存在并不等于已经进入当前推理。

模型权重训练形成的参数与能力应用持久状态文件、数据库、历史记录选择与组装检索、读取、摘要本轮生成权重与上下文共同参与本轮上下文当前实际可访问的材料存在数据库里的记录,只有被读取并提供给本轮推理后,才能作为当前证据。知识、持久状态与本轮上下文是三个层次

例如,仓库里有一份最新测试报告,模型权重中不可能包含刚刚生成的报告内容。应用必须读取报告,把相关结果交给模型;如果仅传一个路径,模型通常需要调用工具才能获取正文。文件可以帮助跨会话恢复,却不会自动扩大单次上下文。

“API 无状态”也要限定到接口。显式传历史的消息接口通常由调用方组织续话;托管会话可能在服务端保存和补入历史,推理服务还可能复用前缀状态。对应用更有用的问题是:⁠本次推理实际接收了什么,谁负责补回历史,哪里保存可恢复的真实状态? Claude 的窗口文档区分了训练知识与当前工作上下文,并说明输入、输出及 thinking 的具体计数规则。Claude 上下文窗口

窗口预算与输出预留

先给下一步留空间

先确认目标模型的输入、输出限制及计数规则,再分配材料。以下是一个假设输入与输出合计上限为 128,000 token 的规划例子,K 在本表表示 1,000;不代表某个模型的固定配置。

项目预算保留理由
系统约束与工具定义8K决定任务边界与可用操作
近期历史与任务摘要32K保留当前方案和关键决定
检索证据24K支持本轮判断,带来源位置
当前问题1K用户本轮新增要求
生成预留16K依接口规则包含回答、推理和工具调用输出
安全余量8K给计数误差与下一步材料留下空间
合计89K距离上限还有 39K

输入部分是 65K。若下一次工具返回 50K 日志,计划总量会变成 139K,超出 11K。此时应先过滤日志或读取关键区间,而不是等请求报错再随意删除最早的历史。流式输出改变交付方式,不会增加窗口容量。

可以用下面的独立算例检查预算账。它只验证算术,实际请求仍要用目标模型的计数工具处理消息模板、工具定义和多模态内容。

limit = 128_000
parts = {"rules_tools": 8_000, "history": 32_000,
         "evidence": 24_000, "question": 1_000}
reserve, margin = 16_000, 8_000
planned = sum(parts.values()) + reserve + margin
assert planned == 89_000
assert limit - planned == 39_000
incoming = 50_000
print("需要至少削减", max(0, planned + incoming - limit), "token")
# 需要至少削减 11000 token

不同分词器不能用固定倍率互换。即使用供应商计数端点,也应把结果视为请求前估计,并用响应 usage 校准;Claude 官方明确指出两者可能存在少量差异。Token counting

材料放得下,下一步还放得下吗?

预算图里的固定 49K 包含规则、历史、问题和安全余量;输出预留与证据独立调整。超限是算术结果,删掉哪部分则是证据选择问题。不能为了让条形图缩短,就删除会改变结论的限制条件。

正在呈现知识画面
材料放得下,下一步还放得下吗?

输入、工具结果、输出预留与余量共同占用规划预算;缓存命中不删除逻辑上下文。

按证据需求选择材料

上下文选择首先是证据覆盖问题。问“某函数在哪里定义”,通常只需定位符号及少量邻近代码;问“迁移方案是否遗漏兼容性要求”,则可能需要同时读取多个完整章节。检索命中两段却漏掉否定条件,会比完整读取更危险。

任务材料选择必须验证的边界
定位事实或符号检索片段加邻近上下文版本、同名对象、限定条件
比较少量完整文档文档整体或章节整体是否截断附录、例外与定义
跨大量资料综合分阶段检索、归并证据再生成多跳关联是否覆盖、结论来源是否可追溯
持续修改工程当前文件、需求、差异、验证结果摘要是否仍对应最新工作区

以“这个配置是否适用于生产环境”为例,检索可能命中示例中的 enabled: true,但限制说明在前一段:“仅限单节点测试”。证据单元应包含配置及其适用条件,而不只是包含关键词的行。每段材料附文件或文档标识、版本、位置;冲突证据并列保留,让模型识别冲突,必要时再读取原文。

检索内容是待分析的数据,不会因为出现在上下文里就获得执行指令的权限。网页中出现“忽略先前要求”时,应保留来源身份并按资料内容处理;任务权限由应用的可信配置与用户授权决定。相关执行边界见 安全与防护。

研究中观察到关键证据放在长输入的不同位置会影响表现,部分任务在中部更易退化。这支持做位置敏感性测试,并不证明所有模型都“看不见中间”,也不保证把结论复制到末尾就正确。Lost in the Middle

缓存的复用边界

KV cache 是推理中复用注意力历史状态的机制;prompt caching 是服务对重复提示前缀提供的复用能力,可能在底层使用这类状态。两者处于不同抽象层,并非完全无关。API 缓存命中也不表示模型“记住了答案”:变化的后缀仍需生成。

请求 A工具定义系统与稳定材料用户问题 A请求 B工具定义系统与稳定材料用户问题 B前缀内容及缓存条件相同缓存命中节省前缀计算或费用;这段输入仍然占用模型的上下文窗口。相同前缀可复用;变化发生后,后续状态需要重新计算

在前缀缓存中,工具定义和稳定指令适合放在动态消息之前。若每次在最前面插入随机 ID,后续本来相同的材料也可能无法复用;若只修改最后的问题,之前的共同前缀仍有复用机会。这里的边界还受缓存粒度、有效期、模型和服务配置影响,不能只比较可见正文。

以 Claude 为例,缓存涉及 tools、system、messages 的层次,命中还受最小长度、缓存断点及有效期约束;可从 cache_read_input_tokens 等 usage 字段核验。费用和限制会随模型、平台变化,接入时查对应页面,不把某个价格倍率当成缓存机制本身。Prompt caching

改动发生在哪里,复用就止于哪里?

把动态 ID 放在最前面,与只修改末尾问题做对照。前缀的依赖关系说明了稳定材料放置的价值;真实命中还必须满足服务的版本、计数、有效期与配置条件。

正在呈现知识画面
改动发生在哪里,复用就止于哪里?

后面的相同文本不能越过前面的变化继续当作共同前缀;复用边界与文本总相似度不同。

把历史压缩成可恢复状态

摘要的目标是让下一轮能继续做事。单纯写“已经讨论并修复了若干问题”几乎没有恢复价值,因为它没有说清修了哪里、验证过什么、下一步依赖什么。

假设任务正在修复导入器:旧方案已被否决,当前补丁已落盘,但仅运行了单元测试。一个有用的摘要应明确区分事实、未完成项和约束:

{
  "goal": "修复导入器重复提交问题",
  "constraints": ["保持现有文件格式", "先不改公共接口"],
  "current_artifacts": ["src/importer.py", "tests/test_importer.py"],
  "decisions": ["使用输入批次 ID 去重;不使用进程内集合"],
  "verified": ["单元测试通过;详细结果在 artifacts/unit-test.txt"],
  "pending": ["验证进程重启后的重复提交"],
  "next_action": "读取当前补丁与测试报告,补做恢复场景验证"
}

这是应用自定义状态格式,不是供应商的 compaction 协议。恢复时先核对文件和报告是否仍存在、是否对应当前修改;摘要中的“测试通过”不能替代测试产物,更不能自动代表后续修改也通过了测试。

操作保留什么主要损失或要求
裁剪旧输出当前相关片段与原文定位被删除细节需重新读取
压缩历史目标、约束、决定、证据索引和下一步摘要可能遗漏或误写,关键状态需核验
持久记忆跨会话确实有用的稳定信息要处理版本、过期与权限

若使用服务端 compaction,必须按该接口保留和回传其结构化状态,不能把任意摘要 JSON 塞进协议块。具体兼容范围和流程见 Claude Compaction;跨会话设计见 记忆与状态。

如何验证一次上下文改动有效

固定一组任务与答案依据,比较原始历史、压缩历史、检索材料三种输入。记录任务正确率、必要证据召回、输入量、首个可见输出延迟及总成本;再做一次“压缩后恢复”,检查是否重复已经失败的方案、遗漏用户约束或宣称未完成的步骤已完成。

如果输入量降低而恢复错误增多,说明删掉了关键状态;如果输入量升高但正确率不变,检查是否重复保留了工具输出;如果事实回答正确但无法给出支持位置,检查证据索引。这样才能判断上下文选择改善了任务,而不只是让 prompt 看起来更整齐。后续评估方法见 评估与可观测。

继续阅读:检索、RAG 与证据链。