提示词
Layer 1: Prompt Assembly Architecture
Claude Code 源码深度拆解 -- prompts.ts 编排器及其卫星模块的完整逆向工程分析
- 状态
- 已收录
- 语言
- 中文
- 来源
- Projects/claude-code-deep-dive/layer1-prompt-assembly.html
- 重复副本
- 0
提取结果
提示词片段
headlessProfilerCheckpoint('before_getSystemPrompt')headlessProfilerCheckpoint('after_getSystemPrompt')buildSystemPromptBlocks()
enhanceSystemPromptWithEnvDetails()
export async function getSystemPrompt( tools: Tools, // 当前会话启用的全部工具列表 model: string, // 模型标识符,如 'claude-opus-4-6' additionalWorkingDirectories?: string[], // 额外工作目录(worktree 等) mcpClients?: MCPServerConnection[], // MCP 服务器连接状态 ): Promise<string[]>Copy
return [ `\nYou are an autonomous agent. Use the available tools to do useful work.\n\n${CYBER_RISK_INSTRUCTION}`, getSystemRemindersSection(), await loadMemoryPrompt(), envInfo, getLanguageSection(settings.language), isMcpInstructionsDeltaEnabled() ? null : getMcpInstructionsSection(mcpClients), getScratchpadInstructions(), getFunctionResultClearingSection(model), SUMMARIZE_TOOL_RESULTS_SECTION, getProactiveSection(), ].filter(s => s !== null)Copyconst dynamicSections = [ systemPromptSection('session_guidance', () => ...), // 缓存 systemPromptSection('memory', () => ...), // 缓存 systemPromptSection('ant_model_override', () => ...), // 缓存 systemPromptSection('env_info_simple', () => ...), // 缓存 systemPromptSection('language', () => ...), // 缓存 systemPromptSection('output_style', () => ...), // 缓存 DANGEROUS_uncachedSystemPromptSection( // 不缓存! 'mcp_instructions', () => ..., 'MCP servers connect/disconnect between turns' ), systemPromptSection('scratchpad', () => ...), // 缓存 systemPromptSection('frc', () => ...), // 缓存 systemPromptSection('summarize_tool_results', ...), // 缓存 // + 条件追加: numeric_length_anchors, token_budget, brief ]Copyconst resolvedDynamicSections = await resolveSystemPromptSections(dynamicSections) return [ // --- 静态内容(可缓存) --- getSimpleIntroSection(outputStyleConfig), // 1. 身份定义 getSimpleSystemSection(), // 2. 系统行为规则 outputStyleConfig === null || keepCoding // 3. 做任务指南(条件) ? getSimpleDoingTasksSection() : null, getActionsSection(), // 4. 执行操作准则 getUsingYourToolsSection(enabledTools), // 5. 工具使用指南 getSimpleToneAndStyleSection(), // 6. 语气与格式 getOutputEfficiencySection(), // 7. 输出效率 // === 边界标记 === ...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []), // --- 动态内容(注册管理) --- ...resolvedDynamicSections, ].filter(s => s !== null)Copy
正文
清洗后的原始内容
Layer 1: Prompt Assembly Architecture
Claude Code 源码深度拆解 -- prompts.ts 编排器及其卫星模块的完整逆向工程分析
1架构概述
1.1 prompts.ts 的角色定位:编排器而非模板
prompts.ts(914 行)是 Claude Code 系统 prompt 的核心编排器。它不是一个静态模板文件——不存在一个预写好的长文本在运行时被填入变量。相反,它是一个运行时决策引擎,根据当前会话的上下文(模型类型、工具集合、feature flags、用户配置、MCP 服务器状态等)动态组装出一个 string[] 数组,每个元素代表系统 prompt 的一个语义分节。
这种设计的核心优势在于:
- Prompt Cache 友好:静态分节与动态分节通过边界标记分离,静态部分可以跨组织级别缓存(
cacheScope: 'global'),大幅降低 API 成本 - 条件组装:每个分节函数独立决定是否返回内容(
null表示跳过),避免了"if-else 嵌套地狱" - 分节级 Memoization:通过
systemPromptSections.ts的缓存层,避免跨 turn 重复计算 - 三路运行模式:同一入口函数
getSystemPrompt()支持 SIMPLE、PROACTIVE、标准三种完全不同的 prompt 拓扑
1.2 调用关系:谁调用 getSystemPrompt(),返回值如何被消费
QueryEngine.ts:284 是主入口调用点。它在 headlessProfilerCheckpoint('before_getSystemPrompt') 和 headlessProfilerCheckpoint('after_getSystemPrompt') 之间调用此函数,获取到 string[] 后与 getUserContext() 和 getSystemContext() 的结果合并,最终传递给 buildSystemPromptBlocks()。
SessionMemory.ts:413 是第二个调用点,用于会话恢复场景。它使用三路并行 Promise.all 同时获取系统 prompt、用户上下文和系统上下文。
另一个重要的卫星函数是 enhanceSystemPromptWithEnvDetails()(行 760-791),用于子 agent(subagent)场景。它不重新调用 getSystemPrompt(),而是在已有的 prompt 基础上追加环境信息和技能发现引导。
2getSystemPrompt() 完整拆解
2.1 函数签名与参数
export async function getSystemPrompt(
tools: Tools, // 当前会话启用的全部工具列表
model: string, // 模型标识符,如 'claude-opus-4-6'
additionalWorkingDirectories?: string[], // 额外工作目录(worktree 等)
mcpClients?: MCPServerConnection[], // MCP 服务器连接状态
): Promise<string[]>Copy
返回类型是 Promise<string[]>,不是单个字符串。每个数组元素代表一个独立的语义分节,在 API 层面被拼接为 \n\n 分隔的文本块,或者被拆分到不同的 TextBlockParam 中以获得不同的缓存策略。
2.2 SIMPLE 模式(极简路径)
当环境变量 CLAUDE_CODE_SIMPLE=1 时,函数直接返回一个单元素数组,跳过所有复杂逻辑:
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
return [
`You are Claude Code, Anthropic's official CLI for Claude.\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`,
]
}Copy
这个模式用于测试和极端轻量场景。注意 getSessionStartDate() 是 memoize(getLocalISODate),在会话启动时冻结日期值,避免跨午夜的 prompt cache 失效。
2.3 PROACTIVE 模式(自主代理路径)
当 feature('PROACTIVE') || feature('KAIROS') 且 proactiveModule.isProactiveActive() 为真时,进入自主代理路径。此路径完全跳过标准模式的分节框架,直接构建一个面向自主工作的精简 prompt:
return [
`\nYou are an autonomous agent. Use the available tools to do useful work.\n\n${CYBER_RISK_INSTRUCTION}`,
getSystemRemindersSection(),
await loadMemoryPrompt(),
envInfo,
getLanguageSection(settings.language),
isMcpInstructionsDeltaEnabled() ? null : getMcpInstructionsSection(mcpClients),
getScratchpadInstructions(),
getFunctionResultClearingSection(model),
SUMMARIZE_TOOL_RESULTS_SECTION,
getProactiveSection(),
].filter(s => s !== null)Copy
关键差异:没有 "Doing tasks" 部分、没有 "Actions" 部分、没有缓存边界标记,且身份定义从 "CLI tool" 变为 "autonomous agent"。自主代理有自己独特的指令(详见 getProactiveSection()),包括 tick 处理、睡眠控制、终端焦点感知等。
2.4 标准模式:完整组装流程
标准模式是最复杂也最核心的路径,分为三个阶段:
阶段 1:三路并行数据预获取
const [skillToolCommands, outputStyleConfig, envInfo] = await Promise.all([
getSkillToolCommands(cwd), // 扫描可用的 Skill 命令
getOutputStyleConfig(), // 解析输出样式配置
computeSimpleEnvInfo(model, dirs), // 计算环境信息
])Copy
这三个异步操作完全独立,可并行执行。getSkillToolCommands 扫描 .claude/commands/ 目录;getOutputStyleConfig 读取样式配置并处理插件强制样式;computeSimpleEnvInfo 获取 git 状态、操作系统信息和模型元数据。
阶段 2:动态分节数组构建
构建一个 SystemPromptSection[] 数组,每个元素包含名称、计算函数和缓存策略。在此阶段不执行任何计算——只是注册:
const dynamicSections = [
systemPromptSection('session_guidance', () => ...), // 缓存
systemPromptSection('memory', () => ...), // 缓存
systemPromptSection('ant_model_override', () => ...), // 缓存
systemPromptSection('env_info_simple', () => ...), // 缓存
systemPromptSection('language', () => ...), // 缓存
systemPromptSection('output_style', () => ...), // 缓存
DANGEROUS_uncachedSystemPromptSection( // 不缓存!
'mcp_instructions', () => ...,
'MCP servers connect/disconnect between turns'
),
systemPromptSection('scratchpad', () => ...), // 缓存
systemPromptSection('frc', () => ...), // 缓存
systemPromptSection('summarize_tool_results', ...), // 缓存
// + 条件追加: numeric_length_anchors, token_budget, brief
]Copy
阶段 3:解析 + 组装最终数组
const resolvedDynamicSections = await resolveSystemPromptSections(dynamicSections)
return [
// --- 静态内容(可缓存) ---
getSimpleIntroSection(outputStyleConfig), // 1. 身份定义
getSimpleSystemSection(), // 2. 系统行为规则
outputStyleConfig === null || keepCoding // 3. 做任务指南(条件)
? getSimpleDoingTasksSection() : null,
getActionsSection(), // 4. 执行操作准则
getUsingYourToolsSection(enabledTools), // 5. 工具使用指南
getSimpleToneAndStyleSection(), // 6. 语气与格式
getOutputEfficiencySection(), // 7. 输出效率
// === 边界标记 ===
...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []),
// --- 动态内容(注册管理) ---
...resolvedDynamicSections,
].filter(s => s !== null)Copy
3全部分节构建函数逐个拆解
3.1 身份与系统基础
getSimpleIntroSection(outputStyleConfig) -- 行 175-184
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #1 |
| 返回类型 | string(始终返回) |
| 缓存标记 | Static |
这是整个系统 prompt 的开篇。根据 outputStyleConfig 是否为 null 决定身份描述:
- 有自定义输出样式:
"helps users according to your 'Output Style' below" - 无自定义样式:
"helps users with software engineering tasks"
同时注入 CYBER_RISK_INSTRUCTION(网络安全行为边界,由 Safeguards 团队维护,不可随意修改)和 URL 生成禁令。
getSimpleSystemSection() -- 行 186-197
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #2 |
| 返回类型 | string(始终返回) |
| 标题 | # System |
包含 6 条系统级行为规则:
- 输出文本使用 GitHub-flavored Markdown(CommonMark 渲染)
- 工具权限模式说明——用户拒绝工具时不要重试相同调用
<system-reminder>标签说明——与工具结果/用户消息无关的系统注入信息- 外部来源注入检测——疑似 prompt injection 时向用户报警
- Hooks 机制说明(调用
getHooksSection()) - 自动压缩说明——对话不受上下文窗口限制
getSimpleDoingTasksSection() -- 行 199-253
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #3(条件包含) |
| 条件 | outputStyleConfig === null || keepCodingInstructions === true |
| 标题 | # Doing tasks |
| ant 差异 | 4 条额外子项(注释风格、完成验证、bug 报告、false-claims 缓解) |
这是最长的分节之一,包含软件工程任务的核心行为指南。关键内容包括:
- 不要自作主张添加功能、重构或"改进"
- 不要为不可能发生的场景添加错误处理
- 不要为一次性操作创建帮助函数/工具类
- 修改前先读代码,尽量编辑而非新建文件
- 注意安全漏洞(OWASP Top 10)
codeStyleSubitems中有 4 条 ant-only 规则(注释极简主义、完成前验证、如实报告结果、false-claims 缓解)
标记为 @[MODEL LAUNCH]: Update comment writing for Capybara,是针对 Capybara 模型过度注释倾向的对冲。包括"默认不写注释"、"不要解释代码做什么"、"不要删除已有注释除非删除对应代码"三条。这些规则仅在 USER_TYPE === 'ant' 时注入。
3.2 操作与工具
getActionsSection() -- 行 255-267
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #4 |
| 标题 | # Executing actions with care |
| 分支 | 无条件分支 |
这是一个篇幅较长的纯文本分节,定义了 Claude Code 对"高风险操作"的行为框架。核心思想是"可逆性与影响范围评估":
- 自由执行:本地可逆操作(编辑文件、运行测试)
- 需要确认:不可逆操作(删除文件/分支、force push、修改 CI/CD)、可见于他人的操作(push、创建 PR/issue、发送消息)、上传到第三方工具
- 障碍处理原则:不要用破坏性操作走捷径(如
--no-verify),调查根因而非绕过检查
getUsingYourToolsSection(enabledTools) -- 行 269-314
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #5 |
| 标题 | # Using your tools |
| 输入 | enabledTools: Set<string> |
| 依赖 | hasEmbeddedSearchTools()、isReplModeEnabled() |
根据运行模式有两个完全不同的分支:
REPL 模式(行 277-285):Read/Write/Edit/Glob/Grep/Bash/Agent 被隐藏,只保留 TaskTool 相关引导。
标准模式(行 289-313):核心是"专用工具优先于 Bash"的强制规则——用 FileReadTool 而非 cat,用 FileEditTool 而非 sed,用 GlobTool 而非 find(除非 ant 内置搜索工具启用时跳过 Glob/Grep 引导)。还包括并行工具调用指南和 TaskTool 使用引导。
getAgentToolSection() -- 行 316-320
根据 isForkSubagentEnabled() 返回不同的 AgentTool 使用说明:
- Fork 模式:Agent 在后台运行,保持工具输出脱离主上下文。如果自己就是 fork,直接执行不要再委托。
- 标准模式:用于并行化独立查询或保护主上下文窗口,但不要过度使用,不要重复 subagent 已在做的工作。
getDiscoverSkillsGuidance() -- 行 333-341
仅在 feature('EXPERIMENTAL_SKILL_SEARCH') 开启时返回。告知模型技能会自动浮现("Skills relevant to your task:" 提醒),但在中途转向、不寻常工作流或多步计划时应主动调用 DiscoverSkillsTool。
3.3 语气与效率
getSimpleToneAndStyleSection() -- 行 430-442
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #6 |
| 标题 | # Tone and style |
| ant 差异 | ant 时不包含"responses should be short and concise" |
5 条格式规则:不用 emoji(除非用户明确要求)、引用代码时用 file_path:line_number、GitHub issue 用 owner/repo#123 格式、工具调用前不用冒号。ant 用户跳过"简短回复"规则(因为 ant 版有自己更详细的输出效率指令)。
getOutputEfficiencySection() -- 行 402-428
| 属性 | 值 |
|---|---|
| 位置 | 静态前缀 #7 |
| ant 差异 | 完全不同的内容 |
这个分节在 ant 和非 ant 之间有完全不同的内容:
ant 版(标题:# Communicating with the user):长篇叙事式指导,强调"为人写作而非记录日志"。核心规则包括:工具调用前简要说明意图、关键时刻给简短更新、假设用户离开后失去了线索、用流畅散文而非片段、倒金字塔结构、不要过分强调琐碎的过程。标记为 @[MODEL LAUNCH]: Remove this section when we launch numbat。
外部版(标题:# Output efficiency):精简指令——开门见山、先行动后解释、聚焦于需要用户输入的决策和关键里程碑更新。
3.4 SessionSpecificGuidanceSection -- 条件最密集的分节
getSessionSpecificGuidanceSection(enabledTools, skillToolCommands) -- 行 352-400
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #1 |
| 缓存 | systemPromptSection |
| 标题 | # Session-specific guidance |
| 依赖 | enabledTools, skillToolCommands, 多个 feature flags |
注释明确说明(行 343-351):此分节的每一个条件判断都是运行时 bit,如果放在 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 之前,会导致 Blake2b 前缀哈希出现 2^N 个变体,彻底摧毁全局缓存命中率。这正是 PR #24490 和 #24171 中出现的 bug 类型。
此分节最多包含以下条目(全部条件判断):
AskUserQuestionTool存在时:提示用户被拒绝的工具调用可以用此工具追问原因- 非 non-interactive 会话时:告知用户可以用
! <command>前缀在 prompt 中运行 shell 命令 AgentTool存在时:注入getAgentToolSection()内容(fork 或标准模式)ExploreAgent启用 + 非 fork 模式:搜索工具直接 vs Agent 探索的使用分界SkillTool存在且有技能时:/skill-name快捷方式说明DiscoverSkillsTool存在时:技能自动浮现引导VERIFICATION_AGENTfeature +tengu_hive_evidenceGrowthBook flag:验证代理规则——非平凡实现必须经过独立对抗验证
当所有条件都为 false 时,函数返回 null,整个分节被跳过。
3.5 环境信息
computeSimpleEnvInfo(modelId, additionalWorkingDirectories) -- 行 651-710
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #4 |
| 缓存 | systemPromptSection |
| 标题 | # Environment |
使用 Promise.all([getIsGit(), getUnameSR()]) 并行获取 git 状态和操作系统信息,然后组装 <env> 标签块。包含的信息:
- 主工作目录和 worktree 标识
- 是否为 git 仓库
- 额外工作目录列表
- 平台、Shell、OS 版本
- 模型名称和 ID(undercover 模式下隐藏)
- 知识截止日期(从
getKnowledgeCutoff()) - 最新 Claude 模型家族信息(Opus 4.6、Sonnet 4.6、Haiku 4.5)
- Claude Code 可用平台说明
- Fast mode 说明(使用相同模型,仅更快输出)
getKnowledgeCutoff(modelId) -- 行 713-730
基于模型 canonical name 的简单模式匹配返回知识截止日期:
| 模型 | 截止日期 |
|---|---|
| claude-sonnet-4-6 | August 2025 |
| claude-opus-4-6 | May 2025 |
| claude-opus-4-5 | May 2025 |
| claude-haiku-4 | February 2025 |
| claude-opus-4 / claude-sonnet-4 | January 2025 |
| 其他 | null(不显示) |
getShellInfoLine() -- 行 732-743
从 process.env.SHELL 提取 shell 名称。Windows 平台额外添加 "(use Unix shell syntax, not Windows)" 提示。
3.6 语言、样式与 MCP
getLanguageSection(languagePreference) -- 行 142-149
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #5 |
| 缓存 | systemPromptSection |
| 条件 | 仅当 settings.language 非空时返回 |
注入 # Language 标题,指令模型用指定语言回复,但技术术语和代码标识符保持原形式。
getOutputStyleSection(outputStyleConfig) -- 行 151-158
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #6 |
| 缓存 | systemPromptSection |
| 条件 | 仅当 outputStyleConfig !== null 时返回 |
简单包装:# Output Style: {name}\n{prompt}。输出样式的复杂逻辑在 outputStyles.ts(详见第 6 章)。
getMcpInstructionsSection(mcpClients) -- 行 160-165
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #7 |
| 缓存 | DANGEROUS_uncached |
| 原因 | MCP 服务器在 turn 之间可能连接/断开 |
调用 getMcpInstructions()(行 579-603),过滤已连接且有 instructions 的 MCP 客户端,为每个生成 ## {serverName}\n{instructions} 块,用 # MCP Server Instructions 标题包装。当启用 isMcpInstructionsDeltaEnabled() 时,此分节返回 null——MCP 指令改由 attachments 注入。
3.7 辅助功能分节
getScratchpadInstructions() -- 行 797-818
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #8 |
| 缓存 | systemPromptSection |
| 条件 | isScratchpadEnabled() |
当启用 scratchpad 时,注入 # Scratchpad Directory,指示模型使用会话特定的临时目录而非 /tmp。用途包括中间结果存储、临时脚本、分析处理中的工作文件等。
getFunctionResultClearingSection(model) -- 行 821-839
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #9 |
| 缓存 | systemPromptSection |
| 条件 | feature('CACHED_MICROCOMPACT') + 模型支持 + 配置启用 |
当 micro-compact(自动上下文压缩)启用时,告知模型旧的工具结果会被自动清除以释放空间,最近 N 条结果始终保留。这与 SUMMARIZE_TOOL_RESULTS_SECTION 配合,提醒模型在回复中写下可能后续需要的重要信息。
getBriefSection() -- 行 843-858
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀(条件追加) |
| 条件 | feature('KAIROS') || feature('KAIROS_BRIEF'),且 Brief 工具启用,且非 PROACTIVE 活跃 |
当 Brief 模式启用但 PROACTIVE 未激活时,注入 BRIEF_PROACTIVE_SECTION。如果 PROACTIVE 激活,此内容已由 getProactiveSection() 内联包含,因此这里跳过以避免重复。
getProactiveSection() -- 行 860-913
| 属性 | 值 |
|---|---|
| 位置 | PROACTIVE 路径专属 |
| 条件 | feature('PROACTIVE') || feature('KAIROS') + isProactiveActive() |
| 标题 | # Autonomous work |
最长的单分节之一,定义了 Claude Code 作为自主代理时的完整行为框架。结构包括:
- Tick 处理:
<tick>是保活机制,处理最新一条即可 - Pacing:用 SleepTool 控制动作间隔,无事可做时必须调用 Sleep
- 首次唤醒:简短问候并等待方向,不要擅自探索代码库
- 后续唤醒:主动寻找有用的工作,不要重复提问
- 响应性:用户活跃时保持紧密反馈循环
- 偏向行动:用最佳判断行动而非请求确认
- 终端焦点:根据
terminalFocus调整自主程度
3.8 系统级辅助分节
getHooksSection() -- 行 127-129
单行指令,解释 hooks 是用户配置的 shell 命令(响应工具调用等事件),<user-prompt-submit-hook> 来自用户端。被 hook 阻塞时应尝试调整方法或请用户检查 hooks 配置。被 getSimpleSystemSection() 内部调用。
getSystemRemindersSection() -- 行 131-134
两条规则:<system-reminder> 标签是系统自动添加的有用信息(与具体工具结果或用户消息无关);对话通过自动摘要拥有无限上下文。仅在 PROACTIVE 路径直接使用。
getAntModelOverrideSection() -- 行 136-140
| 属性 | 值 |
|---|---|
| 位置 | 动态后缀 #3 |
| 缓存 | systemPromptSection |
| 条件 | USER_TYPE === 'ant' 且非 undercover |
从 getAntModelOverrideConfig() 获取 ant 内部模型覆盖配置的 defaultSystemPromptSuffix。返回 null 当:非 ant 用户、undercover 模式、或无覆盖配置。
4SYSTEM_PROMPT_DYNAMIC_BOUNDARY 深度解析
4.1 边界标记的定义与语义
export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY =
'__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'Copy
这个字符串常量是 Claude Code prompt 缓存架构的核心枢纽。它将 getSystemPrompt() 返回的数组分为两个区域:
| 区域 | 内容 | cacheScope | 特征 |
|---|---|---|---|
| 静态前缀 | 7 个分节(Intro → OutputEfficiency) | 'global' | 跨用户/跨组织可缓存,内容与会话状态无关 |
| 动态后缀 | 10+ 分节(SessionGuidance → Brief) | null | 每个会话可能不同,含用户配置/模型/工具集依赖 |
静态前缀(7 个分节)的顺序
getSimpleIntroSection()-- 身份定义 + 安全指令getSimpleSystemSection()-- 系统行为规则getSimpleDoingTasksSection()-- 任务执行指南(条件包含)getActionsSection()-- 操作风险评估getUsingYourToolsSection()-- 工具使用偏好getSimpleToneAndStyleSection()-- 语气与格式getOutputEfficiencySection()-- 输出效率
动态后缀(10+ 分节)的顺序
session_guidance-- 会话特定引导memory-- Memory promptant_model_override-- ant 模型覆盖env_info_simple-- 环境信息language-- 语言偏好output_style-- 输出样式mcp_instructions-- MCP 服务器指令 uncachedscratchpad-- Scratchpad 目录frc-- Function Result Clearingsummarize_tool_results-- 工具结果摘要提醒numeric_length_anchors-- 数值长度锚点 ant-onlytoken_budget-- Token 预算 TOKEN_BUDGETbrief-- Brief 模式 KAIROS
shouldUseGlobalCacheScope() 的条件
export function shouldUseGlobalCacheScope(): boolean {
return (
getAPIProvider() === 'firstParty' &&
!isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS)
)
}Copy
仅当 API Provider 是 Anthropic 第一方(非 Bedrock/Vertex/Foundry)且未禁用实验性 beta 时,才启用全局缓存范围。Foundry 被显式排除——GrowthBook 从未将 Foundry 用户纳入 rollout 实验。
4.2 splitSysPromptPrefix 的处理逻辑
splitSysPromptPrefix()(utils/api.ts:321)是消费边界标记的关键函数,有三个处理路径:
路径 1:MCP 工具存在(skipGlobalCache=true)
过滤掉边界标记,返回最多 3 个块,全部用 cacheScope: 'org'(不使用全局缓存,因为 MCP 工具是用户特定的):
- Attribution header(
cacheScope: null) - CLI 系统 prompt 前缀(
cacheScope: 'org') - 其余拼接(
cacheScope: 'org')
路径 2:全局缓存模式 + 边界标记存在
这是最优路径。返回最多 4 个块:
- Attribution header(
cacheScope: null) - CLI 系统 prompt 前缀(
cacheScope: null) - 边界前静态内容拼接(
cacheScope: 'global')-- 跨所有用户共享 - 边界后动态内容拼接(
cacheScope: null)-- 每次重新处理
路径 3:默认模式
3P 提供商或边界缺失时的回退。返回最多 3 个块,全部 org 级缓存。
4.3 为什么不能随意移动分节(PR 教训)
源码注释(行 343-351)明确警告:getSessionSpecificGuidanceSection 中的每个条件判断都是一个运行时 bit。如果将它放在边界标记之前(静态区域),每个 bit 组合(如 hasAskUserQuestionTool x hasSkillTool x hasAgentTool x ...)都会产生不同的 Blake2b 哈希前缀,导致 2^N 个缓存变体,全局缓存命中率暴跌。
同样的道理,outputStyleConfig 被特意保留在静态区域(虽然它有条件判断),因为"身份定义的开场位置"的 eval 影响更大。但 isForkSubagentEnabled() 因为读取 getIsNonInteractiveSession() 而必须在边界后。
CLI 系统 prompt 前缀(CLI_SYSPROMPT_PREFIXES)也通过内容匹配(而非位置)来识别,定义在 constants/system.ts:
const CLI_SYSPROMPT_PREFIX_VALUES = [
`You are Claude Code, Anthropic's official CLI for Claude.`,
`You are Claude Code, Anthropic's official CLI for Claude, running within the Claude Agent SDK.`,
`You are a Claude agent, built on Anthropic's Claude Agent SDK.`,
] as constCopy
三种前缀对应不同的入口场景:默认交互、Agent SDK 中的 CC 预设、纯 Agent SDK 场景。
5systemPromptSections.ts 缓存机制
5.1 缓存 API
SystemPromptSection 类型
type SystemPromptSection = {
name: string // 缓存键
compute: ComputeFn // () => string | null | Promise<string | null>
cacheBreak: boolean // true = 每个 turn 重新计算
}
type ComputeFn = () => string | null | Promise<string | null>Copy
systemPromptSection(name, compute) -- 缓存版
返回 { name, compute, cacheBreak: false }。计算一次后缓存到 /clear 或 /compact 触发 clearSystemPromptSections()。
DANGEROUS_uncachedSystemPromptSection(name, compute, _reason) -- 非缓存版
返回 { name, compute, cacheBreak: true }。每个 turn 重新计算。_reason 参数是文档用途——强制调用者解释为什么需要打破缓存。
5.2 resolveSystemPromptSections 并行解析
export async function resolveSystemPromptSections(
sections: SystemPromptSection[],
): Promise<(string | null)[]> {
const cache = getSystemPromptSectionCache() // Map<string, string | null>
return Promise.all(
sections.map(async s => {
if (!s.cacheBreak && cache.has(s.name)) {
return cache.get(s.name) ?? null // 缓存命中
}
const value = await s.compute() // 计算
setSystemPromptSectionCacheEntry(s.name, value) // 写入缓存
return value
}),
)
}Copy
所有分节的计算通过 Promise.all 并行执行。缓存存储在 STATE.systemPromptSectionCache(bootstrap/state.ts:1641),是一个简单的 Map<string, string | null>。
clearSystemPromptSections() -- 缓存清除
export function clearSystemPromptSections(): void {
clearSystemPromptSectionState() // Map.clear()
clearBetaHeaderLatches() // 重置 beta 头锁存
}Copy
在 /clear 和 /compact 时调用,确保新对话获得最新的评估结果。同时重置 beta header latches,让 AFK/fast-mode/cache-editing 头在新对话中重新评估。
5.3 分节缓存类型矩阵
| 分节名称 | 缓存策略 | 说明 |
|---|---|---|
session_guidance | cached | 工具集/技能在会话内不变 |
memory | cached | Memory prompt 在会话内稳定 |
ant_model_override | cached | 模型覆盖在会话内不变 |
env_info_simple | cached | 环境信息在会话内不变 |
language | cached | 语言设置在会话内不变 |
output_style | cached | 样式在会话内不变 |
mcp_instructions | DANGEROUS_uncached | MCP 服务器在 turn 间连接/断开 |
scratchpad | cached | Scratchpad 路径在会话内固定 |
frc | cached | Micro-compact 配置在会话内稳定 |
summarize_tool_results | cached | 静态文本 |
numeric_length_anchors | cached | 静态文本(ant-only) |
token_budget | cached | 曾用 DANGEROUS_uncached,改为 cached 以避免 ~20K token/次的缓存失效 |
brief | cached | Brief 状态在会话内稳定 |
注释明确记录:此分节曾经是 DANGEROUS_uncached(根据 getCurrentTurnTokenBudget() 切换),会在每次预算切换时破坏约 20K token 的 prompt cache。改为 cached + "When the user specifies..." 的条件措辞后,即使无预算时该文本也是无害的 no-op,同时消除了缓存失效。
6输出样式系统
6.1 OutputStyleConfig 类型定义
export type OutputStyleConfig = {
name: string // 样式名称
description: string // 用户可见描述
prompt: string // 注入系统 prompt 的指令文本
source: SettingSource | 'built-in' | 'plugin'
keepCodingInstructions?: boolean // true = 保留 "Doing tasks" 分节
forceForPlugin?: boolean // true = 插件强制启用此样式
}Copy
keepCodingInstructions 是一个关键字段:当自定义输出样式完全改变了 Claude 的身份定位(如"你是一个诗人")时,保留"Doing tasks"分节中的编程指南是无意义的。设为 false 或未设置时,getSimpleDoingTasksSection() 将被跳过。
6.2 三种内置样式
default(null)
OUTPUT_STYLE_CONFIG['default'] 返回 null——表示不注入任何特殊输出样式 prompt。这是最常见的路径。
Explanatory(教学模式)
keepCodingInstructions: true。在正常编程辅助基础上,添加"Insights"教育性解释板块——代码前后提供 2-3 个与当前代码库相关的教育要点,使用特殊的星标格式渲染。
Learning(学习模式)
keepCodingInstructions: true。最复杂的内置样式。核心机制是"Learn by Doing"——当生成 20+ 行涉及设计决策、业务逻辑或关键算法的代码时,要求用户自己写 2-10 行。包含完整的请求格式模板、TodoList 集成指南、三个示例(整函数、部分函数、调试)和贡献后洞察分享规则。
6.3 自定义样式加载
getOutputStyleDirStyles(cwd) -- loadOutputStylesDir.ts
从 .claude/output-styles/*.md 目录(项目级和用户级)加载 Markdown 文件,解析 frontmatter 获取 name/description/keepCodingInstructions,文件内容作为 prompt。使用 memoize 缓存结果。
getAllOutputStyles(cwd) -- outputStyles.ts:137
合并所有来源的样式,优先级从低到高:
- 内置样式(built-in)
- 插件样式(plugin)
- 用户设置样式(userSettings)
- 项目设置样式(projectSettings)
- 托管设置样式(policySettings)-- 最高优先级
6.4 getOutputStyleConfig() 选择逻辑
export async function getOutputStyleConfig(): Promise<OutputStyleConfig | null> {
const allStyles = await getAllOutputStyles(getCwd())
// 1. 检查插件强制样式
const forcedStyles = Object.values(allStyles).filter(
style => style?.source === 'plugin' && style.forceForPlugin === true
)
if (forcedStyles[0]) return forcedStyles[0] // 多个时用第一个,log warn
// 2. 读取用户设置
const outputStyle = settings?.outputStyle || 'default'
return allStyles[outputStyle] ?? null
}Copy
优先级链:插件强制样式 > 用户/项目设置 > default(null)。多个插件有强制样式时仅使用第一个并记录警告。
7上下文注入 (context.ts)
7.1 getSystemContext()
export const getSystemContext = memoize(
async (): Promise<{ [k: string]: string }> => {
// 跳过 git status: CCR 模式或 git 指令被禁用时
const gitStatus = isRemote || !shouldIncludeGitInstructions()
? null : await getGitStatus()
// 系统 prompt 注入(ant-only,用于缓存打破)
const injection = feature('BREAK_CACHE_COMMAND')
? getSystemPromptInjection() : null
return {
...(gitStatus && { gitStatus }),
...(injection ? { cacheBreaker: `[CACHE_BREAKER: ${injection}]` } : {}),
}
},
)Copy
getGitStatus()(同文件 :36-111)通过 5 路 Promise.all 并行执行 git 命令:branch、defaultBranch、status --short、log --oneline -n 5、config user.name。状态超过 2000 字符时截断并附加提示。整个结果被 memoize 缓存,会话期间只执行一次。
缓存失效机制
setSystemPromptInjection(value) 同时清除 getUserContext.cache 和 getSystemContext.cache,确保注入变更时上下文立即刷新。
7.2 getUserContext()
export const getUserContext = memoize(
async (): Promise<{ [k: string]: string }> => {
// CLAUDE_CODE_DISABLE_CLAUDE_MDS: 硬关闭
// --bare: 跳过自动发现,但尊重 --add-dir
const shouldDisable = isEnvTruthy(process.env.CLAUDE_CODE_DISABLE_CLAUDE_MDS)
|| (isBareMode() && getAdditionalDirectoriesForClaudeMd().length === 0)
const claudeMd = shouldDisable
? null
: getClaudeMds(filterInjectedMemoryFiles(await getMemoryFiles()))
setCachedClaudeMdContent(claudeMd || null) // 供 yoloClassifier 使用
return {
...(claudeMd && { claudeMd }),
currentDate: `Today's date is ${getLocalISODate()}.`,
}
},
)Copy
关键设计决策:
- CLAUDE.md 加载:通过
getMemoryFiles()发现所有 CLAUDE.md 文件(全局、项目、用户级),经filterInjectedMemoryFiles()过滤掉已通过 Memory 系统注入的文件,最终由getClaudeMds()格式化为上下文文本 - currentDate 注入:使用
getLocalISODate()(非 memoized 版本),每次调用获取实时日期。但由于getUserContext本身被 memoize,日期实际上在首次调用时冻结 - --bare 模式语义:"跳过我没要求的东西",不是"忽略我要求的东西"——
--add-dir指定的目录仍然被尊重 - 缓存副作用:
setCachedClaudeMdContent()将 CLAUDE.md 内容缓存到全局状态,供yoloClassifier.ts读取(避免循环依赖:permissions/filesystem -> permissions -> yoloClassifier)
8Memory Prompt 构建 (memdir/memdir.ts)
8.1 MEMORY.md 截断规则
export const MAX_ENTRYPOINT_LINES = 200
export const MAX_ENTRYPOINT_BYTES = 25_000 // ~125 chars/line at 200 linesCopy
truncateEntrypointContent(raw) 实现双重截断:
- 先行截断:如果超过 200 行,取前 200 行
- 后字节截断:如果截断后仍超过 25000 字节,在最后一个换行符处切断(避免行中截断)
截断时附加警告信息,说明触发原因(行数超限、字节超限、或两者皆超)。设计目标:捕获那些单行很长、虽在 200 行内但已达 197KB 的极端索引文件(p100 观测值)。
8.2 buildMemoryLines() 完整结构
buildMemoryLines(displayName, memoryDir, extraGuidelines, skipIndex) 构建行为指令(不含 MEMORY.md 内容),返回 string[]。完整结构如下:
# {displayName}标题 + 目录路径 +DIR_EXISTS_GUIDANCE("目录已存在,直接写入")- 总体描述:建立记忆系统的目的
- 显式记忆保存/遗忘指令
## Types of memory(TYPES_SECTION_INDIVIDUAL)-- 四类记忆的 XML 结构化定义## What NOT to save in memory(WHAT_NOT_TO_SAVE_SECTION)## How to save memories-- 两步流程(写文件 + 更新 MEMORY.md 索引),或单步流程(skipIndex=true)## When to access memories(WHEN_TO_ACCESS_SECTION)## Before recommending from memory(TRUSTING_RECALL_SECTION)-- 记忆验证规则## Memory and other forms of persistence-- 记忆 vs 计划 vs 任务的区分- 可选:
extraGuidelines(如 Cowork 注入的额外策略) - 可选:
## Searching past context(tengu_coral_fernfeature flag)
ensureMemoryDirExists(memoryDir)
幂等目录创建。fs.mkdir 递归创建(内部已处理 EEXIST)。由 loadMemoryPrompt() 调用(通过 systemPromptSection 缓存,每会话只执行一次)。确保模型可以直接写入而无需检查目录存在。
8.3 内存类型定义
| 类型 | 描述 | 何时保存 |
|---|---|---|
user | 用户角色、目标、职责、知识水平 | 了解到用户详情时 |
feedback | 用户对工作方式的修正和确认 | 纠正("不要这样")或确认("对,就是这样")时 |
project | 进行中的工作、目标、事件、bug(非代码可推导) | 了解谁在做什么、为什么、截止日期时 |
reference | 外部系统信息指针(Linear、Slack、Grafana 等) | 了解到外部资源位置时 |
WHAT_NOT_TO_SAVE_SECTION 核心内容
- 代码模式、架构、文件路径——可从项目当前状态推导
- Git 历史——
git log / git blame是权威来源 - 调试解决方案——修复在代码中,上下文在 commit message 中
- CLAUDE.md 中已记录的内容
- 临时任务细节
- 即使用户明确要求也适用——如果用户要求保存 PR 列表,应追问"什么是令人惊讶或不显而易见的"
TRUSTING_RECALL_SECTION(记忆验证规则)
eval 验证的关键洞察(memory-prompt-iteration case 3, 0/2 -> 3/3):
- 记忆中命名的文件路径——使用前检查文件是否存在
- 记忆中命名的函数或 flag——使用前 grep 确认
- "记忆说 X 存在"不等于"X 现在存在"
- 仓库状态摘要是时间冻结的快照——询问"最近"或"当前"状态时,优先用 git log 或读代码
loadMemoryPrompt() 的调度逻辑
根据启用的内存系统选择不同的构建路径:
- KAIROS + auto:
buildAssistantDailyLogPrompt()——追加式日志模式,写入logs/YYYY/MM/YYYY-MM-DD.md - TEAMMEM + auto + team:
buildCombinedMemoryPrompt()——私有 + 团队双目录 - auto only:
buildMemoryLines()——单目录个人记忆 - 全部禁用:返回
null,记录遥测
9特性门控清单
9.1 Feature Flags 列表(从代码提取)
| Feature Flag | 控制的分节/行为 | 类型 |
|---|---|---|
CACHED_MICROCOMPACT | getFunctionResultClearingSection + getCachedMCConfig 条件导入 | bun:bundle DCE |
PROACTIVE | proactiveModule 条件导入 + 整个 PROACTIVE 路径 | bun:bundle DCE |
KAIROS | proactiveModule 导入 + PROACTIVE 路径 + Brief 分节 + 日志模式记忆 | bun:bundle DCE |
KAIROS_BRIEF | BRIEF_PROACTIVE_SECTION 导入 + Brief 分节 | bun:bundle DCE |
EXPERIMENTAL_SKILL_SEARCH | DiscoverSkillsTool 导入 + getDiscoverSkillsGuidance() | bun:bundle DCE |
VERIFICATION_AGENT | 验证代理规则注入(session_guidance 内) | bun:bundle DCE |
TOKEN_BUDGET | token_budget 分节 | bun:bundle DCE |
BREAK_CACHE_COMMAND | systemPromptInjection 缓存打破(context.ts) | bun:bundle DCE |
TEAMMEM | teamMemPaths/teamMemPrompts 条件导入(memdir.ts) | bun:bundle DCE |
NATIVE_CLIENT_ATTESTATION | Attribution header 中的 cch 占位符 | bun:bundle DCE |
GrowthBook Flags(运行时)
| Flag | 控制 |
|---|---|
tengu_hive_evidence | 验证代理是否启用(ant-only A/B) |
tengu_coral_fern | "Searching past context" 分节 |
tengu_moth_copse | skipIndex(记忆不维护 MEMORY.md 索引) |
tengu_herring_clock | 团队记忆 cohort 遥测 |
tengu_attribution_header | Attribution header 启用/禁用 |
环境变量 Flags
| 环境变量 | 效果 |
|---|---|
CLAUDE_CODE_SIMPLE | 极简模式,跳过所有复杂逻辑 |
CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS | 禁用全局缓存范围 |
CLAUDE_CODE_DISABLE_CLAUDE_MDS | 禁用 CLAUDE.md 加载 |
CLAUDE_CODE_DISABLE_AUTO_MEMORY | 禁用自动记忆 |
CLAUDE_CODE_OVERRIDE_DATE | 覆盖日期(ant-only 测试) |
CLAUDE_CODE_REMOTE | 远程模式,跳过 git status |
CLAUDE_COWORK_MEMORY_EXTRA_GUIDELINES | Cowork 注入的额外记忆策略 |
USER_TYPE | 'ant' = Anthropic 内部用户 |
9.2 ant vs 非 ant 差异矩阵
| 分节/行为 | ant | 外部 |
|---|---|---|
| 注释风格(DoingTasks) | 4 条极简规则:默认不写注释、不解释 WHAT、不删已有注释、完成前验证 | 无额外规则 |
| OutputEfficiency | "Communicating with the user"——长篇叙事式指导 | "Output efficiency"——简洁指令 |
| ToneAndStyle | 不含"be short and concise" | 含"be short and concise" |
| False-claims 缓解 | 如实报告规则(不隐瞒失败、不虚称通过) | 无 |
| 主动性提示 | "你是协作者不只是执行者" | 无 |
| Bug 报告引导 | /issue 或 /share 建议 + Slack 频道发送 | 无 |
| numeric_length_anchors | 工具调用间文本 ≤25 词,最终回复 ≤100 词 | 无 |
| AntModelOverride | 可能注入模型覆盖 prompt 后缀 | 无 |
| Undercover 模式 | 隐藏所有模型名称/ID | N/A |
10与 AI-Fleet 的对照分析
10.1 对比矩阵
| 维度 | Claude Code (prompts.ts) | AI-Fleet (CLAUDE.md + .claude/rules/) |
|---|---|---|
| 架构模式 | 运行时编排器——TypeScript 函数动态组装 string[],每个分节独立计算 | 静态声明式——Markdown 文件由 CLI harness 全文加载到 T0/T1 层 |
| 缓存机制 | 三层:全局缓存(SYSTEM_PROMPT_DYNAMIC_BOUNDARY)+ 分节 memoization(systemPromptSections.ts)+ 函数 memoize(context.ts) | 无显式缓存层;依赖 Claude 的 KV-cache 隐式复用(规则:静态常量放前面,动态变量放后面) |
| 条件组装 | 每个分节函数独立返回 string | null,feature flags + 运行时条件驱动 | 粗粒度条件——rules-on-demand 按任务类型整体加载(04/07/10/11),无分节级开关 |
| 身份多态 | 三路模式(SIMPLE / PROACTIVE / 标准),outputStyle 可完全改变身份 | 单一身份("业财税合规产品经理助手"),通过 Skill 切换行为而非身份 |
| 内存系统 | 四类型记忆(user/feedback/project/reference)+ MEMORY.md 索引 + 截断规则 + 日志模式 | MEMORY.md + memory/*.md 文件(T2 按需加载),无类型分类,无截断机制 |
| 上下文管理 | 编译时 DCE(bun:bundle feature())+ 运行时 GrowthBook flags + env vars | 手动分层(T0 Auto-mount / T1 Index-mount / T2 Query-mount / T3 Immutable)+ context_monitor.py 阈值监控 |
| 工具引导 | 根据 enabledTools Set 动态生成工具偏好规则,REPL 模式有独立路径 | TOOLBOX.md 静态描述 + Skill 路由器(semantic_skill_router.py) |
10.2 可借鉴的改进方向
方向 1:分节级缓存标记
CC 的 systemPromptSection() / DANGEROUS_uncachedSystemPromptSection() 模式值得借鉴。AI-Fleet 的 .claude/rules/ 文件全部在 T0 层始终加载(约 19K token / ~10%),但其中有些内容(如 04-frontend-validation.md)只在前端任务时需要。引入类似的缓存/按需标记机制,可以将 T0 负载降低 30-40%。
方向 2:动态边界协议
CC 的 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 将静态指令和会话特定内容物理分离,使得 API 层可以对静态部分使用全局缓存。AI-Fleet 虽然在 06-context-engineering.md 中提到"静态常量在 prompt 开头,动态变量在末尾"的 KV-Cache 保护规则,但缺少显式的边界标记和自动化的缓存分区。可以在 CLAUDE.md 中引入类似的 boundary marker,配合 API wrapper 自动分区。
方向 3:记忆类型分类
CC 的四类记忆(user/feedback/project/reference)+ "什么不应该保存" + "推荐前验证"三层约束系统,比 AI-Fleet 当前的自由形式 memory/*.md 更加结构化。特别是 TRUSTING_RECALL_SECTION("记忆说文件存在不等于文件现在存在")的验证规则,直接解决了记忆漂移问题。AI-Fleet 的 context_monitor.py 监控上下文利用率,但缺少记忆内容的质量约束。
方向 4:条件组装替代全文加载
AI-Fleet 的 .claude/rules/01-11 是"始终加载 + 按需加载"二分法。CC 的模式更细粒度——同一文件内的不同段落根据 feature flag 和运行时条件动态包含/排除。这种模式在 Skill 系统中特别有价值:一个 Skill 的不同章节可以根据当前上下文选择性注入,而非整个 SKILL.md 要么全加载要么全不加载。
方向 5:DANGEROUS_ 前缀命名规范
CC 用 DANGEROUS_uncachedSystemPromptSection 这个刻意恐吓的名字来阻止开发者随意打破缓存,且强制提供 _reason 参数。这种"通过命名制造摩擦"的设计模式值得在 AI-Fleet 的 Harness 层采用——比如 DANGEROUS_forceCompact(reason: string) 替代 forceCompact()。