Files
claude-code/docs/context/system-prompt.mdx
claude-code-best 5301e0ba43 docs: 重写系统提示词,从源码走读改为动态组装设计分析
移除全部 TypeScript 代码、源码路径和实现常量,
聚焦数组缓存驱动的架构选择、三阶段管道设计、
三种缓存模式的选择逻辑和 CLAUDE.md 多级合并的安全考量。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-20 10:46:04 +08:00

152 lines
6.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 支持的架构设计