目录
  1. 1. 一、System Prompt 的整体架构
  2. 2. 二、默认 System Prompt 的组成(getSystemPrompt())
    1. 2.1. 2.1 核心行为规范(静态,可全局缓存)
    2. 2.2. 2.2 动态 Section(每次会话重新计算)
  3. 3. 三、Section 缓存机制(systemPromptSections.ts)
  4. 4. 四、静态/动态边界(API 侧缓存优化)
  5. 5. 五、关键编码风格约束(面试必备)
  6. 6. 六、多角色 System Prompt 变体
    1. 6.1. 6.1 标准交互模式
    2. 6.2. 6.2 Coordinator 模式
    3. 6.3. 6.3 SubAgent 模式(AgentTool)
    4. 6.4. 6.4 Proactive 模式(KAIROS)
    5. 6.5. 6.5 Override 模式(loop-mode/SDK)
  7. 7. 七、Environment 注入(动态上下文)
  8. 8. 八、MCP 工具的动态注入
  9. 9. 九、Hooks 感知
  10. 10. 十、面试要点
  • 深度补充:System Prompt 源码精读
    1. 1. 十一、Section 缓存机制:两层缓存的完整架构
      1. 1.1. 第一层:进程内 Section 缓存(In-process Memoization)
      2. 1.2. 哪些 Section 可全局缓存,哪些必须每次重算
      3. 1.3. 第二层:Anthropic API 侧缓存(Prompt Cache / cache_control)
    2. 2. 十二、getSystemPrompt() 完整调用链
    3. 3. 十三、prompts.ts 核心行为约束的实际文本
      1. 3.1. 过度帮助的克制(L201-L203)
      2. 3.2. 不要给时间估计(L232)
      3. 3.3. 失败后的诊断规范(L233)
      4. 3.4. 安全意识(L234)
      5. 3.5. 兼容性删除的约束(L236)
      6. 3.6. Ant 内部版独有:诚实报告规范(L240,仅 USER_TYPE=ant 时注入)
    4. 4. 十四、Environment Section 注入细节
    5. 5. 十五、三种 Agent 变体的 Prompt 差异
      1. 5.1. 变体一:Default(标准 Claude Code)
      2. 5.2. 变体二:Coordinator(多 Agent 协调模式)
      3. 5.3. 变体三:Proactive(自主 Agent / KAIROS 模式)
    6. 6. 十六、Hooks 感知的完整注入路径
      1. 6.1. 1. 主 System Prompt 中的 Hooks Section
      2. 6.2. 2. getSystemRemindersSection() 中的 system-reminder 标签说明
    7. 7. 十七、面试深度题(基于源码的五道硬核问答)
      1. 7.1. Q1:为什么静态内容必须放在 System Prompt 开头,而不是末尾?
      2. 7.2. Q2:mcp_instructions 为什么是唯一的 DANGEROUS_uncached Section?进程内缓存和 API 侧缓存在此如何联动?
      3. 7.3. Q3:SystemPrompt 是 readonly string[] 加品牌类型,为什么不直接用 string?
      4. 7.4. Q4:Proactive 模式为什么不走 Section 注册表(dynamicSections),而是直接 await 所有内容?
      5. 7.5. Q5:USER_TYPE=ant 的条件判断在构建时如何处理?外部发布版和内部版的 prompt 大小差异有多大?
    8. 8. 十八、补充:buildEffectiveSystemPrompt 的优先级决策树
    9. 9. 涉及源文件
  • 【Claude Code源码剖析】21-System Prompt 构建与 Prompt 工程

    ⚠️ 学习声明:本文档基于 Claude Code 2.1.88 源码分析整理,仅供个人学习研究使用,不做任何商业用途。

    这是决定模型行为的最上游配置,所有工具调用、角色定位、安全约束都在此定义。


    一、System Prompt 的整体架构

    Claude Code 的 System Prompt 不是一段静态字符串,而是多段动态内容的拼接,在每次会话启动时按优先级构建:

    buildEffectiveSystemPrompt() 优先级(高 → 低):

    0. overrideSystemPrompt ← 最高优先级,完全替换其他所有 prompt

    1. coordinatorSystemPrompt ← Coordinator Mode 时使用

    2. agentSystemPrompt ← 有 AgentDefinition 时使用(替换 default)
    (proactive 模式下追加而非替换)

    3. customSystemPrompt ← --system-prompt CLI 参数

    4. defaultSystemPrompt ← 标准 Claude Code prompt(getSystemPrompt())

    + appendSystemPrompt ← 永远追加到末尾(除非 override 激活)

    二、默认 System Prompt 的组成(getSystemPrompt())

    prompts.ts 中的 getSystemPrompt() 拼接了以下逻辑段落,每段都是一个命名的 Section

    2.1 核心行为规范(静态,可全局缓存)

    Section 内容摘要
    Intro 角色定位:interactive agent for software engineering tasks
    System 工具调用规则、权限模式说明、system-reminder 标签处理
    Doing Tasks 编码风格约束(不过度注释、不多余抽象、最小代码改动)
    Proactiveness 何时主动补充信息 vs 只做被要求的事
    Synthetic Messages 如何处理 harness 注入的合成消息
    Images 图像输入的处理规则
    Environment OS、shell、工作目录、Git 状态注入
    Tool Use 工具调用的通用规则

    2.2 动态 Section(每次会话重新计算)

    Section 内容 是否缓存
    memory memdir 加载的自动记忆内容 否(每次加载)
    mcp-instructions MCP 服务器的工具说明 否(按连接状态)
    output-style 用户配置的输出风格 是(session 级)
    language 语言偏好(中文/英文等)
    todo-tools Todo 工具相关指令
    scratchpad 草稿本目录信息
    worktree Git Worktree 状态 否(实时)

    三、Section 缓存机制(systemPromptSections.ts)

    System Prompt 的计算开销不小(读文件、查 Git 状态等),CC 用命名缓存避免每次重复计算:

    // 缓存版本(session 内只算一次)
    export function systemPromptSection(name: string, compute: () => ...) {
    return { name, compute, cacheBreak: false }
    }

    // 非缓存版本(每次都重新计算,会破坏 prompt cache)
    export function DANGEROUS_uncachedSystemPromptSection(
    name: string,
    compute: () => ...,
    _reason: string, // 必须说明为什么需要 cache-break
    ) {
    return { name, compute, cacheBreak: true }
    }

    缓存清除时机/clear 命令 或 /compact 压缩时,clearSystemPromptSections() 清空所有缓存,确保新会话获得新鲜的 prompt。


    四、静态/动态边界(API 侧缓存优化)

    prompts.ts 中有一个关键常量:

    export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

    这个边界将 System Prompt 数组分为两部分:

    [静态部分(跨用户/跨会话不变)]  ← scope: 'global',Anthropic API 跨 org 缓存
    DYNAMIC_BOUNDARY
    [动态部分(含用户路径、Git状态等)] ← 不缓存,每次请求新鲜内容

    工程价值:静态部分可以命中 Anthropic 服务端的 prompt cache,大幅减少 token 消耗和首 token 延迟。


    五、关键编码风格约束(面试必备)

    prompts.ts 中的 getSimpleDoingTasksSection() 包含了 CC 对 Claude 行为最核心的约束,这些是 Anthropic Prompt Engineering 的精华:

    代码修改最小化原则:
    ├─ "Don't add features beyond what was asked"
    ├─ "A bug fix doesn't need surrounding code cleaned up"
    ├─ "Don't add docstrings to code you didn't change"
    └─ "Three similar lines is better than a premature abstraction"

    注释原则(ant 内部版本更严格):
    ├─ "Default to writing NO comments"
    ├─ "Only add when the WHY is non-obvious"
    └─ "Don't explain WHAT the code does"

    诚实性约束(Capybara v8 特别加强):
    ├─ "If tests fail, say so with relevant output"
    ├─ "Never claim 'all tests pass' when output shows failures"
    └─ "Report outcomes faithfully"

    这些约束直接影响模型的输出行为,是 Prompt Engineering 的核心实践。


    六、多角色 System Prompt 变体

    CC 根据运行模式选择不同的 System Prompt:

    6.1 标准交互模式

    getSystemPrompt() → 完整的编程助手 prompt,约 8000+ tokens

    6.2 Coordinator 模式

    // 当 CLAUDE_CODE_COORDINATOR_MODE=true
    getCoordinatorSystemPrompt() // 专注于任务分发、团队协调

    6.3 SubAgent 模式(AgentTool)

    // AgentDefinition.getSystemPrompt()
    // 内置 Agent 类型:verification-agent, explore-agent, etc.
    // 每种 Agent 有专属的 prompt,专注特定任务

    6.4 Proactive 模式(KAIROS)

    // proactive 模式:agent prompt APPEND 到 default,而非替换
    // 默认模式:agent prompt REPLACE default
    isProactiveActive()
    ? [...defaultPrompt, agentPrompt] // 追加
    : [agentPrompt] // 替换

    6.5 Override 模式(loop-mode/SDK)

    // 完全替换所有 prompt
    overrideSystemPrompt → 忽略所有其他配置

    七、Environment 注入(动态上下文)

    System Prompt 中注入了运行时上下文,让 Claude 了解当前工作环境:

    // 注入内容示例(DANGEROUS_uncached,每次请求刷新)
    `
    Here is useful information about the environment:
    <env>
    Working directory: /home/user/myproject
    Is directory a git repo: Yes
    Platform: linux
    OS Version: Ubuntu 22.04
    Shell: /bin/bash
    Today's date and time: 2026-05-27
    </env>
    `

    注意这是 **DANGEROUS_uncachedSystemPromptSection**——因为工作目录可能在会话中变化(如 cd 命令)。


    八、MCP 工具的动态注入

    当用户配置了 MCP 服务器后,System Prompt 中会追加 MCP 工具的说明:

    function getMcpInstructionsSection(mcpClients): string | null {
    // 格式化每个 MCP 服务器的工具列表
    // 告知 Claude 如何调用这些工具
    // 包含工具描述、参数说明等
    }

    这让 Claude 能”知道”并正确调用外部 MCP 工具。


    九、Hooks 感知

    System Prompt 中包含对 Hooks 的说明,让模型理解 hook 反馈的来源:

    function getHooksSection(): string {
    return `Users may configure 'hooks', shell commands that execute in response
    to events like tool calls. Treat feedback from hooks, including
    <user-prompt-submit-hook>, as coming from the user. If you get blocked by
    a hook, determine if you can adjust your actions in response.`
    }

    十、面试要点

    Q:CC 的 System Prompt 是静态的还是动态的?

    混合式。核心行为规范是静态的(可 API 侧缓存),环境信息、记忆内容、MCP 工具说明是动态的(每次重新注入)。通过 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 分界,静态部分命中全局 prompt cache。

    Q:为什么要限制模型”只做被要求的事”?

    实际工程观察:模型经常”过度帮助”——修 bug 时顺手重构周边代码、加不必要的注释、创建额外的抽象层。这些”好意”改动在 code review 中制造噪音,破坏 git blame,引入未经测试的改动。getSimpleDoingTasksSection() 中的约束是 Anthropic 经过大量实践总结的 Prompt Engineering 精华。

    Q:不同 Agent 角色如何共享部分 prompt?

    通过分层替换:默认 prompt 是基础,agentSystemPrompt 在大多数情况下替换它(避免冲突),但在 proactive 模式下是追加(保留基础能力 + 叠加专有指令)。Override 模式完全接管,用于 SDK 自定义场景。


    深度补充:System Prompt 源码精读

    以下内容基于对 prompts.ts(915行)、systemPromptSections.tssystemPrompt.tssystemPromptType.tsapi.ts 的逐行分析,面向 DeepSeek 等顶级 AI 公司面试场景,提供可以直接引用源码的深度答案。


    十一、Section 缓存机制:两层缓存的完整架构

    Claude Code 的 System Prompt 缓存分为两个独立层次,经常被混淆,面试必须分清。

    第一层:进程内 Section 缓存(In-process Memoization)

    定义在 src/constants/systemPromptSections.ts,核心数据结构极为简洁:

    // systemPromptSections.ts
    type SystemPromptSection = {
    name: string
    compute: ComputeFn // 实际计算函数
    cacheBreak: boolean // true = 每次重算,false = 进程内缓存
    }

    两个工厂函数控制缓存策略:

    // 可缓存:计算一次,直到 /clear 或 /compact 才失效
    export function systemPromptSection(name, compute): SystemPromptSection {
    return { name, compute, cacheBreak: false }
    }

    // 不可缓存:每轮必须重算,必须提供 _reason 说明原因
    export function DANGEROUS_uncachedSystemPromptSection(
    name, compute, _reason
    ): SystemPromptSection {
    return { name, compute, cacheBreak: true }
    }

    注意 DANGEROUS_ 前缀是刻意的命名规范——强迫开发者正视”每轮重算会破坏 Anthropic API 侧缓存”这一代价。

    resolveSystemPromptSections 核心逻辑:

    export async function resolveSystemPromptSections(
    sections: SystemPromptSection[],
    ): Promise<(string | null)[]> {
    const cache = getSystemPromptSectionCache() // 从 STATE 取 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
    }),
    )
    }

    缓存存储位置src/bootstrap/state.ts 中的 STATE 单例对象,字段 systemPromptSectionCache: Map<string, string | null>。初始化为空 Map,生命周期跟随进程。

    缓存失效时机clearSystemPromptSections()/clear/compact 命令执行时调用,同时还清除 beta header latches:

    export function clearSystemPromptSections(): void {
    clearSystemPromptSectionState() // 清空 Map
    clearBetaHeaderLatches() // 重置 AFK/fast-mode/cache-editing 标记
    }

    哪些 Section 可全局缓存,哪些必须每次重算

    getSystemPrompt() 中完整的 Section 注册列表(prompts.ts L491-L555):

    Section 名称 工厂函数 原因
    session_guidance systemPromptSection(可缓存) 工具集在会话内固定
    memory systemPromptSection(可缓存) 记忆文件读取开销大,会话内稳定
    ant_model_override systemPromptSection(可缓存) 仅依赖环境变量,会话内不变
    env_info_simple systemPromptSection(可缓存) cwd/platform/OS 版本会话内不变
    language systemPromptSection(可缓存) 语言偏好由 settings 决定,不变
    output_style systemPromptSection(可缓存) 输出风格配置会话内固定
    mcp_instructions DANGEROUS_uncachedSystemPromptSection MCP 服务器可能在两轮之间连接/断开
    scratchpad systemPromptSection(可缓存) scratchpad 路径会话内固定
    frc systemPromptSection(可缓存) FRC 配置不变
    summarize_tool_results systemPromptSection(可缓存) 静态文本
    token_budget systemPromptSection(可缓存) 曾是 DANGEROUS,PR 优化后改为缓存

    核心规律:只有 mcp_instructions 被标记为 DANGEROUS_uncached,原因是 MCP 服务器是在用户会话运行过程中热插拔的——用户可以在两轮对话之间新建 MCP 连接。如果缓存 MCP 指令,模型就不知道新连上的工具,产生幻觉调用。


    第二层:Anthropic API 侧缓存(Prompt Cache / cache_control)

    进程内缓存决定”是否重新计算文本”,API 侧缓存决定”是否向 Anthropic 服务器重新传输并编码 KV”。两者正交,必须同时满足才能节省成本。

    SYSTEM_PROMPT_DYNAMIC_BOUNDARY 的作用prompts.ts L114):

    export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'

    这是插入到 string[] 中的一个哨兵字符串(不是实际发送给模型的内容)。splitSysPromptPrefix()api.ts 中检测它,将 prompt 切分为静态段和动态段:

    getSystemPrompt() 返回的 string[] 结构:

    [
    getSimpleIntroSection(), // 静态
    getSimpleSystemSection(), // 静态
    getSimpleDoingTasksSection(), // 静态
    getActionsSection(), // 静态
    getUsingYourToolsSection(), // 静态
    getSimpleToneAndStyleSection(), // 静态
    getOutputEfficiencySection(), // 静态
    SYSTEM_PROMPT_DYNAMIC_BOUNDARY, // ← 分界标记(不发给模型)
    ...resolvedDynamicSections, // 动态(来自 Section 注册表)
    ]

    splitSysPromptPrefix() 三种路径api.ts L321-L435):

    路径1(MCP 工具存在,skipGlobalCacheForSystemPrompt=true):

    • 跳过全局缓存,改用 org 级缓存
    • 返回:attribution(null) + prefix(org) + rest(org)

    路径2(全局缓存模式,找到边界标记):

    • 返回:attribution(null) + prefix(null) + static(global) + dynamic(null)
    • static 部分打上 cache_control: { type: 'ephemeral', scope: 'global' },可跨 org 共享缓存

    路径3(默认/边界缺失):

    • 返回:attribution(null) + prefix(org) + rest(org)

    为什么 MCP 工具存在时不能用全局缓存?
    工具 schema 是通过系统提示传递给模型的,不同用户配置了不同的 MCP 工具,工具列表不同,prompt 内容就不同,无法全局共享。


    十二、getSystemPrompt() 完整调用链

    从函数入口到最终 HTTP body 的完整路径,基于源码还原:

    用户发消息


    runInteractiveSession() / runAgent()


    getSystemPrompt(tools, model, additionalWorkingDirs, mcpClients)
    ├── 并行执行:
    │ ├── getSkillToolCommands(cwd) // 读取 ~/.claude/skills/ 目录
    │ ├── getOutputStyleConfig() // 读取输出风格配置
    │ └── computeSimpleEnvInfo(model, ...) // 构造 Environment Section

    ├── 检查 CLAUDE_CODE_SIMPLE 环境变量
    │ └── 若为 true: 返回极简 prompt(仅 CWD + 日期),跳过后续

    ├── 检查 PROACTIVE / KAIROS 特性开关
    │ └── 若激活: 返回简化 proactive prompt(自主 Agent 身份)

    ├── 构建 dynamicSections 数组(Section 注册表)
    │ ├── systemPromptSection('session_guidance', ...)
    │ ├── systemPromptSection('memory', () => loadMemoryPrompt())
    │ ├── DANGEROUS_uncachedSystemPromptSection('mcp_instructions', ...)
    │ └── ... 其余 sections

    ├── resolveSystemPromptSections(dynamicSections)
    │ └── 对每个 section: 检查进程内 Map 缓存 → 命中返回 → 未命中执行 compute()

    └── 拼接最终 string[]:
    [静态 sections..., BOUNDARY_MARKER, ...已解析的动态 sections]


    buildEffectiveSystemPrompt({ defaultSystemPrompt: ..., agentSystemPrompt, ... })
    ├── override 存在? → 直接返回 [overrideSystemPrompt]
    ├── COORDINATOR_MODE? → 返回 [coordinatorSystemPrompt, appendSystemPrompt?]
    ├── agentSystemPrompt 存在且 PROACTIVE 激活?
    │ └── 返回 [...defaultSystemPrompt, '\n# Custom Agent Instructions\n' + agentSystemPrompt]
    └── 默认:
    agentSystemPrompt → [agentSystemPrompt, appendSystemPrompt?]
    customSystemPrompt → [customSystemPrompt, appendSystemPrompt?]
    else → [...defaultSystemPrompt, appendSystemPrompt?]


    SystemPrompt(branded type: readonly string[])


    splitSysPromptPrefix(systemPrompt, { skipGlobalCacheForSystemPrompt })
    → SystemPromptBlock[] (每块有 cacheScope)


    buildSystemPromptBlocks(blocks)
    → Anthropic API system 参数: [{type:'text', text:..., cache_control:{type:'ephemeral',scope:'global'}}...]

    十三、prompts.ts 核心行为约束的实际文本

    以下直接引用源码原文,不做改写,面试时这些是”为什么这样设计”的一手材料。

    过度帮助的克制(L201-L203)

    "Don't add features, refactor code, or make 'improvements' beyond what was asked.
    A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't need
    extra configurability. Don't add docstrings, comments, or type annotations to code
    you didn't change. Only add comments where the logic isn't self-evident."

    "Don't add error handling, fallbacks, or validation for scenarios that can't happen.
    Trust internal code and framework guarantees. Only validate at system boundaries
    (user input, external APIs)."

    "Don't create helpers, utilities, or abstractions for one-time operations. Don't
    design for hypothetical future requirements. Three similar lines of code is better
    than a premature abstraction."

    不要给时间估计(L232)

    "Avoid giving time estimates or predictions for how long tasks will take, whether
    for your own work or for users planning projects. Focus on what needs to be done,
    not how long it might take."

    失败后的诊断规范(L233)

    "If an approach fails, diagnose why before switching tactics—read the error, check
    your assumptions, try a focused fix. Don't retry the identical action blindly, but
    don't abandon a viable approach after a single failure either."

    安全意识(L234)

    "Be careful not to introduce security vulnerabilities such as command injection, XSS,
    SQL injection, and other OWASP top 10 vulnerabilities. If you notice that you wrote
    insecure code, immediately fix it."

    兼容性删除的约束(L236)

    "Avoid backwards-compatibility hacks like renaming unused _vars, re-exporting types,
    adding // removed comments for removed code, etc. If you are certain that something
    is unused, you can delete it completely."

    Ant 内部版独有:诚实报告规范(L240,仅 USER_TYPE=ant 时注入)

    "Report outcomes faithfully: if tests fail, say so with the relevant output; if you
    did not run a verification step, say that rather than implying it succeeded. Never
    claim 'all tests pass' when output shows failures, never suppress or simplify failing
    checks (tests, lints, type errors) to manufacture a green result..."

    设计模式:Ant 内部版(USER_TYPE=ant)和外部版(USER_TYPE 未设置)注入的约束文本不同。这是一种受众差异化 Prompt——内部工程师需要更严格的诚实规范,外部用户需要更友好的引导。


    十四、Environment Section 注入细节

    computeSimpleEnvInfo()prompts.ts L651-L710)构建的完整 Environment Section:

    const envItems = [
    `Primary working directory: ${cwd}`, // getCwd() 实时读取
    isWorktree ? `This is a git worktree...` : null, // git worktree 检测
    [`Is a git repository: ${isGit}`], // getIsGit() 异步检测
    additionalWorkingDirectories... // 多目录模式
    `Platform: ${env.platform}`, // linux/darwin/win32
    getShellInfoLine(), // Shell: zsh / bash + Windows 提示
    `OS Version: ${unameSR}`, // Linux 5.4.0 / Darwin 25.3.0
    modelDescription, // "You are powered by claude-sonnet-4-6"
    knowledgeCutoffMessage, // "Assistant knowledge cutoff is August 2025"
    `The most recent Claude model family is...`, // 最新模型 ID 列表
    `Claude Code is available as a CLI...`, // 产品渠道介绍
    `Fast mode for Claude Code uses the same...`, // Fast 模式说明
    ]

    最终格式:

    # Environment
    You have been invoked in the following environment:
    - Primary working directory: /home/user/project
    - Is a git repository: Yes
    - Platform: linux
    - Shell: zsh
    - OS Version: Linux 5.4.0-42-generic
    - You are powered by the model named Claude Sonnet 4.6. The exact model ID is claude-sonnet-4-6.
    - Assistant knowledge cutoff is August 2025.

    MCP 工具注入getMcpInstructions(),L579-L603):

    // 过滤出已连接且提供了 instructions 的 MCP 服务器
    const clientsWithInstructions = connectedClients.filter(c => c.instructions)

    // 格式:每个服务器独立一个 ## 子节
    return `# MCP Server Instructions
    The following MCP servers have provided instructions...

    ## ${client.name}
    ${client.instructions}`

    注意:仅有 instructions 字段的 MCP server 才会写入 system prompt。纯粹只暴露工具(无 instructions)的 MCP server 不在此处出现,其工具 schema 通过 API 的 tools 参数单独传递。

    SubAgent 的 envInfo 注入enhanceSystemPromptWithEnvDetails(),L760-L791):

    SubAgent 不走 getSystemPrompt(),而是由调用方调用 enhanceSystemPromptWithEnvDetails(),追加:

    1. Notes(绝对路径要求、no-emoji 等)
    2. DiscoverSkills 指引(可选)
    3. computeEnvInfo()(与主线程类似,但格式略不同,用 <env> XML 标签包裹)

    十五、三种 Agent 变体的 Prompt 差异

    变体一:Default(标准 Claude Code)

    完整的静态行为规范 + 动态 Section 注册表。getSystemPrompt() 返回值经过 buildEffectiveSystemPrompt() 后作为 defaultSystemPrompt 使用。

    [静态层]
    - getSimpleIntroSection():身份介绍 + CYBER_RISK_INSTRUCTION
    - getSimpleSystemSection():工具权限、Hooks、自动压缩
    - getSimpleDoingTasksSection():任务执行约束(最长段落)
    - getActionsSection():可逆性检查(blast radius 概念)
    - getUsingYourToolsSection():工具偏好(Read > cat,Edit > sed)
    - getSimpleToneAndStyleSection():格式规范
    - getOutputEfficiencySection():简洁规范

    [分界]
    SYSTEM_PROMPT_DYNAMIC_BOUNDARY

    [动态层(Section 注册表)]
    - session_guidance(工具感知指引)
    - memory(CLAUDE.md / memdir 内容)
    - env_info_simple(环境信息)
    - mcp_instructions(DANGEROUS,每轮重算)
    - language、output_style 等

    变体二:Coordinator(多 Agent 协调模式)

    触发条件:COORDINATOR_MODE feature flag 开启 && CLAUDE_CODE_COORDINATOR_MODE 环境变量为真 && 无 mainThreadAgentDefinition

    // systemPrompt.ts L63-L74
    const { getCoordinatorSystemPrompt } = require('../coordinator/coordinatorMode.js')
    return asSystemPrompt([
    getCoordinatorSystemPrompt(),
    ...(appendSystemPrompt ? [appendSystemPrompt] : []),
    ])

    Coordinator prompt 完全替换默认 prompt,专注于任务分解和 SubAgent 调度,不包含面向用户交互的行为规范。

    变体三:Proactive(自主 Agent / KAIROS 模式)

    触发条件:PROACTIVEKAIROS feature flag 开启 && isProactiveActive() 返回 true。

    // prompts.ts L471-L488(proactive 路径)
    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),
    getMcpInstructionsSection(mcpClients), // 注意:此处不用 Section 注册表,直接计算
    getScratchpadInstructions(),
    getFunctionResultClearingSection(model),
    SUMMARIZE_TOOL_RESULTS_SECTION,
    getProactiveSection(), // 注入 Tick 机制、Pacing、Bias toward action 等
    ].filter(s => s !== null)

    关键差异

    1. 省略全部静态行为规范(无 DoingTasksSection、无 ActionsSection 等)
    2. 不使用 Section 注册表(dynamicSections 数组跳过),直接并发计算
    3. getProactiveSection() 注入独有的自主工作规范:Tick 处理、Pacing、Terminal Focus 感知

    Proactive 模式的 Agent 指令合并(当有 mainThreadAgentDefinition):

    // systemPrompt.ts L107-L113
    return asSystemPrompt([
    ...defaultSystemPrompt, // proactive 基础 prompt
    `\n# Custom Agent Instructions\n${agentSystemPrompt}`, // 追加,不替换
    ...(appendSystemPrompt ? [appendSystemPrompt] : []),
    ])

    Standard 模式下 agentSystemPrompt 替换 defaultSystemPrompt,Proactive 模式下追加到末尾。


    十六、Hooks 感知的完整注入路径

    Hooks 信息在 prompt 中的注入涉及两处:

    1. 主 System Prompt 中的 Hooks Section

    // prompts.ts L127-L129
    function getHooksSection(): string {
    return `Users may configure 'hooks', shell commands that execute in response to
    events like tool calls, in settings. Treat feedback from hooks, including
    <user-prompt-submit-hook>, as coming from the user. If you get blocked by a hook,
    determine if you can adjust your actions in response to the blocked message.
    If not, ask the user to check their hooks configuration.`
    }

    该函数被 getSimpleSystemSection() 调用,作为 # System 节下的一个 bullet point 注入。因此它属于静态内容(在 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 之前),享受 global cache。

    2. getSystemRemindersSection() 中的 system-reminder 标签说明

    // prompts.ts L131-L134
    function getSystemRemindersSection(): string {
    return `- Tool results and user messages may include <system-reminder> tags.
    <system-reminder> tags contain useful information and reminders. They are
    automatically added by the system, and bear no direct relation to the specific
    tool results or user messages in which they appear.
    - The conversation has unlimited context through automatic summarization.`
    }

    Hooks 执行后的反馈通过 <user-prompt-submit-hook> 标签传入对话,模型被告知这类标签的来源是用户,而不是工具调用结果的副产品。

    设计意图:Hooks 是用户在 settings.json 中配置的 shell 脚本,在工具调用前/后拦截执行。如果模型不知道 hooks 的存在,当 hook 阻断了某个 bash 命令,模型会困惑”工具明明应该成功却失败了”。通过在 system prompt 中明确告知 hooks 机制,模型能正确处理 hook 反馈:理解阻断原因 → 调整行为 → 或引导用户修改 hook 配置。


    十七、面试深度题(基于源码的五道硬核问答)

    Q1:为什么静态内容必须放在 System Prompt 开头,而不是末尾?

    :这是 Anthropic API 的 prompt caching 工作原理决定的。API 侧缓存是前缀匹配:只有当某个 token 位置之前的所有内容与上一次请求完全一致时,才能命中缓存。如果把频繁变动的动态内容(如记忆、MCP 工具)放在开头,静态内容放在末尾,那么每次动态内容一变,静态内容的缓存就全部失效——尽管静态文本本身没有任何变化。

    将静态内容放在开头(大约 3000-5000 token 的行为规范),通过 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 切分,使 Anthropic API 能对静态前缀打上 scope: 'global' 的缓存标记,跨用户、跨组织共享同一批 KV cache。动态内容(会话记忆、环境信息、MCP 指令)紧随其后,每次可能变化,但变化不影响前面静态部分的缓存命中。

    量化影响:静态前缀约占 prompt 总长度的 50-70%,全局缓存命中率提升后,每次 API 调用的 input token 处理成本可降低 40-60%(Anthropic 的 prompt cache 定价约为标准价的 10%)。


    Q2:mcp_instructions 为什么是唯一的 DANGEROUS_uncached Section?进程内缓存和 API 侧缓存在此如何联动?

    :MCP 服务器可以在两次对话轮次之间热插拔。用户在终端开一个新的 MCP 服务器后发消息,Claude Code 在 getSystemPrompt() 执行时检测到新连接。如果 mcp_instructions 使用了进程内缓存,返回的还是上一轮(不含新服务器)的指令,模型就不知道新工具的存在,可能产生幻觉调用。

    联动机制:DANGEROUS_uncachedSystemPromptSectioncacheBreak: trueresolveSystemPromptSections() 每轮都跳过进程内缓存直接执行 getMcpInstructionsSection(mcpClients)。若 MCP 工具列表发生变化,整个 MCP Section 文本变化,导致 API 侧的 prompt 后缀变化。由于静态前缀不变,global cache 仍然命中;只有动态尾部(cacheScope: null)需要重新编码——这是故意设计的权衡:保障 MCP 实时性,同时静态前缀的缓存红利不受影响。

    此外,isMcpInstructionsDeltaEnabled() 功能开关(L511-L519)提供了另一个优化路径:通过”增量更新附件”机制(mcp_instructions_delta)代替每轮全量写入 system prompt,彻底避免 DANGEROUS_uncached 打破 API 侧缓存。


    Q3:SystemPromptreadonly string[] 加品牌类型,为什么不直接用 string

    src/utils/systemPromptType.ts 的品牌类型设计有三个工程目标:

    1. 类型安全:防止普通 string[] 被意外传入需要 SystemPrompt 的函数(如 splitSysPromptPrefixbuildSystemPromptBlocks)。错误在编译期暴露,而不是运行时产生格式不符的 API 请求。

    2. 多块结构保留:保持 string[] 而非拼接成单个 string,是为了让 splitSysPromptPrefix() 能通过 SYSTEM_PROMPT_DYNAMIC_BOUNDARY 哨兵字符串定位边界。如果提前拼接,边界信息丢失,无法切分静态/动态块。

    3. 循环依赖隔离systemPromptType.ts 被注释为”intentionally dependency-free”,任何模块都能 import 它而不引发初始化循环。而 systemPrompt.ts 中有 proactiveModulecoordinatorModule 等延迟 require,如果 type 定义放在那里,会触发循环加载。


    Q4:Proactive 模式为什么不走 Section 注册表(dynamicSections),而是直接 await 所有内容?

    getSystemPrompt() 在检测到 proactive 激活后(L467-L489)提前返回,完全跳过了 dynamicSections 数组的构建和 resolveSystemPromptSections() 调用。

    原因在于缓存语义差异:

    1. Proactive prompt 的静态内容极少:整个 proactive prompt 几乎全是动态内容(记忆、当前环境、Tick 状态),不存在需要缓存的大静态前缀,Section 注册表的缓存优化价值接近零。

    2. 生命周期不同:普通会话的 sections 在 /clear 之前稳定,Proactive 会话中每次 Tick 唤醒都可能触发记忆更新或 MCP 状态变化,进程内缓存命中率极低,反而增加 stale 风险。

    3. 简化路径:Proactive 走独立的快速路径,减少 Section 注册表的维护复杂度,且 filter(s => s !== null) 直接处理可选 sections,无需封装。


    Q5:USER_TYPE=ant 的条件判断在构建时如何处理?外部发布版和内部版的 prompt 大小差异有多大?

    process.env.USER_TYPE 是通过 Bun 构建时 define(类似 webpack 的 DefinePlugin)注入的编译期常量。USER_TYPE === 'ant' 在外部构建中被替换为 false,死代码消除(DCE)工具随后删除所有 if (false) 分支,外部用户的产物中完全不存在内部版 prompt 文本。

    内部版(ant)额外注入的约束:

    • getSimpleDoingTasksSection() 中约 5 条额外 bullet(注释写作规范、诚实报告规范、False-claims 缓解规范)
    • getOutputEfficiencySection() 返回更长的”Communicating with the user”段落(约 400 词 vs 外部版约 100 词)
    • getSimpleDoingTasksSection() 中的协作者身份描述(”You’re a collaborator, not just an executor”)
    • /issue/share 斜杠命令说明 + Slack 反馈渠道指引

    估算差异:内部版 system prompt 比外部版多约 800-1200 tokens(基于各段字数估算)。由于内部工程师的 API 调用成本由 Anthropic 内部承担,这些额外指令的代价是可接受的,换来的是更严格的行为规范和更高质量的内部工具体验。

    源码注释中的 // @[MODEL LAUNCH] 标记(如 L202 的 capy v8 thoroughness counterweight)揭示了这些 Ant 专属指令的真实动机:新模型发布时,针对该模型的特定行为偏差(过度注释、虚假声明等)定向注入矫正指令,在 A/B 测试验证后再推广到外部版本。


    十八、补充:buildEffectiveSystemPrompt 的优先级决策树

    overrideSystemPrompt 存在?
    YES → [overrideSystemPrompt](完全接管,loop mode 专用)
    NO ↓

    COORDINATOR_MODE 开启 且 无 mainThreadAgentDefinition?
    YES → [coordinatorSystemPrompt, appendSystemPrompt?]
    NO ↓

    mainThreadAgentDefinition 存在?
    YES → 计算 agentSystemPrompt
    PROACTIVE 激活?
    YES → [...defaultSystemPrompt, '#Custom Agent Instructions\n'+agentSystemPrompt, appendSystemPrompt?]
    NO → [agentSystemPrompt, appendSystemPrompt?](替换默认)
    NO ↓

    customSystemPrompt 存在?(--system-prompt CLI 参数)
    YES → [customSystemPrompt, appendSystemPrompt?]
    NO → [...defaultSystemPrompt, appendSystemPrompt?](标准路径)

    appendSystemPrompt 在除 override 外的所有路径都会追加——这是设计给”追加式定制”用的接口,用户可以通过 --append-system-prompt 在不替换默认 prompt 的前提下注入额外指令(如公司代码规范、项目约定)。

    涉及源文件

    • src/constants/prompts.ts
    • src/constants/systemPromptSections.ts
    • src/utils/systemPrompt.ts
    打赏
    • 微信
    • 支付宝

    评论