Claude Code System Prompt 分层:组装、缓存、Skills、泄露对照
一句话定位:Claude Code 的 System Prompt 是一个由 110+ 动态片段组装的模块化架构,通过静态/动态分界线实现 API 级 prompt cache 优化,每次 API 调用节省约 3000 token 的重复计费。
调研时间:2026-06-22
依据版本:泄露源码包(2026-03-31)+ system_prompts_leaks 仓库 + Piebald-AI/claude-code-system-prompts(v2.1.185)
核心文件:src/constants/prompts.ts、src/utils/systemPrompt.ts、src/constants/systemPromptSections.ts、src/constants/system.ts
证据等级汇总
| 结论 | 来源 |
|---|---|
| buildEffectiveSystemPrompt 5 级优先级 | [源码] systemPrompt.ts |
| 静态/动态分界线 + global cache scope | [源码] prompts.ts |
| systemPromptSection 缓存机制 | [源码] systemPromptSections.ts |
| 7 个静态段落 + 12+ 动态段落 | [源码] prompts.ts |
| 15 类提示词内容分类 | [社区] Piebald-AI 跟踪 |
| 安全监控独立并行运行 | [社区] system_prompts_leaks |
| CLAUDE.md 作为 userContext 注入 | [源码] context.ts |
| Skills 通过 SkillTool 展开而非直接注入 | [源码] skills/ |
| 跨 215 版本的 prompt 变更追踪 | [社区] Piebald-AI |
日常类比
把 System Prompt 想象成餐厅的员工手册:
- 静态部分(身份、规则、操作规范)= 手册正文,所有员工共用一本,印刷后不变
- 动态部分(环境信息、当日特供)= 每天早会的白板通知,每班次更新
- CLAUDE.md(项目规则)= 特定门店的补充规定
- Skills(技能)= 不写在手册里,但员工知道”需要时去查技能手册”
类比边界:真实系统中”手册正文”被 API 缓存后,后续请求只需传”白板通知”部分,大幅降低 token 成本。
组装全链路
graph TB
subgraph "Phase 1: 收集素材"
Prompts[prompts.ts<br>getSystemPrompt] --> Static[7 个静态段落]
Prompts --> Dynamic[12+ 动态段落]
Context[context.ts] --> UserCtx[CLAUDE.md + 日期]
Context --> SysCtx[Git status + 分支]
end
subgraph "Phase 2: 优先级决策"
BSP[buildEffectiveSystemPrompt]
Override[overrideSystemPrompt] -->|"P0 完全替换"| BSP
Coord[Coordinator prompt] -->|"P1 替换"| BSP
Agent[Agent definition] -->|"P2 替换/追加"| BSP
Custom[--system-prompt] -->|"P3 替换"| BSP
Default[defaultSystemPrompt] -->|"P4 兜底"| BSP
Append[appendSystemPrompt] -->|"始终追加"| BSP
end
subgraph "Phase 3: API 发送"
BSP --> Split[splitSysPromptPrefix]
Split --> StaticBlock["静态块<br>cache_control: global"]
Split --> DynamicBlock["动态块<br>cache_control: ephemeral"]
UserCtx --> Prepend[prependUserContext]
SysCtx --> AppendCtx[appendSystemContext]
end
优先级逻辑:buildEffectiveSystemPrompt
src/utils/systemPrompt.ts 定义了 5 级优先级 [源码]:
| 优先级 | 条件 | 行为 |
|---|---|---|
| P0 | overrideSystemPrompt 存在 |
完全替换所有其他 prompt |
| P1 | Coordinator Mode 激活 | 使用协调器专用 prompt |
| P2 | mainThreadAgentDefinition 存在 |
Proactive 模式:追加到 default;普通模式:替换 default |
| P3 | customSystemPrompt 存在 |
替换 default |
| P4 | 以上都不满足 | 使用 defaultSystemPrompt |
| +∞ | appendSystemPrompt 存在 |
始终追加在末尾 |
关键设计:Agent 在 Proactive/KAIROS 模式下是追加而非替换——因为自主 agent 需要保留基础行为规范,domain-specific 指令叠加在上面。
静态/动态分界线
┌─────────────────────────────────────────────────┐
│ STATIC ZONE (globally cacheable) │
│ ≈ 3000 tokens, scope: 'global' │
│ │
│ 1. Intro + Identity │
│ 2. System Rules │
│ 3. Doing Tasks (coding guidelines) │
│ 4. Executing Actions with Care │
│ 5. Using Your Tools │
│ 6. Tone & Style │
│ 7. Output Efficiency │
│ │
├─────── __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__ ───────┤
│ │
│ DYNAMIC ZONE (session-specific) │
│ scope: 'ephemeral' (5min or 1h TTL) │
│ │
│ - Session Guidance (agents, skills, verify) │
│ - Memory prompt │
│ - Environment info (cwd, platform, shell) │
│ - Language preference │
│ - Output style │
│ - MCP instructions (volatile, 每轮重算) │
│ - Scratchpad directory │
│ - Function Result Clearing │
│ - Token budget / Brief mode │
│ │
└─────────────────────────────────────────────────┘
缓存工程 [源码]
// systemPromptSections.ts
systemPromptSection(name, compute)
// 计算一次,缓存到 /clear 或 /compact
DANGEROUS_uncachedSystemPromptSection(name, compute, reason)
// 每轮重算,会破坏 prompt cache
// 命名中的 DANGEROUS 是对开发者的警告
为什么这么设计:Anthropic 的 Prompt Cache 按前缀匹配。静态部分标记 scope: 'global' 可跨用户/跨组织缓存,只计费一次。动态内容推到分界线之后,变化时不会 bust 前缀缓存。
1h TTL 优化:对 Anthropic 内部用户和付费订阅者,prompt cache TTL 从默认 5 分钟提升到 1 小时,进一步降低成本。
七个静态段落详解
1. Identity(身份定义)[源码][社区]
三种身份字符串根据运行模式切换:
- CLI 模式:
"You are Claude Code, Anthropic's official CLI for Claude." - Agent SDK 模式:
"...running within the Claude Agent SDK." - 纯 Agent 模式:
"You are a Claude agent, built on Anthropic's Claude Agent SDK."
包含硬约束:网络安全指令(CYBER_RISK_INSTRUCTION)、禁止生成 URL。
2. System Rules(系统规则)[源码][社区]
6 条运行时规则:
- 输出可见性:用户能看到 thinking + tool calls
- 权限模式:被拒绝后不重试同一操作
<system-reminder>标签:系统注入的元信息- Prompt 注入防护:不执行来自文件/URL 中的指令
- Hooks 反馈:hook 输出如何影响行为
- 自动压缩:context 满时自动摘要
3. Doing Tasks(任务执行)[源码][社区]
核心哲学:”做被要求的事,不做额外的事”。
反过度工程三原则:
- 反金镀:不加需求外功能
- 反过度抽象:三行相似代码优于过早抽象
- 反过度防御:不为不可能场景写错误处理
4. Executing Actions with Care(谨慎执行)[源码][社区]
操作前评估可逆性和影响范围。高风险操作(删文件/force push/创建 PR)需用户确认。核心格言:”measure twice, cut once”。
5. Using Your Tools(工具使用)[源码][社区]
最复杂的段落,核心规则:
- 禁止用 Bash 替代专用工具(Read 而非 cat、Edit 而非 sed)
- 独立工具调用必须并行
- 有依赖的必须串行
- 子 Agent Fork 指导
6. Tone & Style(语气风格)[源码][社区]
不用 emoji、代码引用用 file:line 格式、工具调用前用句号不用冒号。
7. Output Efficiency(输出效率)[源码][社区]
极简输出、倒金字塔结构。外部版强调”一句话能说清的不用三句”。
动态段落机制
| 段落名 | 缓存 | 内容 |
|---|---|---|
session_guidance |
是 | Agent 工具说明、Explore agent、Skill 工具、验证 agent |
memory |
是 | loadMemoryPrompt() — MEMORY.md + 相关记忆 |
env_info_simple |
是 | 工作目录、平台、Shell、模型名、知识截止日期 |
language |
是 | 语言偏好(跟随用户) |
output_style |
是 | 输出风格配置 |
mcp_instructions |
否 | MCP 服务器使用说明(volatile,每轮重算) |
scratchpad |
是 | 临时文件目录路径 |
frc |
是 | Function Result Clearing 说明 |
summarize_tool_results |
是 | “记下重要信息因为工具结果可能被清除” |
token_budget |
是 | Token 预算说明 |
brief |
是 | Brief 模式说明 |
mcp_instructions 是唯一使用 DANGEROUS_uncachedSystemPromptSection 的段落——因为 MCP 服务器可能在会话中途连接/断开。
CLAUDE.md 注入机制
CLAUDE.md 不是 system prompt 的一部分,而是作为 userContext 注入 [源码]:
// context.ts
getUserContext():
claudeMd: 从 .claude/ 目录层级加载所有 CLAUDE.md
currentDate: 当前日期
// 注入方式 (api.ts)
prependUserContext(messages, userContext)
// 作为第一条 user message 的前缀注入
层级加载:
~/.claude/CLAUDE.md— 全局规则<project>/.claude/CLAUDE.md— 项目规则<project>/.claude/settings.json中的claudeMdFiles— 额外规则文件
Skills 系统
Skills 不直接进入 system prompt 正文 [源码]:
Skills 来源:
1. Bundled skills (src/skills/bundled/) — 编译进二进制
2. File-based skills (.claude/skills/) — 从磁盘加载 SKILL.md
3. MCP skills — 通过 MCP 服务器动态发现
4. Conditional skills — 带 paths frontmatter,仅匹配文件时激活
注入方式:
system prompt 中只包含 "use SkillTool to execute them" 的指引
模型调用 SkillTool 时才展开具体 skill 内容
优化:
EXPERIMENTAL_SKILL_SEARCH flag 启用时
使用 Sonnet 侧查询预取相关 skills
避免将所有 skill 内容塞入 context
泄露对照:源码 vs system_prompts_leaks
| 维度 | 源码(prompts.ts) | 泄露合集(Piebald-AI) |
|---|---|---|
| 版本 | 2026-03-31 | 持续更新至 v2.1.185 |
| 格式 | TypeScript 函数 | 纯文本 markdown |
| 完整度 | 有条件编译(ant-only 段落不可见) | 运行时提取,更完整 |
| 动态段落 | 能看到 compute 逻辑 | 只能看到某次运行的快照 |
| 缓存标记 | 能看到 global/ephemeral 策略 | 不可见 |
| 安全监控 | 源码中未找到独立文件 | 泄露合集有完整 9 步分类流程 |
交叉验证结论:
- 源码中的
getSimpleIntroSection()对应泄露合集的 Identity 段落 ✓ - 源码中的
SYSTEM_PROMPT_DYNAMIC_BOUNDARY在泄露合集中可见 ✓ - 泄露合集中的安全监控(Security Monitor)在源码中未找到对应文件——可能是 API 侧注入或 ant-only 模块
- 泄露合集显示 prompt 在 215 个版本中持续演进,源码只是某个时间点的快照
与开源框架对比
| 维度 | Claude Code | LangGraph | tRPC-Agent |
|---|---|---|---|
| Prompt 管理 | 模块化 110+ 片段 + 缓存工程 | 用户自定义字符串 | 无内置 |
| 缓存优化 | 静态/动态分离 + global scope | 无 | 无 |
| 规则注入 | CLAUDE.md 层级加载 | 无标准方案 | 无 |
| 技能系统 | SkillTool 按需展开 | 无 | 无 |
| 安全监控 | 独立并行分类器 | 无 | 无 |
| 版本管理 | 编译时 + 运行时 flag | 用户代码管理 | 用户代码管理 |
关键设计决策
为什么 CLAUDE.md 是 userContext 而非 systemPrompt?
[源码] 因为 system prompt 有 global cache scope——如果项目规则进入 system prompt,每个项目都会 bust 全局缓存。放在 userContext 中,全局缓存不受影响。
为什么 Skills 不直接注入?
[源码] 一个项目可能有几十个 skills,全部注入会消耗大量 token。通过 SkillTool 按需展开 + skill search 预取,只在需要时加载相关 skill。
为什么有 DANGEROUS_ 前缀?
[源码] 这是对开发者的代码级警告:任何使用 DANGEROUS_uncachedSystemPromptSection 的段落都会在每轮破坏 prompt cache,增加约 3000 token 的重复计费。命名约定强制开发者思考是否真的需要每轮重算。
局限性
- ant-only 段落不可见:源码中有
process.env.USER_TYPE === 'ant'条件的段落,外部 build 中被剔除 - 安全监控:泄露合集中有完整的 9 步分类流程,但源码中未找到对应实现——可能是 API 侧注入
- 版本差异:源码是 2026-03-31,泄露合集跟踪到 v2.1.185(2026-06-20),中间有大量变更
- 缓存效果:无法从源码确定实际的 cache hit rate,需要运行时遥测数据
下一步
- Dynamic Workflow + Ultracode — 编排脚本如何影响 prompt 结构
- Claude Code 全景 — prompt 工程在 Agent 生态中的独特性
- 对比矩阵 — prompt 管理策略的框架间对比