10. Prompt、上下文与压缩
Agent 产品最容易出现“功能越加越多,system prompt 越来越长”。Craft 的主要应对不是单纯缩短文案,而是把上下文按稳定性、时效性和一次性消费语义拆开,再针对 Claude/Pi 的缓存与 resume 行为放到不同位置。
10.1 上下文来源
Section titled “10.1 上下文来源”一次 turn 可能包含:
基础 Craft system prompt项目/仓库 context 文件清单workspace capabilitiesworking directoryproject config / asset manifest / MEMORY.md用户 preferencessession_state(permission、plans/data path、mode transition)source state / enabled source 指引skill content 或读取前置要求recovery / branch seed / transfer summary(一次性)用户文本与 attachments不是所有内容都应放 system,也不是每轮都应重算。
10.2 PromptBuilder
Section titled “10.2 PromptBuilder”PromptBuilder 是 BaseAgent 共享模块。核心拆成:
Volatile context
Section titled “Volatile context”- 当前日期/时间;
session_state:permission、paths、一次性 mode transition signal;- source state。
特点:每轮可能变化、必须恰好构建一次。尤其 session state 会“消费”一次性 transition;若同一 turn 为日志和 prompt 调两次 builder,第二次内容不同。
Stable context
Section titled “Stable context”- workspace capabilities;
- working directory/长期能力描述等相对稳定内容。
特点:幂等、适合缓存前缀。
Recovery context
Section titled “Recovery context”buildRecoveryContext:当 provider resume 失败时,把最近持久 user/assistant 对压成隐藏块,帮助新 SDK session 接续。
Pinned preferences
Section titled “Pinned preferences”姓名、时区、城市、偏好 notes 等可固定在会话首个 system prompt 组件中,避免 compaction 后同一会话的人设/关键约束漂移。
10.3 为什么 volatile 必须只构建一次
Section titled “10.3 为什么 volatile 必须只构建一次”一个隐蔽 bug:
debug(buildVolatileContextParts())send(buildVolatileContextParts())若第一次消费 modeTransition,模型实际收到的第二份就缺信息。代码注释把“exactly once”设为不变量。正确做法:
const volatile = buildVolatileContextParts(...)debug(volatile)send(volatile)这类 one-shot context 应尽量显式返回“内容 + commit token”,比隐藏消费更易推理;当前实现需要调用者自律。
10.4 Claude 与 Pi 的放置差异
Section titled “10.4 Claude 与 Pi 的放置差异”Claude path
Section titled “Claude path”Claude 将 volatile + stable context 组合到 user turn tail。SDK 自己管理 system prompt/resume/compaction,统一尾注可减少恢复路径差异。
Pi path
Section titled “Pi path”- stable parts 进入 system prompt 的缓存友好前缀;
- volatile parts 进入 user message tail;
- 每轮时间/session state 的变化不会让整个 system prefix cache 失效。
flowchart TD ST["Stable context"] --> PIS["Pi system prefix / cache"] VO["Volatile context"] --> TAIL["Current user tail"] USER["User message"] --> TAIL PIS --> REQ["Model request"] TAIL --> REQ这是 context engineering 而非 provider-neutral 纯抽象:同一语义根据 backend cache contract选择位置。
10.5 System prompt 的构造
Section titled “10.5 System prompt 的构造”入口:getSystemPrompt。它组合:
- default/mini/custom preset;
- session tools 与工作方式;
- permission mode/plan/data folder 规则;
- source/skill 使用规则;
- project context;
- working directory 中的 context file 清单;
- platform/headless 特征。
Mini agent 使用更短 preset 和最小工具集(Read/Edit/Write/Glob/Grep/Bash),用于摘要/小任务,不继承所有交互 UI 能力。
10.6 AGENTS.md / CLAUDE.md 发现
Section titled “10.6 AGENTS.md / CLAUDE.md 发现”findAllProjectContextFiles 递归寻找大小写不敏感的 AGENTS.md/CLAUDE.md:
- 排除 node_modules/build/cache 等目录;
- 单文件最多约 10KB;
- 数量上限约 30;
- 结果缓存约 5 分钟;
- system prompt 主要列出发现位置,指示 Agent 按作用域读取,而不是盲目内联整个 monorepo。
这种做法兼顾:
- monorepo 每个 package 有局部规则;
- system prompt 不被几十个文件撑爆;
- Agent 在操作目标前主动读取最近作用域规则。
10.7 Project context defanging
Section titled “10.7 Project context defanging”Project memory、资产说明和用户文件属于不可信内容。formatProjectContextForPrompt() 把它们放进明显边界,避免其中的“忽略之前指令”被误当系统规则。
防御原则:
- 标明内容来源和权威级别;
- 用结构标签包裹;
- 限长;
- 不让用户内容闭合标签/伪造高优先级区块;
- 明确它是数据而非指令。
源码:formatProjectContextForPrompt。
10.8 Project MEMORY.md
Section titled “10.8 Project MEMORY.md”Memory 是项目层长期知识,默认上限 5k tokens,保留顶部。它与 conversation history 分工:
- history:发生过什么,provider resume/compaction 管;
- memory:跨 session 仍有用的结论,由 Agent/用户维护;
- preferences:跨 workspace 的个人偏好;
- session state:当前 turn 临时事实。
将四者混成一个“memory”会导致作用域泄漏和无穷增长。
10.9 Skills 的渐进加载
Section titled “10.9 Skills 的渐进加载”Skill mention 或 Task spec skills 不直接把全部 SKILL.md 内联。PrerequisiteManager 记录 required reads:
prompt 出现 [skill:x]→ 模型看到 skill 路径/要求→ 在执行其他工具前必须 Read SKILL.md→ pre-tool-use 检查读取是否完成→ 完成后开放后续工具若 skill 声明 required source,SessionManager 在 chat 前先 enable/activate source。这样“说明”和“能力”同时就绪。
Compaction 后模型可能忘记已经读过 guide/skill,代码会 reset prerequisite state,要求重新读取。这是非常关键的恢复语义。
10.10 Source guide
Section titled “10.10 Source guide”API source 的 endpoint 文档不再全部塞 tool description。Tool description 保持短,Agent 先读 source guide.md;prerequisite manager 可强制这个顺序。
收益:
- tool schema/token 更小;
- 文档可由 workspace 自定义;
- source 热更新不要求重写每个 tool description;
- 只有真正使用 source 的 session 支付上下文成本。
10.11 Attachments
Section titled “10.11 Attachments”附件处理按能力分层:
- 图片/PDF 可转 provider content block;
- Office/邮件/日历等通过内置 CLI 工具转文本或让 Agent按需读取;
- 过大图片在 PreToolUse Read 前 resize;
- provider/model 不支持 image 时过滤或提供文本提示;
- 路径使用 session/workspace 语义,不能把远程服务端路径误当客户端路径。
附件不应全部 base64 持久化到 transcript;StoredAttachment 与 session data 文件保持可恢复引用。
10.12 Resume 与 recovery context
Section titled “10.12 Resume 与 recovery context”正常优先使用 provider transcript,因为它保留精确 tool/message graph。只有 resume 失败才用 Craft messages 构造 recovery:
provider resume ├─ success → 不重复注入历史 └─ failure → 清无效 sdk id → recent persisted messages → recovery block → fresh provider session → one retry如果 resume 成功又注入同一历史,会重复上下文、增 token,甚至让模型以为用户重复发言。
10.13 Branch seed 与 transfer summary
Section titled “10.13 Branch seed 与 transfer summary”两种一次性上下文:
Branch seed
Section titled “Branch seed”provider-native fork 不可用但产品允许降级时,将截止点前消息摘要/种子注入 fresh session。注入后 markBranchSeedApplied() 持久化,避免重启再注入。
Transfer summary
Section titled “Transfer summary”跨 server bundle 未携带 provider transcript 时,源 server 生成 conversation summary;目标 session 首 turn 注入,随后 transferredSessionSummaryApplied=true。
它们都不是永久 system prompt,而是“恢复一次性桥”。
10.14 Compaction 的两个层面
Section titled “10.14 Compaction 的两个层面”Provider context compaction
Section titled “Provider context compaction”对模型上下文做摘要并保留近端 entries。Claude 通过 SDK /compact;Pi 通过 session.compact() 或 SDK auto-compaction。
Craft transcript persistence
Section titled “Craft transcript persistence”完整/展示消息仍保存在 Craft JSONL。Compaction 不是删除 UI 历史;它改变 provider 下一轮看到的上下文与锚点可用性。
这一区分解释了:用户仍能滚回旧消息,但旧 branch anchor 可能已被 provider compact 掉。
10.15 Claude /compact
Section titled “10.15 Claude /compact”ClaudeAgent 识别 SDK slash command,强制走 per-turn query,而不是 persistent input shortcut,因为 compact 会修改 provider session 状态。SDK 返回 compaction status/info:
- SessionManager 持久化 compaction complete message;
- reset prerequisite state;
- UI 展示 compacting/complete;
- branch 若引用被压掉的 message UUID,走明确 fallback。
10.16 Pi compaction
Section titled “10.16 Pi compaction”Pi 的 auto-compaction 由 SDK在 overflow 时执行并 continue。Wrapper 保持 EventQueue 在 recovery 期间打开,使恢复后的 turn 能继续到 UI。
手动 compact 必须等待正在进行的 auto-compaction;并行两个 compaction 会争用 agent abort/controller 与 entry state。请求 timeout 比普通 RPC 长,因为大对话摘要可能需要 60–120 秒。
10.17 Accept & Compact
Section titled “10.17 Accept & Compact”Plan 审批有一个跨 reload 的工作流:
stateDiagram-v2 [*] --> PlanSubmitted PlanSubmitted --> AwaitingCompaction: user Accept & Compact AwaitingCompaction --> ReadyToExecute: compaction_complete ReadyToExecute --> Dispatched: UI/server dispatch plan prompt Dispatched --> [*]: clear pending stateSession 持久化:
pendingPlanExecution = { planPath, draftInputSnapshot?, awaitingCompaction, executionDispatched?}为什么这么细?若用户在压缩完成前 Cmd+R:
- 没持久化 → plan 丢;
- 只存 planPath 不存 awaiting state → 可能提前执行;
- 不存 executionDispatched → reload 后重复执行。
SessionManager 在 compaction complete event 中更新持久状态,并可立即触发后续,不依赖 UI 恰好在线。
源码:pendingPlanExecution、SessionManager。
10.18 上下文预算策略
Section titled “10.18 上下文预算策略”可总结为一个优先级:
- 规则/安全不变量:system,短且稳定;
- 能力目录:system 稳定前缀或短清单;
- 动态 session/source:当前 user tail;
- 大文档/skill/source guide:按需 Read;
- 老对话:provider transcript + compaction;
- 跨 session 长期知识:Project MEMORY.md,限额;
- 大 tool result:落文件 + 摘要;
- 恢复/迁移:一次性隐藏 summary。
10.19 风险与改进点
Section titled “10.19 风险与改进点”PromptBuilder 有消费副作用
Section titled “PromptBuilder 有消费副作用”volatile context exactly-once 依赖调用纪律。可以重构成显式 prepareTurnContext() 返回冻结对象。
System prompt 过大
Section titled “System prompt 过大”主 prompt 文件本身很长;随着功能增加,规则冲突概率上升。应持续把“如何使用特定 source/skill”移到按需文档。
Compaction 后行为漂移
Section titled “Compaction 后行为漂移”SDK summary 可能遗漏权限/项目约束。固定 system components、reset prerequisites 和 session_state 重注入是防线,但需要跨 provider 测试。
Branch anchor 生命周期
Section titled “Branch anchor 生命周期”Provider compaction/TTL 会让锚点失效。产品 UI最好显示分支可用性或在创建时明确降级,而不是到首消息才惊讶。
10.20 本章小结
Section titled “10.20 本章小结”Craft 的 context engineering 核心是按“稳定/易变/按需/一次性/可压缩”分类,再结合 provider 缓存与 resume contract 放置。Compaction 只压模型上下文,不删除产品 transcript;恢复与迁移通过一次性 summary 补桥。
下一章进入工具安全:模型有了上下文后,真正执行 Bash、写文件、调用 API 之前,中央 PreToolUse 管道怎样做决定。