这是「GSD 全景代码解析」专题的第 41 篇。在本系列中,我们将逐层拆解 Get Shit Done (GSD) 这一 56K+ stars 的 Meta-Prompting 框架,从命令系统到工作流编排,从 Agent 设计到上下文工程,带你一览上下文驱动开发的工程全貌。
一、Context Engine 的定位与职责
在 GSD 的 SDK 架构中,Context Engine 是连接「静态参考文档」与「动态运行时状态」的核心枢纽。它负责将分散在不同目录(dreams/、plans/、sessions/)中的结构化信息,组装成一份完整、有序、符合预算约束的上下文报文,最终提交给 AI 模型。
如果说 GSD 的参考体系(第 32-37 篇)解决的是**"写什么"的问题,那么 Context Engine 解决的就是"怎么装"和"装不下怎么办"**的问题。
GSD 的上下文引擎由两个紧密协作的模块组成:
| 模块 | 文件 | 大小 | 核心职责 |
|---|---|---|---|
| Context Engine | context-engine.ts | ~6KB | 上下文组装、模板渲染、状态注入 |
| Truncation Engine | context-truncation.ts | ~6KB | 预算监控、截断决策、质量保障 |
这两个模块遵循**"先组装后截断"**的两阶段流水线设计:Context Engine 尽可能完整地收集所有相关信息,Truncation Engine 则在预算超限时有策略地精简内容,而非简单地丢弃。
二、context-engine.ts:上下文组装流程
2.1 整体组装流水线
Context Engine 的组装流程可以概括为五个阶段:解析 → 加载 → 注入 → 渲染 → 校验。
flowchart TD
A[输入: config.json + 命令参数] --> B[阶段1: 解析配置]
B --> C[阶段2: 加载参考文档]
C --> D[阶段3: 注入运行时状态]
D --> E[阶段4: 模板渲染]
E --> F[阶段5: 预算校验]
F -->|未超限| G[输出: 完整上下文]
F -->|超限| H[触发截断流程]
H --> I[调用 context-truncation.ts]
I --> J[输出: 截断后上下文]2.2 阶段详解
阶段 1:解析配置(Configuration Resolution)
Context Engine 首先读取 config.json 中的上下文配置,确定本次组装需要包含哪些组件:
interface ContextConfig {
// 参考文档配置
references: {
core: string[]; // 核心参考文档(如 gsd-rules.md)
project: string[]; // 项目级参考文档
session: string[]; // 会话级参考文档
};
// 状态配置
state: {
includePlan: boolean; // 是否注入当前 plan
includeSession: boolean; // 是否注入会话历史
includeGit: boolean; // 是否注入 Git 状态
};
// 模板配置
template: string; // 使用的上下文模板(dev/research/review)
// 预算配置
budget: {
maxTokens: number; // 最大 token 预算
reserveTokens: number;// 为模型响应预留的 token
};
}配置解析遵循层级覆盖原则:项目默认值 → .planning/config.json 覆盖 → 命令行参数最终覆盖。这种设计允许用户在全局规范的基础上,针对特定项目或特定命令灵活调整上下文构成。
阶段 2:加载参考文档(Reference Loading)
配置解析完成后,Context Engine 进入文档加载阶段。这一步的核心是**@reference 解析引擎**。
flowchart LR
A[解析 @reference 指令] --> B{文档类型}
B -->|本地文件| C[fs.readFile 读取]
B -->|URL| D[HTTP fetch 获取]
B -->|内置引用| E[从 SDK bundle 读取]
C --> F[缓存到内存]
D --> F
E --> F
F --> G[解析 frontmatter]
G --> H[提取元数据]
H --> I[按优先级排序]加载策略的关键细节:
路径解析:
@reference支持相对路径(相对于.planning/目录)、绝对路径和远程 URL。Context Engine 会依次尝试解析,失败时抛出带有修复建议的错误。缓存机制:同一会话内的重复引用会被缓存,避免磁盘 I/O 重复开销。缓存以引用路径的规范化字符串为键。
frontmatter 提取:每个参考文档的 YAML frontmatter 会被解析为结构化元数据,用于后续的优先级计算和依赖追踪。
加载顺序:文档按照
priority字段(数值越大越优先)排序,同优先级按照配置中出现的顺序排列。这确保了核心规则文档始终位于上下文的头部——模型对上下文开头的注意力通常更强。
阶段 3:注入运行时状态(State Injection)
参考文档是静态的,而 Agent 的执行需要动态信息。Context Engine 在此阶段将运行时的活数据注入到上下文结构中。
注入的状态类型包括:
| 状态类型 | 数据来源 | 注入位置 | 用途 |
|---|---|---|---|
| Plan 状态 | plans/current/plan.md | 参考文档之后 | 让模型了解当前目标和已完成的任务 |
| Session 历史 | sessions/current/session.log | Plan 之后 | 提供多轮对话的连续性 |
| Git 状态 | git status / git diff | Session 之后 | 反映代码仓库的当前变更 |
| 文件树 | find . -type f 过滤后 | 可选位置 | 帮助模型理解项目结构 |
| 环境信息 | Node.js 版本、OS 等 | 尾部 | 调试和兼容性参考 |
状态注入的实现逻辑(伪代码):
function injectState(context: ContextBuilder, config: ContextConfig): void {
if (config.state.includePlan) {
const plan = loadPlanState();
context.addSection('## Current Plan', plan.content, {
priority: 80,
source: plan.path,
});
}
if (config.state.includeSession) {
const session = loadSessionHistory(config.maxHistoryRounds);
context.addSection('## Session History', session.content, {
priority: 70,
source: session.path,
});
}
if (config.state.includeGit) {
const gitStatus = getGitStatus();
if (gitStatus.hasChanges) {
context.addSection('## Working Directory Changes', gitStatus.diff, {
priority: 60,
source: 'git',
});
}
}
}设计要点:
- Plan 状态采用增量注入:只注入
plan.md中标记为in_progress和pending的任务,已完成任务只保留摘要,避免历史任务膨胀上下文。 - Session 历史支持轮数限制:通过
maxHistoryRounds控制注入的对话轮数,默认保留最近 5 轮。更早的历史会被压缩为"执行摘要"。 - Git diff 有大小上限:如果 diff 超过 200 行,会触发 diff 摘要模式,只列出变更文件列表和每个文件的变更统计(
+n/-m),而非完整 diff 内容。
阶段 4:模板渲染(Template Rendering)
所有内容加载完毕后,Context Engine 进入模板渲染阶段。GSD 使用轻量级的字符串模板引擎,而非完整的模板语言(如 EJS 或 Handlebars),以控制复杂度和避免注入风险。
模板语法:
# GSD Context
## System Reference
{{systemReference}}
## Project Reference
{{projectReference}}
## Current Plan
{{planState}}
## Session History
{{sessionHistory}}
## User Request
{{userRequest}}
## Output Instructions
{{outputInstructions}}渲染规则:
- 占位符替换:
{{key}}被替换为对应 Section 的渲染结果。 - 条件渲染:
{{?key}}...{{/key}}仅在 key 存在且非空时渲染内容块。 - 列表渲染:
{{#items}}...{{/items}}用于渲染数组型数据。 - 原始输出:
{{{key}}}表示不进行 HTML 转义(GSD 上下文中不常用,保留给特殊场景)。
模板与模式的结合:
GSD 的上下文模板与之前讨论过的模式模板(dev.md、research.md、review.md)是正交关系。上下文模板控制**"信息如何组织",模式模板控制"模型如何响应"**。最终提交给模型的上下文结构如下:
[模式模板头部: 输出风格定义]
↓
[上下文模板: 结构化信息]
↓
[模式模板尾部: 输出格式约束]这种分离设计允许用户独立切换"信息结构"和"响应风格",例如使用 research 模式配合精简的上下文,或 dev 模式配合详尽的上下文。
阶段 5:预算校验(Budget Validation)
渲染完成后,Context Engine 计算最终上下文的 token 数量:
function validateBudget(context: string, config: ContextConfig): BudgetReport {
const totalTokens = estimateTokenCount(context);
const availableTokens = config.budget.maxTokens - config.budget.reserveTokens;
return {
totalTokens,
availableTokens,
isOverBudget: totalTokens > availableTokens,
overBudgetAmount: Math.max(0, totalTokens - availableTokens),
utilizationRate: totalTokens / config.budget.maxTokens,
};
}预算报告数据结构:
| 字段 | 类型 | 说明 |
|---|---|---|
totalTokens | number | 当前上下文的估算 token 数 |
availableTokens | number | 可用预算(总预算减去预留) |
isOverBudget | boolean | 是否超出预算 |
overBudgetAmount | number | 超预算的 token 数量 |
utilizationRate | number | 预算使用率(0-1) |
如果预算未超限,Context Engine 直接返回组装好的上下文。如果超限,则将上下文对象连同预算报告一并传递给 context-truncation.ts,进入截断流程。
三、context-truncation.ts:智能截断策略
3.1 截断触发条件
Truncation Engine 在以下三种情况下被激活:
- 硬预算超限:组装后的上下文 token 数超过
maxTokens - reserveTokens,必须截断才能提交。 - 软预算警告:使用率超过 85%(可配置),触发预防性截断,为后续对话增长预留空间。
- 渐进式披露请求:上层调用明确要求"精简上下文",通常发生在多轮对话后历史累积过多时。
flowchart TD
A[预算报告] --> B{isOverBudget?}
B -->|是| C[强制截断]
B -->|否| D{utilizationRate > 0.85?}
D -->|是| E[预防性截断]
D -->|否| F{explicitCompact?}
F -->|是| G[请求性截断]
F -->|否| H[无需截断]3.2 截断策略:三种模式
GSD 实现了三种截断策略,分别适用于不同的上下文构成场景。
策略一:尾部截断(Tail Truncation)
适用场景:历史对话、日志输出等时间敏感型内容。
原理:保留最早的内容(通常是系统指令和核心参考),从尾部开始截断。因为对话历史中,越新的消息越可能包含当前话题的关键转折,但完全丢弃尾部又会导致信息丢失,所以 GSD 的尾部截断会保留每轮对话的第一条和最后一条消息,中间的消息用摘要替代。
function truncateTail(sections: ContextSection[], targetTokens: number): ContextSection[] {
const result: ContextSection[] = [];
let currentTokens = 0;
for (const section of sections) {
if (section.type === 'session_history') {
// 对会话历史特殊处理:保留首尾,摘要中间
const compacted = compactSessionHistory(section.content);
const compactTokens = estimateTokenCount(compacted);
if (currentTokens + compactTokens <= targetTokens) {
result.push({ ...section, content: compacted });
currentTokens += compactTokens;
} else {
break;
}
} else {
const sectionTokens = estimateTokenCount(section.content);
if (currentTokens + sectionTokens <= targetTokens) {
result.push(section);
currentTokens += sectionTokens;
} else {
break;
}
}
}
return result;
}策略二:头部截断(Head Truncation)
适用场景:过时的项目背景、早期的 plan 版本等前置信息。
原理:从上下文头部开始截断,保留最新的状态信息。这种策略较少单独使用,因为头部通常是系统指令和核心规则,截断风险较高。GSD 仅在头部内容为"历史版本信息"且明确标记为 deprecated 时才会应用此策略。
策略三:中间截断(Middle Truncation)
适用场景:混合类型内容,需要保留头部规则和尾部请求。
原理:保留上下文的头部(系统指令、核心参考)和尾部(用户当前请求、输出指令),截断中间部分。中间部分通常是状态信息和历史记录,可通过摘要替代。
flowchart LR
A[头部: 系统规则
高优先级
保留] --> B[中间: 状态/历史
中优先级
摘要或截断] --> C[尾部: 用户请求
高优先级
保留]中间截断的实现细节:
function truncateMiddle(sections: ContextSection[], targetTokens: number): ContextSection[] {
// 按优先级分组
const highPriority = sections.filter(s => s.priority >= 90); // 系统规则、用户请求
const mediumPriority = sections.filter(s => s.priority >= 50 && s.priority < 90); // 状态信息
const lowPriority = sections.filter(s => s.priority < 50); // 环境信息、调试数据
// 第一步:保留所有高优先级
let currentTokens = highPriority.reduce((sum, s) => sum + estimateTokenCount(s.content), 0);
const result = [...highPriority];
// 第二步:尝试按优先级顺序纳入中优先级
for (const section of mediumPriority) {
const compacted = generateSummary(section);
const compactTokens = estimateTokenCount(compacted);
if (currentTokens + compactTokens <= targetTokens) {
result.splice(result.length - 1, 0, { ...section, content: compacted });
currentTokens += compactTokens;
}
}
// 第三步:低优先级直接丢弃(已在预算外)
return result;
}3.3 优先级保留机制
截断的核心不是"删除内容",而是"按优先级保留内容"。GSD 的优先级体系分为三个层级:
第一层级:不可截断(Immutable)
- 系统指令(
## System Reference) - 核心规则文档(
gsd-rules.md) - 当前用户请求(
## User Request) - 输出格式约束(
## Output Instructions)
这些内容如果单独就超过预算,Truncation Engine 会向上层抛出 BudgetExceededError,建议用户切换至更大上下文窗口的模型,而非强行截断。
第二层级:可摘要(Summarizable)
- Plan 状态(保留 in_progress,摘要 completed)
- Session 历史(保留最近 2 轮,摘要更早的)
- Git diff(保留变更文件列表,摘要具体 diff)
- 项目参考文档(保留标题和核心规则,展开详细说明)
这些内容在截断时会触发自动摘要机制。GSD 内置了基于关键句提取的轻量级摘要器,不依赖外部模型调用,确保截断流程的确定性和低延迟。
第三层级:可丢弃(Discardable)
- 环境信息(Node.js 版本、OS 信息)
- 调试输出(Agent 内部日志)
- 过时的参考文档版本(标记为
deprecated)
这些内容在预算紧张时会被直接移除,不产生摘要。
3.4 截断后的质量保障
截断不是简单的"剪掉多余部分",GSD 在截断后执行三项质量检查:
1. 结构完整性检查
function validateStructure(sections: ContextSection[]): ValidationResult {
const requiredSections = ['System Reference', 'User Request', 'Output Instructions'];
const missing = requiredSections.filter(
name => !sections.some(s => s.title.includes(name))
);
return {
isValid: missing.length === 0,
missingSections: missing,
};
}确保截断后上下文仍包含所有必需的结构组件。
2. 引用一致性检查
检查上下文中是否存在对其他 Section 的交叉引用(如 "详见上面的 Plan 状态"),如果被引用的 Section 已被截断,则修复引用文本或移除该引用。
function fixCrossReferences(sections: ContextSection[]): ContextSection[] {
const existingTitles = new Set(sections.map(s => s.title));
return sections.map(section => {
let content = section.content;
// 查找 "详见 [Section Name]" 或 "如上所述" 等引用模式
content = content.replace(/详见\s+`?([^`]+)`?/g, (match, refTitle) => {
return existingTitles.has(refTitle) ? match : '';
});
return { ...section, content };
});
}3. Token 估算校准
截断后的上下文会重新进行 token 估算,确保实际提交时不会再次超限。GSD 使用字符数除以 4 的快速估算法作为第一近似,对接近预算阈值的上下文使用更精确的 tiktoken 计数作为二次校验。
四、上下文预算管理
4.1 预算分配模型
GSD 采用分层预算池模型,将总预算按用途分配:
pie title 上下文预算分配(以 128K 模型为例)
"系统指令 + 核心规则" : 20480
"项目参考文档" : 15360
"Plan 状态" : 10240
"Session 历史" : 15360
"Git 状态" : 5120
"用户请求" : 8192
"预留响应空间" : 25600
"缓冲池" : 29648各预算池的默认值(基于 128K 上下文窗口):
| 预算池 | 默认比例 | 说明 |
|---|---|---|
| 系统指令池 | 20% | 核心规则和模式模板 |
| 参考文档池 | 15% | 项目级和会话级参考 |
| 状态池 | 20% | Plan + Session + Git |
| 请求池 | 8% | 用户当前输入 |
| 响应预留 | 20% | 为模型输出保留的空间 |
| 缓冲池 | 17% | 跨池调配的弹性空间 |
弹性调配机制:
当某一池未用尽时,剩余额度会自动流入缓冲池。当另一池超限时,优先从缓冲池借用。如果缓冲池也耗尽,才触发截断。这种设计避免了"某类信息完全无法加载"的刚性限制。
4.2 运行时预算监控
在多轮对话场景中,预算不是静态的——每轮对话都会消耗预算。GSD 通过预算监控器追踪实时使用情况:
interface BudgetMonitor {
// 初始预算
initialBudget: number;
// 当前已用(累计)
cumulativeUsed: number;
// 当前轮次已用
currentRoundUsed: number;
// 剩余可用
remaining: number;
// 趋势预测(基于最近3轮的增长率)
projectedRounds: number;
}当监控器检测到"按当前增长速度,预计 2 轮内将耗尽预算"时,会提前触发预防性压缩:主动对 Session 历史进行滚动摘要,释放预算空间,避免在关键轮次被迫硬截断。
五、与渐进式披露的衔接
GSD 的上下文管理不止于"装得下",更追求"装得巧"。**渐进式披露(Progressive Disclosure)**是 GSD 上下文工程的高级策略,它与 Context Engine 和 Truncation Engine 紧密配合。
5.1 渐进式披露的三层模型
flowchart TD
subgraph "第一层:核心上下文"
A1[gsd-rules.md]
A2[plan.md 摘要]
A3[用户请求]
end
subgraph "第二层:按需加载"
B1[详细 Plan 任务]
B2[相关参考文档]
B3[代码片段]
end
subgraph "第三层:动态触发"
C1[完整 Git diff]
C2[历史会话详情]
C3[外部 API 文档]
end
A1 --> B1
A2 --> B2
A3 --> C1第一层(始终加载):最核心的信息,每轮对话都必须包含。这部分内容被标记为 immutable,不受截断影响。
第二层(按需加载):根据当前任务的类型和阶段动态决定。例如,当 Agent 进入"实现模式"时,自动加载代码规范参考;进入"审查模式"时,自动加载审查清单。
第三层(动态触发):只有在特定条件满足时才加载。例如:
- 当 Plan 中的任务涉及"数据库操作"时,自动加载
database-schema.md - 当 Git diff 包含测试文件变更时,自动加载
testing-guidelines.md - 当用户请求中包含"@doc [URL]"时,实时抓取外部文档
5.2 截断与渐进式披露的协同
截断和渐进式披露不是对立关系,而是互补策略:
- 渐进式披露解决"该不该装":从源头减少不必要的内容进入上下文。
- 截断策略解决"装不下怎么办":在内容已经进入上下文后,智能地精简和压缩。
实际协同流程:
sequenceDiagram
participant U as 用户
participant CE as Context Engine
participant PD as Progressive Disclosure
participant TE as Truncation Engine
participant M as AI 模型
U->>CE: 发起请求
CE->>PD: 请求上下文组件清单
PD->>PD: 分析任务类型 + Plan 状态
PD->>CE: 返回推荐组件列表(已过滤)
CE->>CE: 组装推荐组件
CE->>TE: 预算校验
alt 预算充足
TE->>CE: 通过
CE->>M: 提交完整上下文
else 预算超限
TE->>TE: 按优先级截断/摘要
TE->>CE: 返回截断后上下文
CE->>M: 提交精简上下文
end
M->>U: 返回响应5.3 从粗粒度到细粒度的演进
在 GSD 的实际使用中,上下文管理经历三个阶段的演进:
V1:全量加载:早期将所有参考文档全部加载,依赖大上下文窗口(200K+)硬撑。简单但低效,模型注意力分散。
V2:智能截断:引入 Truncation Engine,在窗口有限时自动精简。解决了"装不下"的问题,但截断本身有信息损失。
V3:渐进式披露:结合 Progressive Disclosure 和 Truncation Engine,从"先装后砍"演进为"按需加载 + 动态精简"。这是 GSD 当前采用的策略。
演进的核心洞察:上下文管理的终极目标不是"塞进尽可能多的信息",而是"在有限的窗口内呈现最高价值的信息"。渐进式披露让"价值筛选"发生在加载之前,截断策略让"价值保留"发生在加载之后,两者结合实现了上下文质量的最大化。
六、小结
本文深入解析了 GSD SDK 中两个关键的上下文管理模块:
context-engine.ts 负责上下文的组装与渲染:
- 通过五阶段流水线(解析 → 加载 → 注入 → 渲染 → 校验)构建完整上下文
- 支持层级配置覆盖和灵活的参考文档加载
- 将 Plan、Session、Git 等运行时状态有机注入静态参考体系
- 采用轻量级模板引擎实现内容与表现分离
context-truncation.ts 负责上下文的截断与保障:
- 支持尾部、头部、中间三种截断策略,适配不同内容类型
- 通过不可截断 / 可摘要 / 可丢弃的三级优先级体系保护关键信息
- 截断后执行结构完整性、引用一致性和预算校准三重质量检查
上下文预算管理采用分层预算池 + 弹性调配 + 运行时监控的组合方案,在保证功能完整性的同时最大化预算利用率。
与渐进式披露的衔接将上下文管理从"先装后砍"的被动模式,升级为"按需加载 + 动态精简"的主动模式,这是 GSD 在 Context Engineering 领域的重要工程创新。
下一篇预告: 第 42 篇《GSD Tools 与状态管理》——深入解析 GSD 的 Tool 系统架构,包括工具注册、权限控制、状态持久化机制,以及 Tools 如何与 Context Engine 协同完成复杂任务编排。