跳转到内容

System Prompt 组装

system prompt 是每次 Claude Code API 调用中最关键的 context 部分。它定义了 Claude 的身份、能力、行为约束和环境感知。src/constants/prompts.ts 中的组装过程是一条精心设计的分层流水线,在 cache 效率与每 session 动态性之间取得平衡。

system prompt 由一个边界标记分为两个区域:

src/constants/prompts.ts
export const SYSTEM_PROMPT_DYNAMIC_BOUNDARY = '__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__'
graph TD
    subgraph "Static Zone (cross-org cacheable)"
        A[Identity & Introduction]
        B[System Information]
        C[Coding Instructions]
        D[Actions Section]
        E[Tool Usage Guide]
        F[Tone & Style]
        G[Output Efficiency]
    end
    H["__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__"]
    subgraph "Dynamic Zone (per-session)"
        I[Environment Info]
        J[Memory Prompt]
        K[Language Preference]
        L[Output Style Config]
        M[MCP Instructions]
        N[Scratchpad Instructions]
        O[Function Result Clearing]
        P[Token Budget]
    end
    A --> B --> C --> D --> E --> F --> G --> H --> I --> J --> K --> L --> M --> N --> O --> P

边界之前的所有内容可以使用 scope: 'global' cache——在所有用户和 session 中完全相同。边界之后的内容包含用户/session 特定信息,不得进行全局 cache。

主入口是 src/constants/prompts.ts 中的 getSystemPrompt()

// src/constants/prompts.ts — simplified structure
export async function getSystemPrompt(
tools: Tools,
model: string,
additionalWorkingDirectories?: string[],
mcpClients?: MCPServerConnection[],
): Promise<string[]> {
// Early exit for --bare / SIMPLE mode
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
return [`You are Claude Code...\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`]
}
// Early exit for proactive/autonomous mode
if (proactiveModule?.isProactiveActive()) {
return [/* minimal autonomous prompt */]
}
// Standard prompt: static sections + dynamic sections
const dynamicSections = [
systemPromptSection('env_info', () => envInfo),
systemPromptSection('memory', () => loadMemoryPrompt()),
systemPromptSection('language', () => getLanguageSection(settings.language)),
systemPromptSection('output_style', () => getOutputStyleSection(outputStyleConfig)),
DANGEROUS_uncachedSystemPromptSection('mcp_instructions', ...),
systemPromptSection('scratchpad', () => getScratchpadInstructions()),
systemPromptSection('frc', () => getFunctionResultClearingSection(model)),
// ...
]
return [
// --- Static content (cacheable) ---
getSimpleIntroSection(outputStyleConfig),
getSimpleSystemSection(),
getSimpleDoingTasksSection(),
getActionsSection(),
getUsingYourToolsSection(enabledTools),
getSimpleToneAndStyleSection(),
getOutputEfficiencySection(),
// === BOUNDARY MARKER ===
...(shouldUseGlobalCacheScope() ? [SYSTEM_PROMPT_DYNAMIC_BOUNDARY] : []),
// --- Dynamic content ---
...resolvedDynamicSections,
].filter(s => s !== null)
}

该函数返回字符串数组(各分区),而非单一字符串。这样可以将边界标记保留给下游 cache 逻辑使用。

function getSimpleIntroSection(outputStyleConfig: OutputStyleConfig | null): string {
// "You are Claude Code, Anthropic's official CLI for Claude..."
// Core identity, tool-using mandate, human-in-the-loop principle
}
function getSimpleDoingTasksSection(): string {
// File editing rules, test/lint requirements
// "When editing files: use Read before Edit"
// "When writing tests: follow existing patterns"
}
function getUsingYourToolsSection(enabledTools: Set<string>): string {
// Conditional sections based on which tools are available
// Includes explore agent usage tips when enabled
// Includes fork agent instructions when FORK_SUBAGENT is active
}

两个变体——ant 内部版(更长,包含更细致的沟通准则)和外部版(简洁的”直入主题”指令):

function getOutputEfficiencySection(): string {
if (process.env.USER_TYPE === 'ant') {
return `# Communicating with the user
When sending user-facing text, you're writing for a person, not logging to a console...`
}
return `# Output efficiency
IMPORTANT: Go straight to the point. Try the simplest approach first...`
}

动态分区通过注册系统(systemPromptSections.ts)管理:

// Two types of dynamic sections
systemPromptSection('memory', () => loadMemoryPrompt())
// ↑ Cached by the section registry — value stabilizes within a session
DANGEROUS_uncachedSystemPromptSection('mcp_instructions', () => getMcpInstructionsSection(...))
// ↑ Recomputed every turn — changes between turns (e.g., MCP connect/disconnect)
// Includes: CWD, date/time, git status, OS, model name
async function computeEnvInfo(
modelId: string,
additionalWorkingDirectories?: string[],
): Promise<string> {
const [isGit, unameSR] = await Promise.all([getIsGit(), getUnameSR()])
// Builds environment string with all runtime context
}

从自动 memory 系统(src/memdir/memdir.ts)加载。memory prompt 分区包含 MEMORY.md 内容、team memory 以及保存/调取指令。

function getLanguageSection(language?: string): string | null {
if (!language) return null
return `IMPORTANT: Always respond in ${language}.`
}

当 MCP server 通过 client.instructions 提供自定义指令时,这些指令会被注入到 system prompt 中:

function getMcpInstructions(mcpClients: MCPServerConnection[]): string | null {
const clientsWithInstructions = connectedClients.filter(c => c.instructions)
// Returns "# MCP Server Instructions\n## server-name\n{instructions}"
}

将 system prompt 精简至最小——仅保留身份、CWD 和日期:

if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
return [`You are Claude Code...\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`]
}

自主 agent 使用截然不同的 prompt,专注于持续的自我驱动工作,而非与人交互:

if (proactiveModule?.isProactiveActive()) {
return [
`You are an autonomous agent. Use the available tools to do useful work.`,
getSystemRemindersSection(),
await loadMemoryPrompt(),
envInfo,
// ... minimal sections
]
}

coordinator agent 使用 src/coordinator/coordinatorMode.ts 中的 getCoordinatorSystemPrompt()——一个完全独立的约 370 行 prompt,定义了编排协议(参见 Coordinator 模式)。

system prompt 占据了 context window 的相当大比例。关键大小决策:

分区相对大小说明
编码指令完整的文件编辑协议
输出效率ant 版本更长
tool 使用指南可变随启用的 tool 数量增长
memory有上限MEMORY.md:200 行,25KB 上限
MCP 指令可变取决于已连接的 server
环境信息约 200 字符

双区拆分确保最大的分区(编码指令、tool 使用)位于静态区并被全局 cache,而较小的每 session 分区则占据动态区。