乐于分享
好东西不私藏

从Claude Code的源码中学到了什么——详细拆解"动态提示词组装"机制

从Claude Code的源码中学到了什么——详细拆解"动态提示词组装"机制

昨天我们讨论了从源码看Claude Code的整个架构设计、技术栈的选择, 以及在动态提示词组装工程、记忆系统(最值得称赞的系统)、multi-agent、安全相关、工程哲学相关启示和一些隐藏功能(彩蛋), 链接如下:

Claude Code 泄露源码:社区学到了什么

云绽科技_渣渣喵,公众号:云开月鸣Claude Code 泄露源码:社区学到了什么
今天我们仔细来扒一扒这个提示此组装工程的细节~ 希望对你的日常使用有帮助哦~

🧩 动态提示词组装详解

一、核心机制:不是”一段提示词”,而是”程序”

“动态提示词”不是单一系统提示,而是数百个提示碎片运行时动态拼装:

  • 安全守则就占 ~5,677 tokens(相当于两万字)

  • 专门调优给Claude的反应模式

  • 这也是换模型就容易跑乱的原因

传统做法是一个写死的长字符串。Claude Code 的做法是:把提示词管理当成软件工程来做

系统提示 = 数百个提示碎片 + 运行时条件判断 + 动态拼接

关键文件context.ts(负责实时组装)、QueryEngine(46,000行,核心引擎)


二、安全守则具体是什么样的?

安全守约占 5,677 tokens(约两万字),是提示词中最大的组成部分之一。

1. 身份与安全规则(~100 tokens)

“You are Claude Code, Anthropic’s official CLI for Claude.”
  • 明确AI身份

  • 授权渗透测试是允许的,但拒绝恶意请求

2. 任务执行规则(~600 tokens,12条独立指令)

规则
内容
Read before modifying
“NEVER propose changes to code you haven’t read”
Don’t over-engineer
“Only make changes that are directly requested”
Don’t create unnecessary files
优先编辑现有文件而非创建新文件
No unnecessary error handling
“Don’t add error handling for scenarios that can’t happen”
No premature abstractions
不要为一次性操作创建抽象
Security
“Be careful not to introduce security vulnerabilities”

这些规则像高级工程师的代码审查意见,压缩成AI必须遵守的指令。

3. 动作执行规则(~540 tokens)

“The cost of pausing to confirm is low, while the cost of an unwanted action 

(lost work, unintended messages sent, deleted branches) can be very high.”

  • 定义每个动作的”可逆性”和”爆炸半径”

  • 读取文件 = 安全可逆

  • git push = 不可逆,需要确认

4. 工具使用政策(~550 tokens)

“Use Read instead of `cat`. Use Edit instead of `sed`. Use Grep instead of `grep`.”

  • 为什么? 可观测性。原生工具调用可以被记录、测量延迟、显示在UI中

  • shell命令是”黑盒”,工具调用是”白盒”


三、动态组装的”六层拼装”

根据泄露代码分析,每次API调用前,系统会按以下层次组装提示词:

(Level1) 基础身份与能力声明(固定)                                                       “You are Claude Code…”  

(Level2) 当前模式特定的提示碎片                                                           – Plan Mode(只读规划

– Coordinator Mode(并行代理协调)

– 普通交互模式   

(Level3) 可用工具的定义(根据权限动态筛选)       

          – 用户允许的工具 → 完整定义  

          – 用户禁止的工具 → 不包含    

(Level4) 上下文信息

          – 当前对话历史

          – MEMORY.md 内容(每次必注入)

          – 文件上下文

(Level5) CLAUDE.md(用户自定义规则,最高优先级)

(Level6) 系统提醒(System Reminders)

          – 自动添加的执行反馈

四、在代码中如何组装?

1. 提示碎片的数据结构

每个提示片段是一个独立的模块,有自己的条件判断:

TypeScript:

// 伪代码示意interface PromptFragment {  idstring;           // 唯一标识  contentstring;      // 提示内容  conditionContextCondition;  // 触发条件  prioritynumber;     // 优先级  tokensnumber;       // token数(用于预算管理)}// 条件判断示例const conditions = {  mode'plan' | 'coordinator' | 'default',  toolsAvailablestring[],  userType'ant' | 'external',  // 内部员工vs外部用户  featureFlagsRecord<stringboolean>}

2. 组装流程(QueryEngine内)

TypeScript:
// 每次调用API前执行:function assemblePrompt(contextContext): string {  const fragmentsstring[] = [];  // 1. 筛选符合条件的提示碎片  const activeFragments = allFragments.filter(f =>     evaluateCondition(f.condition, context)  );  // 2. 按优先级排序  activeFragments.sort((a, b) => b.priority - a.priority);  // 3. 构建token预算(防止超窗)  let tokenBudget = MAX_CONTEXT_TOKENS;  for (const fragment of activeFragments) {    if (fragment.tokens <= tokenBudget) {      fragments.push(fragment.content);      tokenBudget -= fragment.tokens;    } else {      // 超出预算,用指针替代完整内容      fragments.push(createPointer(fragment));    }  }  // 4. 注入CLAUDE.md(最高优先级,用户自定义覆盖)  fragments.push(context.claudeMdContent);  // 5. 最终拼接  return fragments.join('\n\n');}

3. 缓存优化(QueryEngine内)

提示缓存架构有 14个缓存断点,用”粘性锁存器”管理:

  • 模式切换不会让缓存全部失效

  • 只有相关片段变化时才重新计算

  • 为什么重要? 每次缓存失效都要花真钱(API调用成本)

缓存断点的技术原理:

3.1 核心原理:KV Cache(键值缓存)

TypeScript:
// 概念示意图:Transformer的KV Cache机制interface KVCache {  // 每个token处理时产生的三个向量  keysFloat32Array[];    // Key: "我包含什么信息?"  valuesFloat32Array[];  // Value: "我携带什么信息?"  queriesFloat32Array[]; // Query: "我在找什么?"}// 缓存的本质:存储已处理token的Key和Value向量// 这样新token只需要计算Query,然后与缓存的Keys匹配

3.2 Claude Code的14个缓存断点

TypeScript:
// 基于源码分析的缓存断点设计interface CacheBreakpoint {  idnumber;  namestring;  contentstring;  position'prefix' | 'dynamic' | 'suffix';  stickyboolean;  // 粘性锁存器}// 14个断点的具体分布const CACHE_BREAKPOINTSCacheBreakpoint[] = [  // ===== 前缀层(全局共享,所有用户共用)=====  { id1name"identity"content"You are Claude Code..."position'prefix'stickytrue },  { id2name"core_capabilities"content"You are an interactive CLI tool..."position'prefix'stickytrue },  { id3name"security_rules"content"Security guidelines..."position'prefix'stickytrue },  { id4name"task_rules"content"Task execution rules..."position'prefix'stickytrue },  // ===== 工具定义层(相对静态)=====  { id5name"tool_read"content"Read tool schema..."position'prefix'stickytrue },  { id6name"tool_edit"content"Edit tool schema..."position'prefix'stickytrue },  { id7name"tool_bash"content"Bash tool schema..."position'prefix'stickytrue },  { id8name"tool_grep"content"Grep tool schema..."position'prefix'stickytrue },  { id9name"other_tools"content"40+ tool schemas..."position'prefix'stickytrue },  // ===== 动态内容层(会话相关)=====  { id10name"claude_md"content"Project CLAUDE.md"position'dynamic'stickyfalse },  { id11name"memory_md"content"MEMORY.md content"position'dynamic'stickyfalse },  { id12name"mcp_tools"content"MCP server tools"position'dynamic'stickyfalse },  // ===== 对话历史层(持续变化)=====  { id13name"conversation"content"Previous messages"position'dynamic'stickyfalse },  { id14name"current_input"content"User's current message"position'suffix'stickyfalse },];

3.3 “粘性锁存器”(Sticky Latches)机制

TypeScript:
// 核心问题:模式切换会导致缓存失效吗?// 答案:Claude Code用"粘性锁存器"防止这个问题interface StickyLatch {  // 即使模式切换,这部分内容保持不变  lockedContentstring;  // 允许在lockedContent之后动态添加  appendOnlyboolean;}// 实际应用示例const SYSTEM_PROMPT_DYNAMIC_BOUNDARYStickyLatch = {  // 核心身份和能力声明(永远不变)  lockedContent`    You are Claude Code, Anthropic's official CLI for coding.    You are an interactive CLI tool...    [安全守则 5,677 tokens]    [工具定义 ~15,000 tokens]  `,  // 以下内容可以追加,但不能破坏lockedContent  appendOnlytrue};// 模式切换时(如进入Plan Mode)function switchMode(newMode'plan' | 'default' | 'coordinator'): void {  // 不替换整个提示词,而是在lockedContent后追加模式指令  const modeInstruction = getModeInstruction(newMode);  // 缓存前缀(lockedContent)保持warm  // 只有模式指令和对话是新token  return `${SYSTEM_PROMPT_DYNAMIC_BOUNDARY.lockedContent}  ${modeInstruction}  [对话历史]`;}

3.4 缓存的经济效益计算

TypeScript:
// 成本计算(基于Anthropic定价)const PRICING = {  input3.00,           // $/million tokens  cacheWrite3.75,      // $/million tokens (1.25x)  cacheRead0.30,       // $/million tokens (0.1x)};// 典型会话场景(30K tokens系统提示 + 对话)function calculateCost(turns: number): void {  const systemTokens = 27000;  // 系统提示 + 工具定义  const conversationTokens = 3000;  // 每轮对话  // 无缓存:每轮都重新处理全部token  const noCacheCost = turns * (systemTokens + conversationTokens) * PRICING.input / 1e6;  // 有缓存:只付一次写入费,后续读缓存  const cacheWriteCost = systemTokens * PRICING.cacheWrite / 1e6;  const cacheReadCost = (turns - 1) * systemTokens * PRICING.cacheRead / 1e6;  const dynamicCost = turns * conversationTokens * PRICING.input / 1e6;  const withCacheCost = cacheWriteCost + cacheReadCost + dynamicCost;  // 10轮对话示例  // 无缓存: 10 * 30000 * 3 / 1e6 = $0.90  // 有缓存: (27000 * 3.75 + 9 * 27000 * 0.3 + 10 * 3000 * 3) / 1e6 = $0.24  // 节省: 73%}

五、一次用户请求的流程示例

用户输入:”帮我重构这个函数”

系统判断:当前是 Coordinator Mode

→ 注入 Coordinator Mode 的提示碎片

检查可用工具:

→ User allowed: Read, Edit, Bash

→ User denied: Agent, WebFetch

→ 只注入允许工具的完整定义

拉取上下文:

→ MEMORY.md(必注入,约150字符/行的索引)

→ 当前文件的最近修改历史

→ 相关topic文件(按需)

附加 CLAUDE.md:

→ 用户的项目特定规则(最高优先级)

组装完成 → 发送给 Claude API

六、为什么这套系统”换个模型就跑乱”?

因为:

  1. 提示碎片专门为Claude调教 —— 措辞、语气、格式都是针对Claude的反应模式优化

  2. 工具定义的XML格式 —— 假设模型能正确解析特定schema

  3. 隐含的推理假设 —— 比如”读到这条规则后,模型会主动检查XX”

  4. token预算的精细管理 —— 基于Claude的上下文窗口特性设计

这就好比把F1赛车的调校参数直接用在越野车上——零件看起来一样,但运行环境完全不同。

针对提示碎片的内容我们再进一步展开:

6.1 提示碎片的数据结构

根据泄露的源码,提示碎片不是简单的字符串,而是结构化的配置对象

TypeScript:

// 简化版数据结构(基于源码分析)interface PromptFragment {  idstring;                    // 唯一标识,如 "tool-usage-bash"  contentstring;               // 提示内容  condition?: ContextCondition;  // 触发条件  prioritynumber;              // 优先级(0-100)  estimatedTokensnumber;       // 预估token数  cacheableboolean;           // 是否可缓存  category'identity' | 'rules' | 'tools' | 'memory' | 'context';}// 上下文条件判断interface ContextCondition {  mode?: 'plan' | 'coordinator' | 'default';  tools?: string[];              // 需要启用的工具  userType?: 'ant' | 'external'// 内部员工 vs 外部用户  featureFlags?: Record<stringboolean>;  projectConfig?: boolean;       // 是否有项目级配置}

6.2 真实的提示碎片示例

从泄露代码中可以看到具体的提示碎片:

TypeScript:

// 示例1:工具使用 - Bash(75 tokens)const TOOL_USAGE_BASH: PromptFragment = {  id"tool-usage-bash",  content: `Reserve the Bash tool exclusively for system commands and terminal operations.Examples of appropriate Bash usage:- Package management: npm install, pip install, cargo add- Build commands: make, npm run build, cargo build- Git operations: git status, git log --oneline -20- System queries: ps aux, df -h, curl -I https://api.example.comExamples that should use other tools:- Reading files → use Read tool instead of cat- Editing files → use Edit tool instead of sed/awk- Searching content → use Grep tool instead of grep/rg- File discovery → use Glob tool instead of find/ls`,  condition: { tools: ['Bash'] },  priority: 70,  estimatedTokens: 75,  cacheable: true,  category: 'tools'};// 示例2:安全守则 - 文件操作(~600 tokens)const FILE_OPERATION_RULES: PromptFragment = {  id"file-operation-rules",  content: `## Task Execution Rules1. **Read before modifying**: NEVER propose changes to code you haven't read.    Read the relevant code first, understand its context and dependencies.2. **Don't over-engineer**: Only make changes that are directly requested    or are obviously necessary (e.g., syntax errors, type mismatches).    Do not refactor unrelated code or add unnecessary abstractions.3. **Don't create unnecessary files**: Prefer editing existing files over    creating new ones. Use the most appropriate file type for the task at hand.4. **No unnecessary error handling**: Don't add error handling for scenarios    that can't happen, and don't add try/catch blocks unless the code path    can actually fail in expected operation.5. **Security vigilance**: Be careful not to introduce security vulnerabilities    such as SQL injection, XSS, or path traversal.`,  condition: { mode: 'default' },  priority: 90,  // 高优先级,用户规则之前  estimatedTokens: 540,  cacheable: true,  category: 'rules'};// 示例3:Plan Mode 专用提示(~200 tokens)const PLAN_MODE_INSTRUCTIONS: PromptFragment = {  id"plan-mode-instructions",  content: `You are in Plan Mode. Your task is to explore the codebase, understand its structure, and propose a detailed implementation plan.**Constraints:**- Use ONLY read-only tools (Read, Glob, Grep)- Do NOT write files, edit code, or execute commands- Present a clear, step-by-step plan before any modifications**Output format:**1. Current state analysis2. Proposed changes with file paths3. Potential risks and mitigations4. Testing strategy`,  condition: { mode: 'plan' },  priority: 85,  estimatedTokens: 150,  cacheable: true,  category: 'rules'};

1.3 提示碎片的存储方式

TypeScript:

// 源码中发现的提示碎片注册方式// 所有碎片存储在统一注册表中const PROMPT_FRAGMENTS_REGISTRYMap<stringPromptFragment> = new Map();// 注册函数function registerPromptFragment(fragmentPromptFragment): void {  PROMPT_FRAGMENTS_REGISTRY.set(fragment.id, fragment);}// 批量注册BUILT_IN_FRAGMENTS.forEach(registerPromptFragment);// 用户自定义碎片(通过 CLAUDE.md 注入)function registerUserFragment(contentstringprioritynumber = 100): void {  registerPromptFragment({    id`user-${Date.now()}`,    content,    priority,  // 用户碎片最高优先级    cacheablefalse,  // 动态内容不缓存    category'context'  });}

七、给开发者的启示

如果想构建类似的动态提示系统:

原则
实践
模块化
把提示词拆成独立、可复用的片段
条件渲染
根据上下文动态决定注入哪些片段
优先级分层
用户自定义 > 模式特定 > 基础能力
Token预算管理
实时监控,超预算时用指针替代
缓存优化
标记不变的部分,避免重复计算
可观测性
记录最终组装结果,便于调试

学习笔记直达: 

上一篇: Claude Code 泄露源码:社区学到了什么

云绽科技_渣渣喵,公众号:云开月鸣Claude Code 泄露源码:社区学到了什么