提示词缓存
wescode 中的 per-conversation prompt caching——降低成本的关键机制。
缓存概念
Prompt 缓存允许 LLM provider 在对话中复用不变的前缀,显著降低 token 费用:
第 1 轮:[系统提示词 + 工具 + 技能 + 记忆] + [用户消息]
├──── 缓存前缀 ────────────────────┤ ├─ 新增 ─┤
→ 全量计费
第 2 轮:[系统提示词 + 工具 + 技能 + 记忆] + [历史消息] + [用户消息]
├──── 缓存命中(减免)────────────┤ ├───── 新增 ─────┤
→ 前缀按缓存价格计费(通常 ~10%)
第 N 轮:前缀越长 → 缓存节省越多
缓存条件
缓存前缀必须逐字节相同。任何改变——即使是一个空格——都会使缓存失效。
什么会打破缓存
以下操作会使缓存失效:
| 操作 | 影响 | 严重性 |
|---|---|---|
| 修改过去上下文 | 前缀字节变化 | 🔴 全量重计费 |
| 切换工具集 | 工具 schema 变化 | 🔴 全量重计费 |
| 重建系统提示词 | 前缀变化 | 🔴 全量重计费 |
| 加载/卸载技能 | 系统提示词变化 | 🔴 全量重计费 |
| 修改记忆 | 系统提示词变化 | 🔴 全量重计费 |
唯一允许的失效场景
上下文压缩是唯一允许的缓存失效场景——当 token 接近上限时,压缩是必要的。
系统提示词 byte-stable 保证
在整个对话生命周期内,系统提示词保持字节稳定:
对话开始时构建系统提示词(一次性):
├── Agent 角色描述
├── 工具 schema(所有启用的工具)
├── 技能内容(所有启用的技能)
├── Convention 快照
└── Eager Memory(环境/约束/指令等)
此后整个对话期间不改变
→ 字节稳定
→ 缓存持续命中
Slash 命令的缓存感知
默认行为:延迟失效
大多数修改状态的 Slash 命令默认在下一个会话生效:
/skills install code-review
→ 技能安装成功
→ 当前对话:不加载(保持缓存)
→ 下一个新对话:自动加载
即时失效:--now 标志
/skills install code-review --now
→ 技能立即加载
→ 系统提示词重建
→ 缓存失效 ⚠️
→ 当前对话费用增加
缓存感知设计
| 操作 | 默认 | --now |
|---|---|---|
| 安装技能 | 下次生效 | 立即生效 + 缓存失效 |
| 卸载技能 | 下次生效 | 立即生效 + 缓存失效 |
| 修改记忆 | 下次生效 | 立即生效 + 缓存失效 |
| 切换工具 | 下次生效 | 立即生效 + 缓存失效 |
不在对话中途切换工具集
禁止在对话中途切换工具集(强制缓存失效 + 模型困惑):
❌ 对话进行中 → 启用 "browser" 工具集
→ 工具 schema 变化
→ 缓存失效
→ 模型可能对新工具产生幻觉
✅ 开新对话 → 新对话自动包含"browser"工具集
技能/记忆/工具变更生效时机
| 变更类型 | 即时生效 | 下次对话生效 |
|---|---|---|
| 安装技能 | /skills install X --now | /skills install X |
| 卸载技能 | /skills uninstall X --now | /skills uninstall X |
| 修改工具集 | 不推荐 | 默认 |
| Memory 写入 | Eager ❌ / Lazy ✅ | Eager ✅ |
| Convention 变化 | ❌(下次扫描) | ✅ |
Lazy Memory 的特殊性
Lazy Memory 不影响 prompt cache 前缀:
系统提示词(缓存前缀):
├── Agent 描述
├── 工具 schema
├── 技能
├── Eager Memory ← 对话期间固定
└── Convention ← 对话期间固定
用户消息旁边(不在前缀中):
└── Lazy Memory overlay ← 每轮动态变化,不影响缓存
确定性排序
为了确保缓存命中,以下数据源在注入前必须确定性排序:
| 数据源 | 排序方式 |
|---|---|
| 工具 schema | 按名称排序 |
| 技能内容 | 按名称排序 |
| Eager Memory | 按 scope + namespace + id 排序 |
| 环境信息 | 按字段名排序 |
| Plugin 注册 | 按名称排序 |
如果排序不确定(如 map 遍历),即使内容相同,字节顺序不同也会导致缓存失效。
成本影响估算
假设系统提示词 = 10K tokens,对话历史 = 每轮增加 ~2K tokens:
| 轮次 | 无缓存费用(相对) | 有缓存费用(相对) | 节省 |
|---|---|---|---|
| 1 | 10K | 10K | 0% |
| 5 | 50K | 14K + 36K × 10% | ~70% |
| 10 | 100K | 19K + 81K × 10% | ~80% |
| 20 | 200K | 29K + 171K × 10% | ~85% |
长对话中缓存节省显著——保护缓存直接降低费用。
最佳实践
- 不要在对话中途切换工具/技能:开新对话替代
- 使用延迟失效:除非必须立即生效
- 理解 Lazy Memory:它是缓存安全的
- 长对话更受益:缓存节省随对话长度增加
- 关注变更时机:大多数变更在下次对话自动生效