- 人工智能
- AI Agent
- 代码智能体
- Agent 编排
- CLI
- AI 应用
【免费下载链接】gsd-2
A powerful meta-prompting, context engineering and spec-driven development system that enables agents to work for long periods of time autonomously without losing track of the big picture
本篇技术指南基于 gsd-2 仓库中 pi-context-optimization-opportunities.md 这一"研究专用(Research only)"文档展开。它梳理了 pi 引擎(
packages/pi-coding-agent与packages/pi-agent-core)在上下文工程层面的 10 个优化机会:Prompt 缓存、观测掩码、提前压缩阈值、工具结果截断、上下文文件去重、技能懒加载、Token 估算精度、Markdown 化改造、动态工具集以及分阶段成本归因。文中将文档的核心提案与当前仓库源码逐一对照,读者读完可以掌握这套上下文成本与质量优化清单的原理、落点位置与实施优先级。
文档定位:一份"未排期实施"的上下文工程研究清单
原文档开篇即标注了三个关键约束:
- Status: Research only — not planned for implementation.(仅研究,未计划实施)
- Scope: 影响
packages/pi-coding-agent与packages/pi-agent-core的基础设施。 - 受益面: 这些改动将惠及 pi 引擎的每一个消费方,而不仅仅是 GSD 本身。
这意味着本文讨论的所有优化点,应当被理解为审计清单与设计蓝图,而非已经落地的功能描述。文章后续章节会对照当前仓库快照,指出哪些机会点已具备部分基础设施(例如 Anthropic 协议路径上的cache_control),哪些仍是纯提案。
1. Prompt 缓存(cache_control)——最高杠杆率的机会
现状描述
文档指出:在理想状态下,每次 LLM 调用都会为系统提示词、工具定义和上下文文件重新支付完整的输入 Token 成本,因为 API 调用路径上没有设置任何cache_control断点。
机会分析
Anthropic 的 KV 缓存对缓存命中的 Token 提供 90% 的成本削减(0.1x 输入费率)。Claude Code 通过将稳定内容置于易变内容之前,达成了 92–98% 的缓存命中率。
文档给出了三个埋点位置(原指packages/pi-ai/src/providers/anthropic.ts协议路径):
- 在最后一个工具定义块上设置
cache_control: { type: "ephemeral" }; - 在静态系统提示词部分之后(基础样板 + 上下文文件)设置
cache_control; - 让每轮的用户消息保持不缓存(作为易变后缀)。
关键约束:断点必须位于"最后一块静态内容"之后
缓存断点必须放置在所有静态内容之后、任何动态内容(时间戳、按请求变化的变量)之前。把一个时间戳移到缓存断点之前,会让缓存每次调用都失效。
缓存层级关系为:tools → system → messages。任何工具定义的变更都会使 system 与 messages 的缓存失效,因此工具定义应当按字母序确定性排序,避免无谓的缓存抖动。
当前仓库对照
从当前仓库快照看,Anthropic 协议路径已经具备一套完整的缓存控制基础设施,集中在 anthropic-shared.ts:
- getCacheControl():解析缓存保留策略,默认
short,兼容PI_CACHE_RETENTION=long环境变量;直连api.anthropic.com且为 long 时附加ttl: "1h"; - convertTools():把
cache_control打到最后一个工具上,覆盖整个工具块; - convertMessages():在最后一条用户消息与最近的压缩边界消息上各应用一次断点,并刻意控制在 Anthropic 4 个断点的上限内(system + tools + boundary + last user = 4,参见 PR #5027 注释);
- buildParams():对 system prompt 块附加
cache_control,OAuth 模式下仅让最后一个 system 块携带断点以避免浪费断点槽位。
此外仓库还配套了专门的断点测试 anthropic-shared.cache-breakpoint.test.ts。因此可以说:文档提出的"埋点位置"在当前代码中已部分落地(工具末块、system、压缩边界、末条消息),文档的价值更多体现在审计这些断点是否被动态内容破坏、以及工具排序是否确定性足够强。
预期收益
多轮会话中(GSD auto-mode 的主要成本形态),输入 Token 成本可降低 80–90%。
2. 消息管线中的观测掩码(Observation Masking)
现状描述
文档指出:agent-loop.ts在每一轮都把完整的context.messages数组传给 LLM。50 轮之前的工具结果在之后的每一次调用中都被完整重读。AgentContext上的transformContext钩子虽然存在且在每次 LLM 调用前触发,但没有默认实现——是否做裁剪完全由扩展自行负责。
当前仓库对照
transformContext在当前仓库中确实存在且接入了扩展运行时。在 sdk.ts 中可以看到:
transformContext: async (messages) => { const runner = extensionRunnerRef.current; if (!runner) return messages; return runner.emitContext(messages); },即默认行为是"原样返回消息",只有当扩展注册了emitContext处理器时才会被改写——与文档描述完全一致:没有默认实现,扩展全权负责裁剪。
机会与数据
JetBrains Research 在 SWE-bench Verified(500 个任务,最长 250 轮轨迹)上的测试表明:
- 相比未管理的历史,成本降低 50% 以上;
- 性能与 LLM 摘要持平或略有超出;
- 零额外开销(不需要额外的 LLM 调用)。
提议的默认实现
文档给出了一份可在pi-agent-core落地的默认transformContext实现:
// Keep last KEEP_RECENT_TURNS verbatim; mask older tool results const KEEP_RECENT_TURNS = 8; function defaultObservationMask(messages: AgentMessage[]): AgentMessage[] { const cutoff = findTurnBoundary(messages, KEEP_RECENT_TURNS); return messages.map((m, i) => { if (i >= cutoff) return m; if (m.type === "toolResult" || m.type === "bashExecution") { return { ...m, content: "[result masked — within summarized history]", excludeFromContext: false }; } return m; }); }实现要点:
- 保留最近
KEEP_RECENT_TURNS(8 轮)的完整内容,只掩码更早的toolResult/bashExecution; - 用轻量占位符替换旧工具结果正文;
- 掩码发生在 LLM 调用之前,不改写消息存储本身。
仓库中与excludeFromContext相关的机制可作参照:bashExecution消息已经支持excludeFromContext标记(!!前缀),在 agent-session.ts 与 convertToLlm() 中都会被过滤,说明消息级"对 LLM 隐身"的通道早已存在,观测掩码可以复用这套字段与语义。
与压缩的互补关系
观测掩码降低了 Token 的累积速率,从而推迟压缩阈值被触达。二者是互补的:掩码负责稳态,压缩负责罕见的超深会话。
3. 更早的压缩阈值(Earlier Compaction Threshold)
现状:基于固定保留 Token 的触发逻辑
当前常量定义在 constants.ts:
export const COMPACTION_RESERVE_TOKENS = 16_384; export const COMPACTION_KEEP_RECENT_TOKENS = 20_000; export const TOOL_RESULT_MAX_CHARS = 2_000;按文档计算:对于 200K 上下文窗口,压缩在约 183K Token 处触发——91.5% 的利用率。保留空间COMPACTION_RESERVE_TOKENS = 16_384只是为"本次提示 + 本次响应"预留的余量。
问题:上下文漂移比耗尽更致命
文档引用的两个数据点:
- 上下文漂移(Context drift,而非原始耗尽)导致约 65% 的企业 Agent 失败;
- 根据 Zylos 的生产数据,超过约 30K Token 后性能开始可测地退化。
当前阈值意味着会话在压缩触发之前,会先在一个已经劣化的状态下运行很长一段距离。
提议:把触发点降到 70% 利用率
// Proposed COMPACTION_THRESHOLD_PERCENT = 0.70 // fire at 70% of contextWindow COMPACTION_RESERVE_TOKENS = contextWindow * (1 - COMPACTION_THRESHOLD_PERCENT)对 200K 窗口:约在 140K Token 处压缩,比现状提前 43K Token。
权衡
- 压缩更频繁,但每次发生时上下文里"新鲜内容"更多;
- 摘要质量提升,因为每次切割需要丢弃的材料更少;
- 仓库测试 compaction-threshold.test.ts 表明阈值是高度可参数化、可单测的行为,改动风险可控。
4. 工具结果在写入时截断(Tool Result Truncation at Write Time)
现状问题
TOOL_RESULT_MAX_CHARS = 2_000(constants.ts)只在压缩摘要期间生效,而不是在工具结果进入消息存储时生效。一个返回 50KB 日志输出的 bash 结果会被原样存储,并在压缩触发之前逐字逐句地反复重发。
从源码看,消息渲染层已经存在"输出被截断 + 指向完整输出文件"的模式:在 messages.ts 中可以看到[Output truncated. Full output: ${msg.fullOutputPath}]。也就是说,截断体验的基础设施(fullOutputPath 回链)已有雏形,缺的是在写入消息存储的那一刻就执行截断。
两种截断策略
| 策略 | 做法 | 适用 |
|---|---|---|
| 硬截断(Hard truncation) | 按 N 字符切片,追加"\n[truncated — {original_length} chars]" | 简单、零开销 |
| 语义头尾(Semantic head/tail) | 保留前 500 字符(上下文、命令回显)+ 最后 1000 字符(最终输出、错误) | 对 bash 结果更友好,因为错误通常在结尾 |
推荐方案
以语义头尾为默认策略,并按工具类型可配置:文件读取类结果受益于"头";bash/测试输出受益于"头 + 尾"。落点建议在 messages.ts 的convertToLlm()或工具结果处理器中。
5. 上下文文件去重与裁剪
现状:按路径去重,不按内容去重
loadProjectContextFiles()的实现位于 resource-loader.ts,核心行为:
- 搜索顺序为:
~/.gsd/agent/(agent 目录)→ 逐级向上遍历祖先目录 → cwd; - 候选文件名是
AGENTS.md与CLAUDE.md(loadContextFileFromDir); - 去重基于
seenPaths(文件路径 Set),不比较内容; - 整个文件内容被逐字拼接到系统提示词中,不做裁剪、不做摘要。
反模式示例
文档给出的反模式:如果项目在三个祖先层级(仓库根、工作区、家目录)各有AGENTS.md,三层全部注入;若它们共享通用样板内容,该内容就被重复注入多次。
三个优化方向
- 内容级去重:对段落级块做哈希,跳过任何在前面文件里已见过的块;
- 按章节感知加载:解析
AGENTS.md中的##标题,只包含与当前任务类型相关的章节(例如只在运行测试时注入## Testing章节); - Token 预算强制:若全部上下文文件超过 N Token,则对最旧/最远的文件做摘要而不是逐字注入。
仓库已在其他资源(prompts、themes)上实现了按名称去重的dedupeResources(resource-loader.ts),说明"去重"是工程团队认可的模式,只是尚未下沉到AGENTS.md的内容层面。
6. 技能(Skill)内容的懒加载与摘要
现状
当/skill:name被调用时,完整技能文件内容会被以内联<skill>...</skill>形式注入到用户消息中(参考 skills.ts 的formatSkillsForPrompt,技能清单确实以<available_skills>+<skill>XML 包裹)。没有分块、没有摘要。一个 10KB 的技能文件,在那一轮就增加约 2,500 Token。
三个机会
- 技能注入缓存:如果同一个技能在多个轮次中使用(少见但可能),每次都重新注入。可以在首次注入后用
cache_control缓存; - 技能摘要模式:首次引用时只注入 200 Token 的摘要;仅当模型通过
get_skill_detail工具调用请求时才注入完整内容。对最终未被遵循的技能可显著降本; - 技能预取:在已知的长会话开始前(例如 auto-mode 启动时),预先注入所有可能用到的技能并打上
cache_control,使整个会话期间技能内容都命中缓存。
7. Token 估算精度
现状:chars / 4启发式
当前估算逻辑位于压缩管线(compaction.ts,以及 compaction/utils.ts 等),核心公式为Math.ceil(chars / 4)。
文档指出该启发式的两个系统性偏差:
- 对英文散文高估(实际约 3.5 字符/Token);
- 对短标识符代码或 Unicode 内容低估。
机会:引入真正的分词器
@anthropic-ai/tokenizer(tiktoken 兼容,随 SDK 分发):准确,但单次调用约 5ms;- 分层策略:展示用
chars/4,只有在需要做压缩阈值决策的地方(准确率关键)才使用真正的分词器。
收益
更精确的压缩触发时机、更少的无谓压缩、COMPACTION_KEEP_RECENT_TOKENS边界放置更准确。
8. 格式:内部上下文用 Markdown 替代 XML
现状
消息管线在多处使用<skill>、<summary>、<compaction>等 XML 包裹(前述 skills.ts 即为实例),而系统提示词各章节大都是散文式 Markdown。
调研结论
- XML 成对的开闭标签让同等语义内容多消耗15–40%的 Token;
- 但 Claude 针对 XML 做过优化,在需要精确段落解析的任务上准确率更高。
建议的转换原则
转换到 Markdown 的场景:
- 内容非嵌套(扁平指令、状态消息);
- 面向人类可读而非被模型机器解析;
- 不需要精确的边界检测。
保留 XML 的场景:
- 边界模糊的 few-shot 示例;
- 技能内容(需要与周围文本精确隔离);
- 压缩摘要(模型必须将其视为权威历史)。
预估收益
系统提示词 Token 数降低5–15%。
9. 动态工具集交付(Dynamic Tool Set Delivery)
现状
所有工具定义都出现在每一次 LLM 请求中。在静态配置下,工具描述消耗输入 Token 的 60–80%;随着新扩展注册工具,基线线性增长。这解释了为何工具定义排序(见第 1 节)如此关键——任何工具变更都会波及 system 与 messages 缓存。
机会:三函数动态工具集模式
search_tools(query)— 对工具目录做语义搜索;describe_tools(ids[])— 按需拉取完整 schema;execute_tool(id, params)— 执行保持不变。
文档引用 Speakeasy 的测量:Token 削减 91–97%,任务成功率 100%。代价是工具调用次数增加 2–3 倍、墙钟时间延长约 50%,但净成本显著下降。
对 pi 的可行性评估
文档认为工程主体在于语义搜索索引与describe_tools/search_tools两个工具的实现——前提是工具注册表已经把工具元数据与定义分离存储。需要注意:文档所引用的packages/pi-coding-agent/src/core/tool-registry.ts路径在当前仓库快照中未被确认到,建议以仓库实际结构为准核对(工具/命令的注册与冲突检测可见 resource-loader.ts 的detectExtensionConflicts实现)。
10. 成本归因与分阶段报告(Cost Attribution)
现状
SessionManager.getUsageTotals()(session-manager.ts)在整个会话层面累计成本,不保存任何分阶段或分 Agent 的细分。成本可见性仅限于 footer 总数与GSD_SHOW_TOKEN_COST=1的逐轮展示。
机会:结构化的成本检查点事件
interface CostCheckpointEvent { type: "cost_checkpoint"; label: string; // "discuss-phase", "execute-slice-3" deltaTokens: Usage; // tokens since last checkpoint cumulativeTokens: Usage; cumulativeCost: number; }消费场景
GSD 扩展可以订阅这些事件,在/gsd stats中呈现每个里程碑的成本,并标记成本异常偏高的里程碑——从而实现预算感知的规划(budget-aware planning)。
实施优先级总览
文档末尾给出了如追求实施时的完整排序表:
| 优先级 | 项目 | 工作量 | 预期影响 |
|---|---|---|---|
| 1 | Prompt 缓存(cache_control) | 低 | 输入成本降低 80–90% |
| 2 | 提前压缩阈值(70%) | 极低 | 降低长会话中的漂移 |
| 3 | 工具结果写入时截断 | 低 | 压缩间隙之间的上下文膨胀更小 |
| 4 | 上下文文件去重 | 中 | 不固定——多级 AGENTS.md 场景收益高 |
| 5 | 观测掩码(默认transformContext) | 中 | 长期运行的 Agent 成本降低 50%+ |
| 6 | Token 估算(真正分词器) | 低 | 精度提升,成本影响较小 |
| 7 | Markdown 替代 XML 审计 | 低 | 系统提示词降低 5–15% |
| 8 | 技能cache_control缓存 | 低 | 技能密集型会话收益明显 |
| 9 | 动态工具集交付 | 高 | 大型工具目录降低 90%+;重大架构变更 |
| 10 | 分阶段成本归因事件 | 中 | 仅提升可见性;为未来预算路由铺路 |
不难看出一个清晰的模式:前三个项目都是低工作量、高收益的"立即可做"项,而动态工具集与成本归因属于中期架构级投入。结合第 1 节我们已验证的现状,cache_control基础设施在 Anthropic 协议路径上已经就位,接下来真正值得投入的是把断点纪律(静态内容在前、工具确定性排序)固化成可测试的约束,并推动 70% 压缩阈值与写入时截断这两个低成本的稳态优化。
延伸阅读
- 完整原始研究文档:pi-context-optimization-opportunities.md
- 压缩与保留相关常量:constants.ts
- Anthropic 协议缓存控制实现与测试:anthropic-shared.ts / anthropic-shared.cache-breakpoint.test.ts
- 消息存储与 LLM 转换:messages.ts
- 上下文文件加载与去重:resource-loader.ts
- 压缩触发与 Token 估算:compaction.ts
transformContext扩展钩子接线:sdk.ts- 会话成本累计:session-manager.ts
说明:本文所引文档标注为 Research only,所有"预期收益/降低百分比"均为文档引用或基于其调研来源的表述;当前仓库的实际行为以源码与测试为准,实施前建议按仓库最新代码重新核算。
- 人工智能
- AI Agent
- 代码智能体
- Agent 编排
- CLI
- AI 应用
【免费下载链接】gsd-2
A powerful meta-prompting, context engineering and spec-driven development system that enables agents to work for long periods of time autonomously without losing track of the big picture
相关推荐
aider 提示词缓存(Prompt Caching)完全指南:从成本优化到缓存保温的实现原理
aider 提示词缓存(Prompt Caching)完全指南:从成本优化到缓存保温的实现原理 aider 是运行在终端里的 AI 结对编程工具,它会将系统提示
人工智能大模型AI Agent代码智能体交互助手CLI开发工具MiroThinker缓存机制:减少重复工具调用提升效率的技巧
MiroThinker缓存机制:减少重复工具调用提升效率的技巧 MiroThinker是一款为深度研究和复杂工具使用场景训练的开源智能体模型,其高效的缓存机制能
人工智能大模型AI Agent深度研究Agent 框架MCP 服务工具调用模型评测Plandex成本优化方案:上下文缓存降低API调用
Plandex成本优化方案:上下文缓存降低API调用 痛点:AI开发工具的高昂API成本 在AI辅助开发日益普及的今天,开发者们面临着一个共同的挑战:API调用
人工智能AI Agent代码智能体CLI开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考