📖 本文是学习 Hello-Agents 项目时整理的笔记与思考。
拥有记忆和检索能力,只是 Agent 智能化的第一步。如何在有限的上下文窗口内,高效地组织、筛选和利用这些信息,才是决定 Agent 实际表现的关键——这正是上下文工程(Context Engineering)所要解决的问题。
文章已同步发布到微信公众号【浪浪山客栈】
https://mp.weixin.qq.com/s/C5qlbeuF2aZm8mjMxnYPJw
什么是上下文工程?
当我们为 Agent 赋予记忆模块(MemoryTool)和知识检索能力(RAG)之后,一个新的挑战随之出现:Agent 可能面对数量庞大、来源各异的候选信息,但 LLM 的上下文窗口是有限的。如何从海量候选中挑选出最有价值的内容,以最紧凑、最清晰的方式送入模型,直接决定了最终回答的质量。
上下文工程,就是系统性地解决"给模型看什么、怎么看"这一问题的方法论。
核心目标
用尽可能少、但高信号密度的 tokens,最大化获得期望结果的概率。
这个目标包含两层含义:
- 少:减少噪声信息、冗余内容对宝贵 token 预算的占用。
- 密:送入上下文的每一个 token,都应对模型的推理和输出产生正向贡献。
主要手段
实现上述目标,通常依赖三类核心手段:
- 压缩整合:对信息进行摘要、去重,在不损失关键语义的前提下缩短长度。
- 结构化笔记:以机器可解析、人类可阅读的格式记录任务状态与关键结论,便于后续按需检索。
- 子代理架构:将复杂任务分解给多个子 Agent,每个子 Agent 只处理与自身任务强相关的上下文,从源头上控制上下文规模。
GSSC:上下文构建的四阶段流水线
GSSC 是一套完整的上下文构建流水线,将上下文的生命周期划分为四个阶段:Gather(汇集)→ Select(选择)→ Structure(结构化)→ Compress(压缩)。每个阶段各司其职,共同保证最终进入模型的上下文既完整又精炼。
第一阶段:Gather(多源信息汇集)
Gather 阶段的目标是广撒网——从所有可能的信息源中汇集候选信息,形成待筛选的信息池。
信息源及其优先级:
| 优先级 | 信息源 | 说明 |
|---|---|---|
| 最高 | 系统指令 | Agent 的角色定义、行为准则,不参与后续评分,直接保留 |
| 高 | 记忆检索 | 从 MemoryTool 中检索与当前任务相关的历史记忆 |
| 高 | RAG 检索 | 从知识库中检索相关知识片段 |
| 中 | 历史对话 | 近期对话轮次,提供当前任务的对话上下文 |
| 低 | 自定义信息包 | 用户或系统注入的额外上下文信息 |
工程最佳实践:
- 容错机制:每个外部信息源都应包裹
try-catch,确保单个源失败(如 RAG 服务超时、记忆库连接异常)不会中断整体流程,Agent 可以在信息不完整的情况下降级运行。 - 优先级处理:系统指令拥有最高优先级,在后续所有阶段中均不被压缩或丢弃。
- 历史限制:对话历史只保留有限的最近几条(如最近 10 轮),避免长对话将上下文窗口填满,使当前任务相关信息无处安放。
第二阶段:Select(智能信息选择)
Select 是整个流水线的核心,直接决定了最终上下文的信息质量。它的目标是从 Gather 阶段汇集的候选信息中,智能筛选出最有价值的内容。
执行步骤:
- 分离系统指令:将系统指令从候选池中单独提取,优先保留,不参与评分竞争。
- 计算系统指令占用的 token:确认系统指令消耗的预算,得到剩余可分配的 token 上限。
- 为其余候选信息计算综合分数:综合相关性与新近性两个维度,为每条候选信息打分。
- 按分数降序排序:分数越高,信息越值得被优先纳入。
- 贪心填充:按分数从高到低依次选入,直至 token 总量达到预设上限。
工程实践要点:
- 评分机制:综合分数 = α × 相关性得分 + β × 新近性得分,其中权重 α、β 可针对不同任务场景进行配置。例如,对于需要历史追溯的分析任务,可以适当降低新近性权重;对于实时对话场景,则可提高新近性权重。
- 贪心算法:贪心策略在实践中被证明是效率与效果的良好平衡点——在不遍历所有组合的前提下,近似找到 token 预算内的最优信息子集。
- 最低相关性过滤:通过
min_relevance参数设定相关性阈值,低于阈值的信息即使 token 预算仍有余量,也会被过滤掉,避免噪声信息混入上下文。
第三阶段:Structure(结构化输出)
经过 Select 筛选后,我们手中拥有的是一批高质量的信息碎片。Structure 阶段的任务是将这些碎片组织成结构清晰、模型易于理解的上下文。
标准模板结构:
[Role & Policies]
明确 Agent 的角色定位与行为准则。
例如:你是一位专注于代码审查的工程助手,回答时需优先考虑安全性和可维护性。
[Task]
当前轮次需要完成的具体任务描述。
例如:审查以下 Python 函数,指出潜在的性能瓶颈。
[State]
Agent 当前所处的状态与背景信息。
例如:当前处于重构项目第二阶段,已完成数据层改造,正在处理业务逻辑层。
[Evidence]
从外部信息源(RAG、记忆库等)检索到的证据信息。
例如:相关文档片段、过去处理类似问题的记忆等。
[Context]
历史对话记录与相关记忆。
例如:最近 5 轮对话摘要,以及与当前任务相关的历史决策记录。
[Output]
期望的输出格式与要求。
例如:以 Markdown 列表形式输出,每条问题附上严重等级(P0/P1/P2)和修改建议。
结构化的核心优势:
- 可读性:清晰的分区让模型和人类都能快速定位信息,降低理解成本。
- 可调试性:当 Agent 输出出现问题时,开发者可以快速定位是哪个分区的信息导致了偏差,大幅降低排查成本。
- 可扩展性:需要接入新的信息源时,只需在模板中新增对应分区,不影响现有结构,符合”开闭原则”。
第四阶段:Compress(兜底压缩)
在某些极端情况下——例如任务背景极为复杂,或系统指令本身就占用了大量 token——即使经过 Select 的精挑细选,结构化后的上下文依然可能超出模型的 token 上限。此时,Compress 阶段作为兜底机制介入。
设计原则:保持结构完整性
Compress 的压缩不是"一刀切"地截断,而是以分区为单位进行压缩。即使 token 预算极度紧张,也要尽量保留每个分区的核心信息,确保模型对整体任务结构的感知不受损。
优化建议:
-
动态调整 token 预算:根据任务复杂度动态调整
max_tokens。简单的问答任务可使用较小预算;涉及多步骤推理或长文档分析的复杂任务,则适当增加预算上限。 - 升级相关性计算方式:在生产环境中,将基于关键词重叠的简单相似度计算替换为向量相似度(如使用 Embedding 模型),可以显著提升语义检索质量,使 Select 阶段就能更精准地筛选信息,从而减少对 Compress 的依赖。
- 缓存机制:对于内容稳定的系统指令和知识库文档,引入缓存机制(如 Anthropic 的 Prompt Caching),避免每次请求都重复计算 token 占用,同时降低推理成本。
- 监控与日志:记录每次上下文构建的关键统计信息(如:命中信息条数、各信息源的 token 占比、被过滤的信息数量、最终 token 使用率等),为后续参数调优提供数据支撑。
-
A/B 测试:对于关键超参数(如相关性权重 α、新近性权重 β、
min_relevance阈值),通过 A/B 测试在真实任务上寻找最优配置,而非仅凭经验拍定。
NoteTool:长时程任务的结构化外部记忆
对于持续数小时乃至数天的长时程任务——如大型代码重构、持续的研究调研、多阶段项目管理——Agent 需要一种能够跨对话轮次持久化、结构化地记录任务状态和关键结论的机制。这正是 NoteTool 的定位。
设计理念
NoteTool 相比传统的 MemoryTool,是一种更轻量、更人类友好的记录方式。它的核心特点包括:
- 结构化记录:采用 YAML 头部 + Markdown 正文的混合格式,既适合机器解析,也方便人类直接阅读和编辑。
- 版本友好:纯文本格式天然支持 Git 等版本控制工具,可以完整追踪笔记内容的演化历史。
- 低开销:无需依赖复杂的向量数据库或关系型数据库,轻量的文件系统操作即可支撑日常的状态追踪需求。
- 灵活分类:通过
type和tags字段,支持多维度的笔记组织与检索,满足不同类型任务信息的管理需求。
存储格式
1. 笔记文件格式
每条笔记是一个独立的 Markdown 文件,由 YAML 头部(元数据)和 Markdown 正文(内容)两部分组成:
---
id: note_20250119_153000_0
title: 项目进展 - 第一阶段
type: task_state
tags: [refactoring, phase1, backend]
created_at: 2025-01-19T15:30:00
updated_at: 2025-01-19T15:30:00
---
# 项目进展 - 第一阶段
## 完成情况
已完成数据模型层的重构,主要改动包括:
1. 统一了实体类的命名规范
2. 引入了类型提示,提升代码可维护性
3. 优化了数据库查询性能
## 测试覆盖
- 单元测试覆盖率:85%
- 集成测试覆盖率:70%
## 下一步计划
1. 重构业务逻辑层
2. 解决依赖冲突问题
3. 提升集成测试覆盖率至 85%
2. 索引文件
NoteTool 在本地维护一个 notes_index.json 文件,用于在无需逐一读取文件的情况下快速检索和管理笔记:
{
"note_20250119_153000_0": {
"id": "note_20250119_153000_0",
"title": "项目进展 - 第一阶段",
"type": "task_state",
"tags": ["refactoring", "phase1", "backend"],
"created_at": "2025-01-19T15:30:00",
"updated_at": "2025-01-19T15:30:00",
"file_path": "./notes/note_20250119_153000_0.md"
}
}
索引文件的主要作用包括:
- 快速检索:按 type、tags、时间范围等条件过滤,无需全量读取笔记内容。
- 元数据管理:集中管理所有笔记的结构化元数据。
- 完整性校验:对比索引与实际文件,检测是否存在孤立文件或索引缺失的情况。
核心操作
NoteTool 提供七类核心操作,覆盖笔记的完整生命周期:
| 操作 | 说明 |
|---|---|
create |
创建新笔记,生成唯一 ID 并更新索引 |
read |
按 ID 读取笔记的完整内容 |
update |
更新笔记正文或元数据(自动更新 updated_at) |
search |
按关键词、type、tags 等条件检索笔记 |
list |
列出所有笔记或按条件过滤后的笔记列表 |
summary |
对笔记内容进行摘要,生成高层次概览 |
delete |
删除笔记文件并从索引中移除 |
最佳实践
1. 合理的笔记分类
为笔记选择合适的 type 是高效检索的前提。推荐的笔记类型体系如下:
task_state:记录阶段性进展与当前状态,是最常用的类型。conclusion:记录经过验证的重要结论与发现,供后续任务引用。blocker:记录当前阻塞问题,优先级最高,应在上下文构建时优先纳入。action:记录明确的下一步行动计划,驱动任务向前推进。reference:记录重要的参考资料、文档链接或外部信息。
2. 定期清理与归档
笔记不应只增不减,定期维护同样重要:
- 已解决的
blocker,将其 type 更新为conclusion,并记录解决方案。 - 已过期的
action,及时删除或标记为completed。 - 利用
tags进行版本管理,如为某个里程碑版本的笔记统一打上v1.0标签,完成后加上archived。
3. 与 GSSC 的配合
NoteTool 需要与前述的上下文构建流水线紧密配合,才能发挥最大价值:
- 在每轮对话的 Gather 阶段,将相关笔记作为一类信息源纳入候选池。
- 在 Select 阶段,根据笔记类型赋予差异化的优先级分数:
blocker>action>conclusion>reference。 - 限制每轮纳入的笔记数量上限,防止历史笔记堆积导致上下文过载。
4. 人机协作
NoteTool 的纯文本格式天然支持人工干预:
- 笔记是标准 Markdown 格式,开发者或用户可以直接在文本编辑器中手动修改,无需通过 API。
- 使用 Git 对笔记目录进行版本控制,完整追踪 Agent 的”思维演化”过程,也便于在出现问题时回滚到历史状态。
- 在关键里程碑节点,安排人工审核 Agent 自动生成的笔记,确保其准确性与完整性。
5. 自动化工作流扩展
NoteTool 作为结构化的持久化存储,可以作为更多自动化工作流的基础:
- 定期触发
summary操作,生成项目进度的摘要报告。 - 基于笔记内容自动生成项目周报或 Release Notes。
- 将笔记内容同步推送至外部协作工具(如 Notion、Confluence),实现 Agent 工作过程的透明化。
TerminalTool:即时文件访问的安全桥梁
在处理涉及本地文件系统的任务时,Agent 面临一个效率困境:如果将所有相关文件预先加载进上下文,会严重消耗 token 预算;但如果什么都不加载,遇到需要文件内容时又无从下手。
TerminalTool 提供了一种优雅的解决方案——即时(Just-in-Time, JIT)上下文:Agent 不在任务开始时预加载所有文件,而是在需要时按需探索和检索,将文件访问的时机和粒度控制权交还给 Agent 自身。
为什么需要 TerminalTool?
在真实工程场景中,有大量任务需要实时、轻量级地访问文件系统,而又不适合预先建立完整索引:
- 代码审查与调试:Agent 需要按需查看特定文件的具体内容,而非批量加载整个代码库。
- 日志分析:实时查看最新的日志片段,而非预加载可能数 GB 的日志文件。
- 文档撰写:在撰写过程中随时查阅相关配置文件或接口文档的当前内容。
这类场景的共同特点是:访问模式难以预测、所需文件随任务动态变化,这使得预先索引和向量化的方案显得笨重且低效。
多层安全机制
允许 Agent 执行命令是一个强大但潜在危险的能力。TerminalTool 通过四层安全机制将风险控制在可接受范围内:
- 命令白名单:只允许执行预定义的安全命令集合(如文件查看、文本搜索、目录导航等),拒绝一切非白名单命令,从根本上杜绝执行高危操作的可能。
-
工作目录限制:将命令执行的范围严格限定在预设的工作目录内,防止 Agent 越权访问系统敏感目录(如
/etc、/root)。 - 超时控制:为每次命令执行设定最大运行时间,避免因意外的长时间运行命令(如误触发大规模文件搜索)导致 Agent 阻塞。
- 输出大小限制:对命令输出的大小进行截断,防止巨型文件内容直接撑满上下文窗口,确保 Agent 能够正常处理返回结果。
核心能力
命令执行(_execute_command)
TerminalTool 的底层能力,负责在安全机制的约束下执行白名单内的命令,并将标准输出(stdout)和标准错误(stderr)结构化地返回给 Agent。
目录导航(cd)
支持 Agent 在工作目录范围内自由切换当前路径,配合 ls、cat、grep 等常用命令,使 Agent 能够像人类工程师一样探索项目结构、按需查阅文件内容。
总结
上下文工程是 Agent 系统走向生产可用的必经之路。本文介绍的三个核心组件,共同构成了一套完整的上下文管理体系:
- GSSC 流水线解决了”如何在每一轮对话中动态构建高质量上下文”的问题,通过汇集、选择、结构化、压缩四个阶段,将海量候选信息蒸馏为精炼的模型输入。
- NoteTool 解决了”如何在跨轮次的长时程任务中持久化和管理关键信息”的问题,以轻量、人类友好的方式为 Agent 提供结构化的外部记忆。
- TerminalTool 解决了”如何在不预加载所有信息的前提下,安全地按需访问文件系统”的问题,以 JIT 上下文的理念最大化 token 预算的利用效率。
三者相辅相成:NoteTool 产生的结构化笔记,通过 GSSC 的 Gather 和 Select 阶段按需注入上下文;TerminalTool 提供的即时文件访问能力,则补充了静态知识库和记忆检索之外的动态信息维度。将这三者有机整合,才能构建出真正具备长期记忆、实时感知与高效推理能力的生产级 Agent 系统。
