--- title: "系统提示词" description: "系统提示词不是一段写死的文本,而是一个动态组装、分块缓存的数组。理解三阶段管道、缓存分界标记和多级优先级选择的设计。" keywords: ["系统提示词", "System Prompt", "动态组装", "Prompt Cache", "缓存策略"] --- ## 核心问题 AI 的行为由系统提示词(System Prompt)决定。但一个编程助手的系统提示词远不止一句"你是一个有用的助手"——它需要包含工具使用规则、安全策略、项目上下文、用户偏好等大量动态内容。 挑战在于:**这些内容每次请求都要发送,而且大部分是不变的。** 如何在保持动态性的同时最小化 token 成本? ## 从文本到数组:缓存驱动的架构选择 系统提示词不是单个字符串,而是一个 **字符串数组**。 ### 为什么是数组? Anthropic 的 Prompt Cache 以**内容块**为单位缓存。将提示词拆为多个块,不变的部分可以获得独立的缓存命中。如果是一个单字符串,任何一个字符变化(如日期更新)都会导致整个提示词的缓存失效。 一个典型的系统提示词约 20K+ tokens,通过缓存分块可以节省 30-50% 的输入 token 费用。 ### 品牌类型保证 系统使用品牌类型(branded type)防止普通字符串数组被意外传入 API 调用——只有通过显式转换才能获得系统提示词类型。这是零开销的编译时安全保证。 ## 三阶段组装管道 ``` 收集内容 → 选择优先级 → 分块 + 缓存标记 ``` ### 第一阶段:内容收集 系统提示词的内容分为两个区域: | 区域 | 内容 | 特点 | |------|------|------| | **静态区** | 工具使用规则、安全策略、输出格式规范 | 所有用户相同 | | **动态区** | 记忆、MCP 指令、模型覆盖、语言偏好 | 因用户/会话而异 | 两个区域之间用一个特殊的**分界标记**分隔。这个标记永远不会发送给 AI——它只是告诉缓存系统:"到这里为止是静态的,从这里开始是动态的"。 ### 第二阶段:优先级选择 最终使用哪个提示词取决于五级优先级: | 优先级 | 来源 | 场景 | |--------|------|------| | **Override** | 外部覆盖 | SDK 集成、自动化测试 | | **Coordinator** | 协调者模式 | 多 Agent 编排 | | **Agent** | Agent 定义 | 自定义 Agent | | **Custom** | 命令行参数 | 用户的自定义提示词 | | **Default** | 完整组装 | 正常使用 | **设计考量**:Override 级别完全替换默认提示词(包括安全规则),这是危险的但必要的——自动化测试和 SDK 集成需要完全控制。其他级别则在默认提示词基础上追加或替换。 ### 第三阶段:分块与缓存标记 分块策略根据条件选择三种模式: **模式 1:全局缓存(1P 用户默认)** ``` [归属头] → 不缓存 [提示词前缀] → 不缓存 [静态内容] → 全局缓存(跨组织共享) [动态内容] → 不缓存 ``` **模式 2:组织缓存(MCP 工具存在时)** ``` [归属头] → 不缓存 [提示词前缀] → 组织缓存 [其余内容] → 组织缓存 ``` **模式 3:组织缓存(3P 用户)** ``` [归属头] → 不缓存 [提示词前缀] → 组织缓存 [其余内容] → 组织缓存 ``` **为什么 MCP 工具会降级缓存**?MCP 工具列表在会话中可能变化(连接/断开),这会改变提示词内容,破坏跨组织缓存的基础。因此当 MCP 工具存在时,只能使用组织级缓存。 ### 缓存破坏的防御 动态区的某些内容(如会话特定的工具集)必须严格放在分界标记之后。如果错误地放在静态区,每个运行时条件的组合会产生 2^N 种不同的哈希值(N = 条件数量),完全破坏缓存命中率。 系统对"每轮都重新计算"的 Section 使用专门的危险标记——开发者必须给出破坏缓存的理由才能使用它。 ## 上下文注入:两条独立管道 系统提示词本身不包含运行时上下文。上下文通过两条独立管道注入: ### System Context(会话级不变) - Git 分支和状态 - 最近的提交记录 - 计算一次,整个会话期间缓存 ### User Context(动态变化) - 合并后的 CLAUDE.md 内容 - 当前日期 **为什么 User Context 不放在 System Prompt 中**?因为 User Context 作为用户消息发送,可以利用 Prompt Cache 的前缀共享——系统提示词是所有用户共享的前缀,User Context 是每个用户的变化部分。这种分层让缓存命中率最大化。 ## CLAUDE.md:项目级知识注入 这是 Claude Code 最巧妙的设计之一。在项目目录中放一个 `CLAUDE.md` 文件,就能让 AI "理解"项目。 ### 多级合并 ``` ~/.claude/CLAUDE.md ← 用户全局(个人偏好) └── /project/CLAUDE.md ← 项目根目录(团队共享) └── /project/src/CLAUDE.md ← 子目录(模块特定) ``` 系统从当前工作目录向上遍历,合并所有匹配的 CLAUDE.md 文件。子目录的 CLAUDE.md 可以覆盖或补充父目录的规则。 **设计哲学**:项目知识应该由团队成员维护、随代码演进、可通过 git 追踪。CLAUDE.md 本质上是"给人读的项目文档,恰好 AI 也能读"。 ### 安全考量 项目级 CLAUDE.md 可以被仓库中的任何人修改(包括恶意贡献者)。系统因此限制了项目级 CLAUDE.md 的影响范围——它们不能覆盖安全关键设置,也不能修改权限模型。 ## Provider 差异与缓存 不同的 API Provider 有不同的缓存能力: | Provider | 全局缓存 | 精确 Token 计数 | 特殊 Beta 功能 | |----------|:--------:|:---------------:|:--------------:| | Anthropic 直连 | ✓ | ✓ | ✓ | | AWS Bedrock | ✗ | ✓(独立端点) | 部分 | | Google Vertex | ✗ | ✗ | 部分 | | OpenAI 兼容 | ✗ | ✗ | ✗ | | Gemini | ✗ | ✗ | ✗ | 3P 用户的系统提示词始终使用组织级缓存,因为没有全局缓存的 API 支持。这也意味着 token 计数依赖估算,影响自动压缩的触发时机。 ## 最小化模式 环境变量 `CLAUDE_CODE_SIMPLE` 可以将整个系统提示词缩减为一行——跳过所有 Section 注册、缓存分块和动态组装。用于最小化 token 消耗的测试场景。 ## 接下来 - **上下文压缩** — 理解当上下文增长超出限制时的压缩策略 - **令牌预算** — 了解 token 窗口的动态计算 - **Provider 系统** — 了解多 Provider 支持的架构设计