从Claude Code的源码中学到了什么——详细拆解"动态提示词组装"机制
Claude Code 泄露源码:社区学到了什么
云绽科技_渣渣喵,公众号:云开月鸣Claude Code 泄露源码:社区学到了什么
🧩 动态提示词组装详解
一、核心机制:不是”一段提示词”,而是”程序”
“动态提示词”不是单一系统提示,而是数百个提示碎片运行时动态拼装:
-
安全守则就占 ~5,677 tokens(相当于两万字)
-
专门调优给Claude的反应模式
-
这也是换模型就容易跑乱的原因
传统做法是一个写死的长字符串。Claude Code 的做法是:把提示词管理当成软件工程来做。
系统提示 = 数百个提示碎片 + 运行时条件判断 + 动态拼接
关键文件:context.ts(负责实时组装)、QueryEngine(46,000行,核心引擎)
二、安全守则具体是什么样的?
安全守约占 5,677 tokens(约两万字),是提示词中最大的组成部分之一。
1. 身份与安全规则(~100 tokens)
-
明确AI身份
-
授权渗透测试是允许的,但拒绝恶意请求
2. 任务执行规则(~600 tokens,12条独立指令)
|
|
|
|---|---|
| Read before modifying |
|
| Don’t over-engineer |
|
| Don’t create unnecessary files |
|
| No unnecessary error handling |
|
| No premature abstractions |
|
| Security |
|
这些规则像高级工程师的代码审查意见,压缩成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 {id: string; // 唯一标识content: string; // 提示内容condition: ContextCondition; // 触发条件priority: number; // 优先级tokens: number; // token数(用于预算管理)}// 条件判断示例const conditions = {mode: 'plan' | 'coordinator' | 'default',toolsAvailable: string[],userType: 'ant' | 'external', // 内部员工vs外部用户featureFlags: Record<string, boolean>}
2. 组装流程(QueryEngine内)
// 每次调用API前执行:function assemblePrompt(context: Context): string {const fragments: string[] = [];// 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(键值缓存)
// 概念示意图:Transformer的KV Cache机制interface KVCache {// 每个token处理时产生的三个向量keys: Float32Array[]; // Key: "我包含什么信息?"values: Float32Array[]; // Value: "我携带什么信息?"queries: Float32Array[]; // Query: "我在找什么?"}// 缓存的本质:存储已处理token的Key和Value向量// 这样新token只需要计算Query,然后与缓存的Keys匹配
3.2 Claude Code的14个缓存断点
// 基于源码分析的缓存断点设计interface CacheBreakpoint {id: number;name: string;content: string;position: 'prefix' | 'dynamic' | 'suffix';sticky: boolean; // 粘性锁存器}// 14个断点的具体分布const CACHE_BREAKPOINTS: CacheBreakpoint[] = [// ===== 前缀层(全局共享,所有用户共用)====={ id: 1, name: "identity", content: "You are Claude Code...", position: 'prefix', sticky: true },{ id: 2, name: "core_capabilities", content: "You are an interactive CLI tool...", position: 'prefix', sticky: true },{ id: 3, name: "security_rules", content: "Security guidelines...", position: 'prefix', sticky: true },{ id: 4, name: "task_rules", content: "Task execution rules...", position: 'prefix', sticky: true },// ===== 工具定义层(相对静态)====={ id: 5, name: "tool_read", content: "Read tool schema...", position: 'prefix', sticky: true },{ id: 6, name: "tool_edit", content: "Edit tool schema...", position: 'prefix', sticky: true },{ id: 7, name: "tool_bash", content: "Bash tool schema...", position: 'prefix', sticky: true },{ id: 8, name: "tool_grep", content: "Grep tool schema...", position: 'prefix', sticky: true },{ id: 9, name: "other_tools", content: "40+ tool schemas...", position: 'prefix', sticky: true },// ===== 动态内容层(会话相关)====={ id: 10, name: "claude_md", content: "Project CLAUDE.md", position: 'dynamic', sticky: false },{ id: 11, name: "memory_md", content: "MEMORY.md content", position: 'dynamic', sticky: false },{ id: 12, name: "mcp_tools", content: "MCP server tools", position: 'dynamic', sticky: false },// ===== 对话历史层(持续变化)====={ id: 13, name: "conversation", content: "Previous messages", position: 'dynamic', sticky: false },{ id: 14, name: "current_input", content: "User's current message", position: 'suffix', sticky: false },];
3.3 “粘性锁存器”(Sticky Latches)机制
// 核心问题:模式切换会导致缓存失效吗?// 答案:Claude Code用"粘性锁存器"防止这个问题interface StickyLatch {// 即使模式切换,这部分内容保持不变lockedContent: string;// 允许在lockedContent之后动态添加appendOnly: boolean;}// 实际应用示例const SYSTEM_PROMPT_DYNAMIC_BOUNDARY: StickyLatch = {// 核心身份和能力声明(永远不变)lockedContent: `You are Claude Code, Anthropic's official CLI for coding.You are an interactive CLI tool...[安全守则 5,677 tokens][工具定义 ~15,000 tokens]`,// 以下内容可以追加,但不能破坏lockedContentappendOnly: true};// 模式切换时(如进入Plan Mode)function switchMode(newMode: 'plan' | 'default' | 'coordinator'): void {// 不替换整个提示词,而是在lockedContent后追加模式指令const modeInstruction = getModeInstruction(newMode);// 缓存前缀(lockedContent)保持warm// 只有模式指令和对话是新tokenreturn `${SYSTEM_PROMPT_DYNAMIC_BOUNDARY.lockedContent}${modeInstruction}[对话历史]`;}
3.4 缓存的经济效益计算
// 成本计算(基于Anthropic定价)const PRICING = {input: 3.00, // $/million tokenscacheWrite: 3.75, // $/million tokens (1.25x)cacheRead: 0.30, // $/million tokens (0.1x)};// 典型会话场景(30K tokens系统提示 + 对话)function calculateCost(turns: number): void {const systemTokens = 27000; // 系统提示 + 工具定义const conversationTokens = 3000; // 每轮对话// 无缓存:每轮都重新处理全部tokenconst 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
六、为什么这套系统”换个模型就跑乱”?
因为:
-
提示碎片专门为Claude调教 —— 措辞、语气、格式都是针对Claude的反应模式优化
-
工具定义的XML格式 —— 假设模型能正确解析特定schema
-
隐含的推理假设 —— 比如”读到这条规则后,模型会主动检查XX”
-
token预算的精细管理 —— 基于Claude的上下文窗口特性设计
这就好比把F1赛车的调校参数直接用在越野车上——零件看起来一样,但运行环境完全不同。
针对提示碎片的内容我们再进一步展开:
6.1 提示碎片的数据结构
根据泄露的源码,提示碎片不是简单的字符串,而是结构化的配置对象:
TypeScript:
// 简化版数据结构(基于源码分析)interface PromptFragment {id: string; // 唯一标识,如 "tool-usage-bash"content: string; // 提示内容condition?: ContextCondition; // 触发条件priority: number; // 优先级(0-100)estimatedTokens: number; // 预估token数cacheable: boolean; // 是否可缓存category: 'identity' | 'rules' | 'tools' | 'memory' | 'context';}// 上下文条件判断interface ContextCondition {mode?: 'plan' | 'coordinator' | 'default';tools?: string[]; // 需要启用的工具userType?: 'ant' | 'external'; // 内部员工 vs 外部用户featureFlags?: Record<string, boolean>;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 requestedor 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 overcreating new ones. Use the most appropriate file type for the task at hand.4. **No unnecessary error handling**: Don't add error handling for scenariosthat can't happen, and don't add try/catch blocks unless the code pathcan actually fail in expected operation.5. **Security vigilance**: Be careful not to introduce security vulnerabilitiessuch 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_REGISTRY: Map<string, PromptFragment> = new Map();// 注册函数function registerPromptFragment(fragment: PromptFragment): void {PROMPT_FRAGMENTS_REGISTRY.set(fragment.id, fragment);}// 批量注册BUILT_IN_FRAGMENTS.forEach(registerPromptFragment);// 用户自定义碎片(通过 CLAUDE.md 注入)function registerUserFragment(content: string, priority: number = 100): void {registerPromptFragment({id: `user-${Date.now()}`,content,priority, // 用户碎片最高优先级cacheable: false, // 动态内容不缓存category: 'context'});}
七、给开发者的启示
如果想构建类似的动态提示系统:
|
|
|
|---|---|
| 模块化 |
|
| 条件渲染 |
|
| 优先级分层 |
|
| Token预算管理 |
|
| 缓存优化 |
|
| 可观测性 |
|
学习笔记直达:
上一篇: Claude Code 泄露源码:社区学到了什么
云绽科技_渣渣喵,公众号:云开月鸣Claude Code 泄露源码:社区学到了什么
夜雨聆风