Hello Agents - 上下文工程
上下文工程是什么?
所谓“上下文”,是指在对大语言模型(LLM)进行采样时所包含的那组 tokens。手头的工程问题,是在 LLM 的固有约束之下,优化这些 tokens 的效用,以便稳定地得到预期结果。
上下文工程 vs. 提示工程
提示工程关注如何编写与组织 LLM 的指令以获得更优结果(例如系统提示的写法与结构化策略);而上下文工程则是在推理阶段,如何策划与维护“最优的信息集合(tokens)”,其中不仅包含提示本身,还包含其他会进入上下文窗口的一切信息。
上下文腐蚀(context rot)
是指随着上下文窗口中的 tokens 增加,模型从上下文中准确回忆信息的能力反而下降。因此,上下文必须被视作一种有限资源,且具有边际收益递减。
上下文工程的目标与实现
用尽可能少、但高信号密度的 tokens,最大化获得期望结果的概率。
- 系统提示(System Prompt):语言清晰、直白,信息层级把握在“刚刚好”的高度。常见两极误区:
- 过度硬编码:在提示中写入复杂、脆弱的 if-else 逻辑,长期维护成本高、易碎。
- 过于空泛:只给出宏观目标与泛化指引,缺少对期望输出的具体信号或假定了错误的“共享上下文”。
- 工具(Tools):工具定义了智能体与信息/行动空间的契约,必须促进效率:既要返回token 友好的信息,又要鼓励高效的智能体行为。工具应当:
- 职责单一、相互低重叠,接口语义清晰;
- 对错误鲁棒;
- 入参描述明确、无歧义,充分发挥模型擅长的表达与推理能力。
- 示例(Few-shot)
长时程任务的上下文工程
无限增大上下文窗口并不能根治“上下文污染”与相关性退化的问题,因此需要直接面向这些约束的工程手段:压缩整合(Compaction)、结构化笔记(Structured note-taking)与子代理架构(Sub-agent architectures)。
压缩整合(Compaction)
- 定义:当对话接近上下文上限时,对其进行高保真总结,并用该摘要重启一个新的上下文窗口,以维持长程连贯性。
- 实践:让模型压缩并保留架构性决策、未解决缺陷、实现细节,丢弃重复的工具输出与噪声;新窗口携带压缩摘要 + 最近少量高相关工件(如“最近访问的若干文件”)。
结构化笔记(Structured note-taking)
- 定义:也称“智能体记忆”。智能体以固定频率将关键信息写入上下文外的持久化存储,在后续阶段按需拉回。
- 实践:上下文保存外部文件的索引+概括,而不是整个文件,需要用了再去读。
- 价值:以极低的上下文开销维持持久状态与依赖关系。
子代理架构(Sub-agent architectures)
- 定义:由主代理负责高层规划与综合,多个专长子代理在“干净的上下文窗口”中各自深挖、调用工具并探索,最后仅回传凝练摘要(常见 1,000–2,000 tokens)。
三者评价:
- 压缩整合:适合需要长对话连续性的任务,强调上下文的“接力”。
- 结构化笔记:适合有里程碑/阶段性成果的迭代式开发与研究。
- 子代理架构:适合复杂研究与分析,能从并行探索中获益。
上下文管理器设计
候选信息包
ContextPacket 是系统中信息的基本单元。每个候选信息都会被封装为一个 ContextPacket,包含内容、时间戳、token 数量和相关性分数等核心属性。
1 | Attributes: |
上下文生成流水线
这边叫GSSC(Gather-Select-Structure-Compress)流水线,它将上下文构建过程分解为四个清晰的阶段。
1.Gather:多源信息汇集
其实就是做出一份候选上下文列表,进行评级、裁剪、融合。核心机制是 优先级+数量限制。
流程:
- 系统指令:有就先放进去(最高优先级),直接给满分相关性
1.0,确保一定保留。 - 记忆检索:调用
memory_tool按user_query搜索(limit 10),把结果解析成ContextPacket加入;失败就告警但不中断。 - RAG 检索:调用
rag_tool搜索知识(limit 5),解析后加入;失败同样只告警。 - 对话历史:只取最近 5 条,每条包装成
ContextPacket,给一个基础相关性0.6。 - 自定义包:外部传进来的
custom_packets直接追加。
2.Select:智能信息选择
_select() 负责把 _gather() 收集到的候选信息 打分、排序、过滤,再在 token 预算内 挑出最值得放进最终上下文的一组(系统指令优先保留)。
流程:
1. **系统指令单独处理**:先把 `type=system_instruction` 的包分离出来,永远保留且不参与竞争。
2. **算 token 预算**:系统指令先占坑,剩余 token 不够就直接只返回系统指令。
3. **给其他包算综合分**:
- 相关性:若还是默认值 `0.5`,就用 `_calculate_relevance()` 重新算
- 新近性:用 `_calculate_recency()` 算时间越近分越高
- 综合分:`relevance_weight * relevance + recency_weight * recency`
4. **过滤低质量**:相关性低于 `min_relevance` 的直接丢掉。
5. **排序 + 贪心选入**:按综合分从高到低依次加入,直到 token 用满为止。
总结:
- 评分机制:相关性 + 新近性加权(可配置权重)
- 贪心选择:预算有限时,优先塞“单位价值最高”的内容
- 过滤机制:用
min_relevance把低相关噪声挡掉
3.Structure:结构化输出
_structure() 把选中的 ContextPacket 按类型分组,再拼成一个带分区标题的 结构化上下文模板字符串,用于最终喂给模型。
流程:
- 分类分组
system_instruction→ 放到system_instructionsrag_result / knowledge→ 当作证据放到evidence- 其他 → 放到一般
context
- 按固定模板输出 5 个区块
[Role & Policies]:系统指令(如果有)[Task]:用户问题user_query(始终有)[Evidence]:检索到的知识/证据(用---分隔)[Context]:对话历史/自定义信息等背景[Output]:输出要求(提示“基于以上信息回答”)
最后用空行把各区块连接成一个完整 prompt。
4.Compress:兜底压缩
_compress() 是上下文超出 token 上限时的兜底压缩:尽量按分区保留结构,只在必要时截断最后一部分内容,让最终上下文不超过 max_tokens。
流程:
- 先估算 token:
_count_tokens(context),没超限就直接返回。 - 按分区处理:用空行
\n\n把上下文切成多个 section(对应你前面的结构化分区)。 - 贪心保留:从前到后逐段加入,只要不超 token 就“整段保留”。
- 需要超限时:
- 计算剩余 token(
remaining_tokens) - 如果剩余还够 50 tokens,就对该 section 做截断(
_truncate_text),并追加提示"[... 内容已压缩 ...]" - 然后停止(后面的分区不再保留)
- 计算剩余 token(
- 输出压缩结果:拼回文本,并打印压缩前后 token 数。
结论:
基本就是先分区压缩,还不够就直接从5个区块从上往下放,token一不够,直接截断,抛弃后面内容。

