Claude Code 深度拆解:上下文里有什么——System Prompt 工程

Hi,大家好,欢迎来到维元码簿。

本文属于 《Claude Code 源码 Deep Dive》 系列,专注于上下文工程中的 System Prompt 板块。如果你想了解整个系列,可以先看系列开篇 | Claude Code 源码架构概览:51万行代码的模块地图

本文聚焦一件事:模型的"人设"是怎么被塑造出来的------System Prompt 的每一个零件怎么组装,又怎么被缓存策略保护。

读完全文,你将能回答这几个问题:

  • Claude Code 专业沉稳的"人设"是谁写的? 没有一个人定义了它,那它是怎么被塑造出来的?
  • System Prompt 为什么是一段数组而不是一个大字符串? 这个看似随意的决定,背后藏着什么工程考量?
  • 为什么有些模块每次调用都重新计算,有些算一次就够了? "会话内一旦确定,不再变化"------这句话背后的缓存哲学是什么?

前情提要:模型收到了什么

本文的定位是上下文工程 ------拆解模型每次调用时 Claude Code 到底组装了什么。那最自然的起点,就是找到那个组装的终点:实际发给 Anthropic API 的请求参数。

paramsFromContext()src/services/api/claude.ts)返回发给 API 的完整参数。拆开看,真正变成 token 喂给模型的有三个字段:

字段 作用 预估占比
system 告诉模型"你是谁、怎么做事"的指令集 ~30%
messages 对话历史:用户输入、模型回复、工具调用结果 ~60%
tools 工具 Schema:告诉模型可以调用哪些工具 ~10%

把三个板块合在一起,模型看到的上下文全景如下:

三个板块各是什么:

  • System Promptsystem,~30%):模型的"身份设定与环境感知",由 7 个静态模块 + 11+ 个动态模块组装成 string[]。静态区所有用户共享,可命中全局缓存;动态区因会话变化。总入口 getSystemPrompt(),但可能被优先级链替换。
  • Messagesmessages,~60%):对话与隐藏注入,承载用户输入、工具结果、附件展开等全部对话信息。
  • Toolstools,~10%):30+ 内置工具 + MCP 外部工具的 JSON Schema。

本文只拆解第一个板块------System Prompt。 Messages 和 Tools 分别在姊妹篇中展开。


System Prompt 的骨架------7 静态 + 11+ 动态

全景图告诉我们 System Prompt 大约占模型上下文的 30%。但 30% 背后的工程量,远比数字显示的更精密。System Prompt 不是一段写死的文本,而是一个动态组装的指令集------根据用户类型、运行模式、工具配置、MCP 连接状态实时拼装。

总入口是 getSystemPrompt()src/constants/prompts.ts),返回 string[]------数组而非大字符串,目的是让后续缓存切分逻辑按元素粒度标记边界。

如上图所示,中间的 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 把 prompt 分为静态区和动态区。静态区对所有用户完全相同,可用 scope: 'global' 跨组织缓存;动态区因会话而异,只能用 scope: 'org' 或逐 turn 重算。团队在设计 prompt 时就把缓存作为一等公民:每个新 section 都必须回答"放在边界之前还是之后?"。

静态区------7 个不变的模块

静态区的内容对所有用户完全相同------你可以和地球另一端的用户共享同一份 KV cache,只要字节一致。这 7 个模块构成了 Claude Code 的"人格基础"。

模块 核心原文 工程洞察
Intro / 身份声明 You must NEVER generate or guess URLs "安全壳"设计------编程场景中模型编造 URL 可导致访问恶意网站
System / 系统行为 Your conversation is not limited by the context window 告诉模型有自动压缩兜底,不会因"怕用完 token"而拒绝复杂任务
Doing Tasks / 任务执行 Don't speculate. Three lines of similar code are fine 最长的静态模块。"不过度设计"贯穿始终
Actions / 行动准则 Consider the reversibility and blast radius of actions 如果只读一个模块就读这个。可逆操作自由执行,不可逆必须确认
Using Tools / 工具使用 Prefer dedicated tools over Bash Bash 最不透明,用户难以审查
Tone & Style / 语气 不用 emoji、代码引用用 file:line 解决了真实 UX 问题
Output Efficiency Go straight to the point. Don't be unnecessarily terse 外部版偏简洁,内部版偏可理解

几个值得展开的设计亮点:

Doing Tasks 的"不过度设计"。四条规则每条背后都有模型曾犯过的真实错误:加了一个没人要求的特性、为三行相似代码抽了个 helper、在系统边界外加了不必要的 validation。团队选择在 prompt 层面显式禁止这些行为,而不是靠模型"自觉"------这是 prompt 工程的务实态度。

Actions 的"可逆性分级"。Claude Code 对 AI 安全的理解不是限制能力,而是让 AI 在高风险场景下主动慢下来。更有意思的是"授权范围匹配":用户批准一次 git push 不等于在所有上下文都批准,这防止了模型把单次授权泛化为永久权限。

Output Efficiency 的"简洁 vs 可理解"博弈 。原文 What's most important is the reader understanding your output without mental overhead or follow-ups, not how terse you are------外部版偏简洁,内部版偏可理解,因为内部用户更频繁使用,追问成本更高。

一个更深的问题:这些行为约束为什么不写进代码,而是写在 prompt 里? 答案是灵活性和可迭代性。代码约束是刚性的------一旦写死 禁止删除未跟踪文件,在所有场景下都生效。但 prompt 约束是软性的,模型可以根据上下文灵活判断。比如"可逆操作自由执行"------什么是可逆的,需要模型根据场景判断(创建文件可逆,push 到 shared branch 不可逆)。这种判断力用代码实现极其困难,但在 prompt 里只需一句话。

更重要的是,prompt 可以快速迭代。Claude Code 团队发现模型在某个场景下行为不理想时,最快的修复方式往往是在 prompt 里加一条规则------比改代码、发布、等用户更新快得多。7 个静态模块中的很多规则,就是这样在真实用户反馈中逐步积累的。

动态区------11+ 个条件模块

如果说静态区是"所有员工共享的基本守则",那动态区就是"针对每个岗位的个性化指引"。

模块 重要级 核心职责
session_guidance P0 基于工具集生成使用策略(Agent / Explore / Skill / Verification)
memory P0 加载 ~/.claude/memory/*.md,跨会话持久知识
mcp_instructions P0 MCP Server 使用说明
env_info_simple P1 环境信息(CWD / 平台 / Shell / 模型名 / 知识截止日期)
output_style P1 自定义输出风格
frc P1 告知模型旧工具结果可能被清除
summarize_tool_results P1 提醒模型处理工具结果时记录重要信息
language P2 用户语言偏好
scratchpad P2 per-session 临时文件目录指引
token_budget P2 用户指定 token 目标时激活
ant_model_override P2 内部 ant 用户额外指令覆盖

session_guidance 是动态区最复杂的模块。内容取决于"这个会话有哪些工具可用"------AgentTool 使用策略、Explore agent 调用时机、Skill 调用指南。因为涉及具体工具列表,不能放静态区,但 memoized 后会话内只算一次。

mcp_instructions 最为特殊------它是目前唯一可能每 turn 重算的模块(MCP Server 可能随时连接/断开)。但后来引入 mcp_instructions_delta 后,MCP 状态稳定时不注入任何内容,缓存完全命中。这是从"破坏缓存"到"保护缓存"的典型演进。

Section 缓存框架:memoized vs uncached

动态区的每个模块都用统一的缓存框架包装。来自 src/constants/systemPromptSections.ts(仅 69 行):

方法 缓存策略 何时重算 对缓存的影响
systemPromptSection() 计算一次,缓存到 /clear/compact 会话重置时 不影响缓存
DANGEROUS_uncachedSystemPromptSection() 每 turn 重算 每次 API 调用前 可能破坏 prompt cache

绝大多数模块走 systemPromptSection() 路径------通过 session 级闭包缓存结果,整个会话只计算一次。只有 MCP 指令等少数场景走 DANGEROUS_uncached 路径。

缓存策略的演进也有故事:token_budget 曾是 DANGEROUS_uncached(每次 budget 翻转触发重算),后来改为 memoized------因为措辞用了条件句,没有 budget 激活时就是 no-op。这一改节省了约 20K tokens/次的缓存断裂。

整个 Section 缓存框架的核心思想只有一句话:尽量不重算,万不得已才重算。

优先级链:System Prompt 的完整决策树

前面看到的 getSystemPrompt() 返回的是"默认"System Prompt。但实际运行中,它只是决策树的一个叶子节点。外层的 buildEffectiveSystemPrompt() 决定了最终使用哪个 prompt:

分支 行为 频率
Override 直接返回 overrideSystemPrompt,跳过所有默认逻辑 极低,内部测试用
Coordinator 用协调者 prompt 替换默认,用于多 Agent 编排 极低,实验功能
Agent + Proactive Default 追加 Agent prompt,保留基础行为指引
Agent / Custom Agent/Custom 替换 Default,完全接管
Default(标准模式) 使用 7+11 模块 绝大多数用户

两个关键设计点:

  • Proactive 是追加,标准 Agent 是替换:Proactive 模式下 Agent prompt 追加到 Default 之后,因为自主代理仍需要基础行为指引。标准模式下 Agent 完全接管,用自己的指令体系替代默认的。
  • appendSystemPrompt 总是追加(Override 除外),确保额外内容不遗漏。

这对用户意味着什么? 绝大多数用户(Default 模式)看到的 Claude Code 是标准版------专业、简洁、不主动加戏。但如果你配置了自定义 Agent prompt,Claude Code 会完全变成你定义的样子------一个代码审查 Agent、一个测试编写 Agent、一个 DevOps Agent。同一个产品,不同的灵魂。

更有趣的是 Proactive 模式。在这个模式下,Agent prompt 追加到 Default 之后而不是替换------这意味着 Agent 保留了 Claude Code 的基础行为(不乱删文件、不用 Bash 能用 Read 就用 Read),同时叠加了领域特定的指令。就像一个有良好职业素养的员工,在基础守则之上接收了项目组的具体指引。这种"追加而非替换"的设计,是工程务实主义的体现。

缓存切分:能共享就共享,不能共享就降级

System Prompt 的缓存切分由 splitSysPromptPrefix()src/utils/api.ts)处理。它的设计目标很明确:尽可能让静态区命中最高级别的缓存共享,同时避免不同用户的动态内容互相干扰。

SYSTEM_PROMPT_DYNAMIC_BOUNDARY 是一个特殊字符串,不在模型的"人格指令"中------模型看到的是一个被替换后的分隔。它的真正作用是在 splitSysPromptPrefix() 中作为缓存切分锚点:

  1. 边界标记之前的所有元素(静态区)合并为一个 TextBlock,scope 为 global
  2. 边界标记之后的所有元素(动态区)合并为另一个 TextBlock,scope 为 org
  3. 两个 TextBlock 分开发送,API 的 KV cache 就能按不同 scope 缓存

三种场景的设计思路:能共享就共享,不能共享就降级。

场景 1:有 MCP 工具时------全部降级为 org。 不同用户配置的 MCP 工具不同,工具 Schema 会影响 System Prompt 的内容。此时全局缓存不可用,所有内容降级为组织级缓存。

场景 2:无 MCP 工具 + 有 boundary marker------静态区命中 global。 这是最优场景。7 个静态模块对所有用户完全相同,可以跨组织、跨用户共享缓存。

场景 3:第三方 API 提供商------回退到 org。 Bedrock、Vertex 等平台可能不支持全局缓存,统一用组织级。

这三种场景体现了"缓存策略应该是条件性的,而不是一刀切的"工程原则。

用户感知不到缓存的存在------直到它断裂。 缓存断裂的直接后果是:这一轮的响应变慢(因为要重新处理整个前缀),API 费用突增(重新处理意味着更多的 input tokens)。Claude Code 内置了"缓存断裂检测"(promptCacheBreakDetection.ts,651 行),它在每次 API 调用前后对比状态:调用前追踪 12 个维度的变更(system prompt 变了?工具 Schema 变了?模型换了?),调用后检查 cache_read_input_tokens 是否骤降。如果断裂了,系统会自动记录事件并归因到具体原因------"MCP 工具新增了 3 个"或"Beta header 从 5min TTL 切到了 1h TTL"。这个检测系统本身也是一个工程细节:不是出了问题才排查,而是主动监控、自动归因。

SystemContext:追加在 System Prompt 尾部

System Prompt 的主体是模块化组装的指令集,但还有一类信息不适合放在指令里------环境状态。当前 Git 分支、是否有未提交变更、工作目录结构......这些信息每轮都可能变化,但语义上更像是"身份背景"而非"对话内容"。

Claude Code 的做法是把这类信息作为 SystemContext 追加在 System Prompt 尾部。放 System Prompt 而非 Messages,是因为它的语义是"你当前所处的环境",不是用户说的话。

与之对应的是 UserContext,它注入到 Messages[0] 的位置。两者都叫"Context",但设计意图完全不同:SystemContext 是环境状态(身份层面),UserContext 是项目知识(对话层面)------详见姊妹篇[消息管道与隐藏注入](./02-Claude Code深度拆解-上下文里有什么-消息上下文管理.md)。

本章小结

System Prompt 看起来只是一段文本,但背后的工程体系非常精密:

  1. 静态区 7 模块:对所有用户相同,共享 global KV cache------你今天调用的缓存,可能复用了另一个用户 10 分钟前计算的结果
  2. 动态区 11+ 模块:通过 Section 缓存框架管理,会话内只算一次
  3. 优先级链:Override > Coordinator > Agent(proactive追加) > Custom > Default------同一个 Claude Code,在不同场景下展现的是不同面貌
  4. 缓存边界 :静态区和动态区用 DYNAMIC_BOUNDARY 分隔,三层 scope(global/org/ephemeral)按条件降级
  5. SystemContext:环境状态追加在尾部,与 UserContext(注入 Messages)形成互补

没有一个人定义了 Claude Code 的人设。 18 个模块按优先级链拼装,你在不同场景下看到的 Claude Code,其实是同一套骨架在不同模块组合下的不同面貌。


系列导航

本文属于 《Claude Code 源码 Deep Dive》 系列中「上下文组成与缓存」命题的子篇章,专注于 System Prompt 的组装与缓存。

本文是完整版《Claude Code 源码深度解析:拆解上下文的组成与缓存》的子命题之一。如果你想了解上下文编排的全景(System Prompt + Messages + Tools + 缓存工程),推荐阅读完整版。

姊妹篇(可独立阅读):

如果这篇文章对你有帮助,欢迎点赞收藏 支持一下。有任何想法或疑问,欢迎评论区留言讨论 👋

相关推荐
xiami_world2 小时前
Claude Design vs. 博思AIPPT深度对比:从架构、交互、数据处理看垂直AI PPT工具的优势
人工智能·ai·信息可视化·powerpoint·思维导图·ppt
刘佬GEO2 小时前
GEO 效果看什么指标:从提及、引用到推荐的判断框架
前端·网络·人工智能·搜索引擎·ai
张忠琳10 小时前
【vllm】vLLM v1 KV Offload — 模块超深度逐行分析之一(七)
ai·架构·vllm
j_xxx404_11 小时前
大语言模型 (LLM) 零基础入门:核心原理、训练机制与能力全解
人工智能·ai·transformer
汪汪88888811 小时前
测试智能平台落地实践:五大核心能力破解传统测试痛点
ai
张忠琳12 小时前
【vllm】vLLM v1 Attention — 系统级架构深度分析(五)
ai·架构·vllm
薛定谔的猫36913 小时前
探索 MCP 协议:构建跨平台的 Agent 自动化生态
ai·自动化·agent·技术趋势·mcp
DavidSoCool13 小时前
win11系统使用DockerDesktop搭建Dify实现知识库问答
ai·大模型·知识库·dify
进击的编程阿伟14 小时前
Claude code 安装使用教程
claude code