SDD(Spec-Driven Development,规范驱动开发)也火了一段时间了,你可能听过但是不一定用过,核心是:先写一份详细的需求文档,让 AI 按文档实现。
但很多人没注意到一个问题:
SDD 的核心是把大段 spec 塞进 prompt,而这恰恰是缓存命中率的杀手。
● ● ●
先搞清楚 prompt cache 在做什么
根据 Anthropic 官方文档,机制很简单:
API 服务商把你请求的前半段存下来,下次前半段没变就直接用缓存。
为什么前半段能缓存?
大模型推理分两步:先把输入的 token 全部转成 KV Cache(键值缓存),再基于这个缓存逐个生成输出。
KV Cache 是 Transformer 架构的产物。每一层注意力计算时,输入的每个 token 都会算出一对 Key 和 Value 向量存起来。后续生成新 token 时,要用这些 Key/Value 和当前 token 做注意力计算。
这个前置的 KV Cache 计算是重复且耗时的——如果两次请求的前缀一模一样,算出来的 KV Cache 也一模一样。
所以 API 服务商把前缀的 KV Cache 存下来,下次遇到相同前缀直接复用,跳过整个前置计算阶段:
请求 1:[系统提示词 + 工具列表 + spec] + 用户消息A
↑ 这部分算出的 KV Cache 存入缓存
请求 2:[系统提示词 + 工具列表 + spec] + 用户消息B
↑ 前缀没变 → 直接取缓存,不重算
↑ 只算新增部分的 KV Cache
缓存命中时省掉的不只是钱——而是整个前缀的前向计算。 这就是为什么读取只要 0.1× 的价格:服务商几乎不用算力,只花了一次内存读取。
命中和不命中,速度差多少?
拿两组真实模型算一下。假设你的 system prompt + 工具列表 + spec 共 20000 token,每轮用户消息 2000 token。
GLM-5.2(智谱,推理速度约 100 token/s)
| 极慢 | |||
| 快 10 倍以上 |
GPT-5.6(OpenAI,推理速度约 120 token/s)
| 极慢 | |||
| 快 10 倍以上 |
这里的关键不是输出速度,而是 TTFT(Time To First Token,首字延迟)。 不命中时,模型要先算完 20000 个 token 的 KV Cache 才能吐出第一个字。命中时,这 20000 个 token 的计算直接跳过。
实际感受:不命中时你可能等十几秒才看到回复开始,命中时几乎是秒回。20 轮对话累计下来,光等待前缀重算的时间就能差几分钟。
所以 prompt cache 的价值有两层:省钱是表层,省时间是底层。 对于交互式 Agent,用户等待体验比 token 费用更致命。
缓存边界(cache breakpoint)把 prompt 一分为二:
| 每次都一样 → 缓存命中 | 每次都变 → 全价计费 |
定价模型(官方文档):
只要同一前缀被请求超过两次,缓存就开始省钱。
● ● ●
SDD 为什么容易破坏缓存
SDD 的典型做法是把 spec 文档注入到 prompt 里。问题出在 spec 放在哪:
❌ SDD 常见错误做法
system = [
"你是一个资深工程师...", ← 这部分固定
"项目技术栈:Flask + ...", ← 这部分固定
spec_document, ← 问题在这!
]
messages = [用户消息...] ← 这部分变化
看起来 spec_document 也固定的,应该能缓存。但实际问题有两个:
问题一:spec 经常修改
SDD 的核心是"spec 先行"。意味着你在开发过程中会反复修改 spec——加一条需求、改一个接口、调一个字段。每次改 spec,整个缓存前缀全部失效。
| 缓存失效!重新写入 | ||
| 又失效... |
问题二:spec 太大
有人测试过 Claude Code 的基础开销是 20000-30000 token(来源)。如果你的 spec 再加 5000 token,每次缓存写入的成本就更高。
● ● ●
正确做法:把 spec 拆成静态和动态
关键思路:不要把整个 spec 当成一个不可分割的块。把它拆开:
✅ 优化后的 SDD + 缓存策略
system = [
身份声明, ← 永远不变 → 深层缓存
工具列表, ← 很少变 → 深层缓存
全局架构规范, ← 很少变 → 深层缓存
─── cache_control 断点 ───
当前迭代的需求文档, ← 会变 → 浅层缓存
]
messages = [具体任务...] ← 每次变 → 不缓存
Anthropic API 支持最多 4 个 cache_control 断点(文档)。利用多断点把 prompt 分成多层:
system=[# 第一层:几乎永远不变(深度缓存){"type":"text","text":identity_and_tools,"cache_control":{"type":"ephemeral"}},# 断点 1# 第二层:版本迭代时才变(浅层缓存){"type":"text","text":architecture_rules,"cache_control":{"type":"ephemeral"}},# 断点 2# 第三层:每次开发会改(更浅缓存){"type":"text","text":current_sprint_spec,"cache_control":{"type":"ephemeral"}},# 断点 3]messages=[...]# 不缓存
改 spec 时只有第三层之后的缓存失效,前两层仍然命中。
● ● ●
算一笔账
假设你的 SDD 项目:
身份 + 工具 + 架构规范:15000 token(几乎不变) 当前迭代的 spec:5000 token(每 2-3 天改一次) 每轮用户消息 + 工具结果:平均 3000 token
不做缓存分层(整个 spec 一起缓存):
每次改 spec,全部 20000 token 重新写入。20 轮对话改了 5 次:
| 总计 | 215,000 token |
做了缓存分层(三层断点):
改 spec 时只有第三层失效(5000 token),前两层仍然命中:
| 总计 | 146,000 token(省 32%) |
spec 越大、改动越频繁,分层缓存的收益越高。
● ● ●
上下文压缩也和缓存有关
Claude Code 在每轮调模型前会做上下文压缩。它的设计思路值得借鉴:压缩只处理对话历史(messages),不会动 system prompt 前缀——所以缓存依然命中,只是 messages 部分更省。
这给了 SDD 一个启示:spec 内容如果太大,不要全部留在对话历史里。让 AI 读完后压缩成摘要,保留关键约束就行。
压缩的策略是从轻到重分四步:
前三步是纯文本操作,最后一步才花一次 API 调用。不是等报错后才救火,而是每次调用前都体检。
● ● ●
SDD + 缓存的实操建议
1. spec 分层放置
2. 不要在 spec 里写会变的东西
❌ "当前时间是 2024-01-15,使用 React 18.2.0" ✅ "使用 React 18,具体版本看 package.json"
3. spec 改了之后主动刷新缓存
Anthropic API 支持 max_tokens: 0 做一次空写入(文档):发一个不生成输出的请求,只为了把新 spec 写入缓存。后续请求就能命中了。
4. CLAUDE.md 保持精简
真实案例(来源):有用户把 1358 行规则精简到 807 行(减 41%),方法就是把流程性规则从 CLAUDE.md 移到按需加载的 Skills。
CLAUDE.md 就是你的全局 spec——它越大,每次缓存写入越贵。
● ● ●
到底需不需要 SDD?
很多人会说:"我提示词写得差不多,需求就能完成,为什么要费劲写 spec?"
这个问题要分两个角度回答:精准开发质量和缓存成本。
从精准开发的角度:看你项目的复杂度
先说结论:SDD 不是越多越好,也不是完全不需要——取决于你的项目复杂度和你对结果的要求。
不需要 SDD 的场景:
提示词写得差不多确实能完成需求——当任务是单次、简单、边界清晰的时候。
"帮我用 Flask 写一个用户登录接口,密码用 bcrypt,
返回 JWT,参照 auth/middleware.py 的风格"
→ 这种任务不需要 spec,模型直接就能写好。
模型已经足够聪明,简单的 CRUD 不需要写文档。 你给它上下文和约束,它就知道怎么做。Claude Code 自己用 515 段动态拼装而不是一份大 spec 就证明了这一点——它只在需要时才加载详细规则。
需要 SDD 的场景:
当任务涉及多模块协作、跨迭代维护、多人开发时,不写 spec 会出问题:
场景:一个电商后台,30 个 API,5 张核心表,
涉及权限、支付、库存、订单状态机。
不写 spec 的后果:
- 第 1 轮模型帮你写了用户模块
- 第 2 轮你让它加支付模块
- 第 3 轮改库存逻辑时,它忘了第 1 轮的表结构
→ 因为上下文被压缩了,早期信息丢了
- 结果:接口冲突、命名不一致、重复代码
SDD 的真正价值不是"让 AI 写得更好",而是"让 AI 在多轮迭代中保持一致"。 spec 是项目的记忆载体——模型上下文会丢,但 spec 文件不会。
判断标准:
一句话:SDD 适合"需求明确但实现复杂"的项目。需求不明确时写 spec 等于浪费——你改 spec 的时间比写代码还多。
从 prompt cache 的角度:看你调 API 的方式
SDD 对缓存的影响,取决于你的调用模式:
用 Claude Code / Cursor(不直接调 API):
不用管缓存——工具层帮你做了 但 CLAUDE.md(你的全局 spec)别写太大 1358 行精简到 807 行,省 41% 的开销
工具用户不需要管 cache,但需要管 spec 的大小。
调 API 做简单应用(短对话):
对话不超过 5 轮 → 缓存收益很小 写入贵 25%,至少要命中 2 次才回本 5 轮里改了 2 次 spec → 缓存基本没省到
短对话场景,SDD 对缓存的影响可以忽略。 不要为了省缓存而过度设计 spec 分层。
调 API 做长周期 Agent(20+ 轮对话):
这是 SDD + 缓存真正重要的场景。
spec 越大、改动越频繁,分层缓存收益越高,极端情况下差距 3-5 倍。
长对话场景,SDD 的缓存管理直接决定项目能不能跑下去。 不做分层,token 费用可能让你被迫中断开发。
两个角度的结合:决策矩阵
| 精准开发 | |||
| 缓存成本 | |||
| 结论 |
大部分人觉得"提示词写得差不多就行",是因为他们的项目还在"简单"那一列。 项目变复杂了,不写 spec 的后果不是"质量差一点",而是"模型在多轮迭代中逐渐失控"——上下文被压缩、早期决策被遗忘、命名和结构越来越不一致。
SDD 不是为了让模型更聪明,而是为了在项目变复杂时保持一致性。Cache 不是为了省钱,而是为了在长对话中保持可持续。
实际业务中 SDD 怎么落地?
很多人理解了缓存原理,但到了实际项目还是不知道:spec 放哪?用 Skill 沉淀还是单独维护 docs?CLAUDE.md 到底怎么加载?项目级 spec 每次都要手动告知文件位置吗?
CLAUDE.md 的加载机制
先回答最基础的:CLAUDE.md 到底是怎么被加载进 prompt 的?
Claude Code 有两级 CLAUDE.md,启动时自动加载:
两级 CLAUDE.md 都是自动加载的,不需要你每次手动告知。 全局管你的个人偏好(比如"所有函数必须有类型标注"),项目级管项目约定(比如"技术栈是 Flask + PostgreSQL")。两者会拼接在一起注入 system prompt。
关键问题:项目级 spec 文档需要每次手动告知位置吗?
不需要——如果你在 CLAUDE.md 里写好了指引。
CLAUDE.md(示例)
技术栈:Flask + SQLAlchemy + PostgreSQL
全局规则:函数必须有类型标注
## Spec 文档索引
- API 设计:docs/api-spec.md
- 数据模型:docs/data-model.md
- 认证模块:docs/module-auth.md
- 支付模块:docs/module-payment.md
开发新功能时,先读 docs/ 下对应的 spec 文件。
不要猜架构,一切以 spec 文档为准。
这段话只占 200 token,但模型每次都知道去哪找详细 spec。 你不需要每次开发都打"请先读 docs/api-spec.md"——CLAUDE.md 里的指引已经告诉它了。
工作流变成:
你说:"帮我加一个优惠券功能"
模型自动:读 CLAUDE.md → 发现 docs/ 有 spec 索引
→ 读 docs/api-spec.md 了解 API 规范
→ 读 docs/data-model.md 了解表结构
→ 按 spec 实现
你只需要说一句话,模型自己会去找 spec。 这就是 SDD 落地的关键:不是把 spec 灌进 prompt,而是在 CLAUDE.md 里建好索引,让模型按需读取。
Spec 沉淀的三层载体
Claude Code 有三层 spec 载体,各管不同的事:
具体操作:
项目结构
├── CLAUDE.md ← 全局架构 + spec 索引(精简)
├── docs/
│ ├── api-spec.md ← API 设计文档
│ ├── data-model.md ← 数据模型定义
│ └── module-auth.md ← 认证模块详细 spec
└── .claude/skills/
├── git-workflow ← 提交规范(触发时才加载)
└── deploy-check ← 部署检查清单(触发时才加载)
Skill vs docs 怎么选?
判断标准很简单:是不是每轮都用?是 → CLAUDE.md;有明确触发时机 → Skill;是参考文档 → docs/。
调 API 做产品的场景:spec 分层注入
如果你直接调 Anthropic API 做 Agent 产品,没有 CLAUDE.md 和 Skill,那分层靠 cache_control 断点实现:
第一层(深度缓存):身份 + 工具定义 + 全局规范
→ 几乎不变,放最前面
第二层(浅层缓存):当前版本架构 spec
→ 版本迭代才变
第三层(不缓存): 当前迭代的具体需求
→ 每次开发都可能改
messages = [本轮对话] → 完全不缓存
不管哪种工具,核心原则一致:不变放前面,少变放中间,多变放后面。
● ● ●
小结
SDD 的价值在于"先想清楚再写代码"。但想清楚的内容也要分层管理——不然你省下的思考时间全花在 token 费用上了。
参考资源:
Anthropic prompt caching 官方文档:https://platform.claude.com/docs/en/build-with-claude/prompt-caching Claude Code token 优化实战:https://boringbot.substack.com/p/how-to-save-millions-in-claude-tokens Prompt caching 实用指南:https://medium.com/@mcraddock/unlocking-efficiency-a-practical-guide-to-claude-prompt-caching-3185805c0eef
夜雨聆风