一次模型请求的输入,通常来自多个地方:系统指令、用户消息、历史工具结果、Skills,以及检索获得的知识。随着任务变长,这些内容也会不断被选择、裁剪和压缩。
因此,上下文构建值得拥有明确的职责:根据当前执行快照,生成一份协议合法、预算可控、来源可追踪的模型输入。
原始事实与派生视图
原始消息应该保留自己的生命周期。上下文模块可以选取其中一部分,生成摘要,或加入检索结果,但不应为了让本轮请求变短而直接覆盖原始历史。
这种分层也意味着:上下文模块负责组装,Message 域负责消息事实,知识存储负责长期材料。不同的数据不必因为最终都进入 Prompt,就共用相同的写入与保留策略。
先固定读取边界
构建开始时,应固定消息读取版本、模型能力、工具快照和相关配置。否则,在构建过程中碰上配置发布,就可能组合出一份从未被整体验证过的输入。
对需要恢复的任务,这个边界尤其重要。恢复策略应明确选择沿用原版本,还是经过可追踪的迁移后使用新版本。
裁剪不能破坏协议
预算计算不仅包含正文,还包含工具 Schema、多模态内容和预留的输出空间。裁剪时也要保持工具调用与工具结果的配对关系。
压缩应记录覆盖范围和来源引用。仅保存一段摘要文本,会让后续难以判断哪些消息已经被概括、哪些仍然需要保留。
spec = load_context_spec(run_id, committed_version)
history = select_complete_messages(spec)
evidence = retrieve_with_permissions(spec, principal)
blocks = fit_budget(instructions, history, evidence, tools)
validate_protocol(blocks, tools)
return ModelInput(blocks, tools), source_manifest(blocks)
这段伪代码表达的是职责顺序,具体预算与压缩算法仍需要结合模型能力验证。
留痕放在真正的模型边界
装配出来的输入可能还会被模型网关转换格式或被中间件改写。因此,排查“模型当时看到了什么”时,应关联最终序列化请求,而不仅是上下文模块输出的中间对象。
可以用请求引用、内容摘要和来源清单连接这两层。需要保存正文时,还应按数据权限与保留策略处理。
完整子图与伪代码见 上下文工程。