시스템 프롬프트 구성

정적 섹션, 동적 섹션, CLAUDE.md 주입, 캐시 경계, 모드 오버라이드까지 — 시스템 프롬프트 조립의 모든 것.

01

개요

시스템 프롬프트는 Claude Code의 행동을 정의하는 핵심 구성 요소입니다. 6개 레이어 아키텍처로 조립됩니다:

02

우선순위 해석기

buildEffectiveSystemPrompt()는 폭포수(waterfall) 패턴으로 시스템 프롬프트의 소스를 결정합니다:

  1. override — 최고 우선순위. overrideSystemPrompt이 설정되면 appendSystemPrompt을 포함한 모든 것을 대체
  2. coordinator — 코디네이터 에이전트가 하위 에이전트에게 전달하는 프롬프트. 이 분기에서도 appendSystemPrompt는 유지됨
  3. agent — 에이전트 모드 전용 프롬프트 (proactive/KAIROS 모드 등)
  4. custom — 사용자 정의 시스템 프롬프트
  5. default — 표준 대화형 프롬프트

appendSystemPromptoverride를 제외한 모든 분기에 추가됩니다. override가 설정되면 완전히 독립적인 프롬프트로 동작합니다.

// utils/systemPrompt.ts — 우선순위 폭포수
if (overrideSystemPrompt) {
  return asSystemPrompt([overrideSystemPrompt])
}
if (isCoordinatorMode()) {
  return asSystemPrompt([
    getCoordinatorSystemPrompt(),
    ...append,
  ])
}
if (agentSystemPrompt) {
  return asSystemPrompt([...defaultSystemPrompt, agentSystemPrompt, ...append])
}
if (customSystemPrompt) {
  return asSystemPrompt([customSystemPrompt, ...append])
}
return asSystemPrompt([...defaultSystemPrompt, ...append])
주의사항

overrideSystemPrompt 설정 시 appendSystemPrompt도 무시됩니다. 기본 프롬프트를 대체하되 추가 지시를 유지하고 싶다면 custom + appendSystemPrompt 조합을 사용해야 합니다.

03

콘텐츠 팩토리

getSystemPrompt()는 두 영역으로 나뉜 시스템 프롬프트를 생성합니다:

정적 섹션 (캐시 경계 앞):

__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__

동적 섹션 (캐시 경계 뒤): 세션마다 달라지는 내용. CLAUDE.md, MCP 지시, 메모리, env_info_simple 같은 등록형 섹션이 여기 들어갑니다.

// constants/prompts.ts — 조립 (간소화)
export async function getSystemPrompt(tools, model, additionalDirs, mcpClients) {
  const [skillCmds, outputStyle] = await Promise.all([
    getSkillToolCommands(cwd),
    getOutputStyleConfig(),
  ])

  return [
    // ── 정적 (전역 캐시 가능) ──
    getSimpleIntroSection(outputStyle),
    getSimpleSystemSection(),
    getSimpleDoingTasksSection(),
    getActionsSection(),
    getUsingYourToolsSection(enabledTools),
    getSimpleToneAndStyleSection(),
    getOutputEfficiencySection(),
    // ── 경계 마커 ──
    SYSTEM_PROMPT_DYNAMIC_BOUNDARY,
    // ── 동적 (세션별, 레지스트리에서 resolve) ──
    ...resolvedDynamicSections,
  ].filter(s => s !== null)
}
04

동적 섹션 레지스트리

동적 섹션은 두 가지 등록 방식을 제공합니다:

// systemPromptSections.ts — 메모이즈 vs 휘발성
export function systemPromptSection(name, compute) {
  return { name, compute, cacheBreak: false }  // 메모이즈
}

export function DANGEROUS_uncachedSystemPromptSection(name, compute, reason) {
  return { name, compute, cacheBreak: true }   // 매 턴 재계산
}
딥 다이브 — mcp_instructions만 휘발성인 이유

MCP(Model Context Protocol) 서버는 세션 도중에 연결되거나 해제될 수 있습니다. 사용자가 새로운 MCP 서버를 추가하면 해당 서버의 지시사항이 시스템 프롬프트에 포함되어야 하고, 서버가 해제되면 제거되어야 합니다.

만약 mcp_instructions를 메모이즈하면, 세션 시작 시점의 MCP 서버 상태가 세션 내내 유지되어 새로 연결된 서버의 지시가 무시되거나, 해제된 서버의 지시가 남게 됩니다. 따라서 매 API 호출마다 현재 연결된 MCP 서버에서 지시사항을 다시 수집합니다.

05

캐시 경계

__SYSTEM_PROMPT_DYNAMIC_BOUNDARY__는 시스템 프롬프트를 두 영역으로 분리합니다:

주의사항 — 캐시 접두사 폭발

캐시 경계 앞에 런타임 조건식을 추가하면 캐시 접두사 해시가 2조건 수배로 늘어납니다. 예를 들어, 정적 섹션에 3개의 if 분기를 추가하면 최대 8가지 서로 다른 캐시 접두사가 생성되어 캐시 적중률이 급락합니다.

새로운 조건부 내용은 반드시 동적 경계 뒤에 배치해야 합니다.

06

CLAUDE.md 주입

CLAUDE.md는 프로젝트별 지시사항을 시스템 프롬프트에 주입하는 메커니즘입니다. 4가지 범위로 로드됩니다:

  1. managed — Anthropic이 관리하는 기본 지시 (최저 효력)
  2. user~/.claude/CLAUDE.md 사용자 전역 설정
  3. project — 프로젝트 루트부터 CWD까지 디렉토리 트리를 상향 탐색하며 발견되는 CLAUDE.md
  4. localCLAUDE.local.md — git에 커밋되지 않는 개인 지시. CWD에 가장 가까운 파일이 최고 효력
// CLAUDE.md 로드 순서
// 1. 관리 메모리 (/etc/claude-code/CLAUDE.md)   — 모든 사용자 전역
// 2. 사용자 메모리 (~/.claude/CLAUDE.md)          — 개인 전역
// 3. 프로젝트 메모리 (CLAUDE.md / .claude/CLAUDE.md — 코드베이스에 커밋
//                     .claude/rules/*.md)
// 4. 로컬 메모리   (CLAUDE.local.md)              — 개인 프로젝트별

추가 기능:

딥 다이브 — proactive/KAIROS 모드의 시스템 프롬프트

proactive(KAIROS) 모드가 활성화되면 시스템 프롬프트 구성이 근본적으로 달라집니다. 표준 대화형 프롬프트(Intro, System, DoingTasks, ...) 전체를 대신하여 짧은 자율 에이전트 정체성 프롬프트를 반환합니다.

대화형 모드에서는 사용자 질문에 응답하는 어시스턴트 역할이지만, KAIROS 모드에서는 자율적으로 계획하고 실행하는 에이전트 역할입니다. 이 근본적인 역할 차이 때문에 기존 프롬프트에 추가하는 것이 아니라 완전히 대체합니다.

07

환경 정보

env_info_simple는 단순한 인라인 문자열이 아니라, 동적 섹션 레지스트리에 등록된 섹션입니다. 각 요청에서 computeSimpleEnvInfo()가 현재 런타임 컨텍스트를 계산해 채우며, 그래서 캐시 경계 뒤에 위치합니다.

// constants/prompts.ts — computeSimpleEnvInfo() 출력 예시
// # Environment
// You have been invoked in the following environment:
//  - Primary working directory: /Users/alice/myproject
//  - Is a git repository: true
//  - Platform: darwin
//  - Shell: zsh
//  - OS Version: Darwin 25.3.0
//  - You are powered by the model named Claude Sonnet 4.6.
//  - Assistant knowledge cutoff is August 2025.
//  - Claude Code is available as a CLI in the terminal, desktop app ...

수집되는 정보 항목:

셸 감지는 process.env.SHELL 경로에서 마지막 구성 요소를 추출합니다. Windows에서는 Unix 셸 구문을 선호하라는 노트(/dev/null 사용, 경로에 슬래시 사용 등)가 추가됩니다. 지식 컷오프 날짜는 모델 정규 이름별로 getKnowledgeCutoff()에 하드코딩되어 있으며, 매 모델 출시마다 // @[MODEL LAUNCH] 업데이트가 필요합니다.

// constants/prompts.ts — shell detection and Windows note
const shellName = process.env.SHELL?.split('/').pop() ?? 'unknown'
const windowsNote = process.platform === 'win32'
  ? `\nNote: prefer Unix shell syntax (use /dev/null not NUL, forward slashes in paths).`
  : ''
딥 다이브 -- 언더커버 모드

Anthropic 엔지니어가 isUndercover() 활성 상태에서 내부 빌드를 실행할 때, 모든 모델 이름 참조가 환경 섹션에서 제거되어 내부 모델 ID가 공개 커밋, PR, 스크린샷 등으로 누출되는 것을 방지합니다.

11

MCP 서버 지시문

MCP 서버가 연결되면, getMcpInstructions()ConnectedMCPServer 객체를 순회하며 instructions 필드를 노출하는 서버의 지시사항 블록을 조립합니다.

// constants/prompts.ts — getMcpInstructions()
function getMcpInstructions(mcpClients) {
  const withInstructions = mcpClients
    .filter(c => c.type === 'connected')
    .filter(c => c.instructions)

  const blocks = withInstructions
    .map(c => `## ${c.name}\n${c.instructions}`)
    .join('\n\n')

  return `# MCP Server Instructions\n\n...\n\n${blocks}`
}

이 함수가 시스템 프롬프트에 "IMPORTANT: Before using any chrome browser tools, you MUST first load them using ToolSearch" 같은 텍스트를 주입합니다. 이는 Claude Code 자체 코드가 아닌 MCP 서버의 instructions 필드에서 그대로 전달되는 내용입니다.

캐싱 전략: mcp_instructions는 유일하게 DANGEROUS_uncachedSystemPromptSection으로 등록되어 매 API 호출마다 재평가됩니다. MCP 서버가 세션 도중 연결/해제될 수 있으므로, 캐시하면 낡은 데이터를 반환할 위험이 있기 때문입니다.

실험적 경로인 mcpInstructionsDelta 기능도 존재합니다. 이 기능이 활성화되면 지시사항이 매 턴 시스템 프롬프트에 재주입되는 대신 영속 첨부(persisted attachment) 객체로 전달됩니다. 이를 통해 늦게 연결되는 MCP 서버가 새 해시를 강제할 때 발생하는 프롬프트 캐시 무효화를 방지합니다.

09

서브에이전트 강화

AgentTool이 실행하는 서브에이전트는 getSystemPrompt()완전히 우회합니다. 호출자가 전달하는 시스템 프롬프트(보통 DEFAULT_AGENT_PROMPT)에서 시작한 후, enhanceSystemPromptWithEnvDetails()가 환경 컨텍스트와 에이전트별 주의사항을 추가합니다.

// The agent default prompt — what every subagent starts with
export const DEFAULT_AGENT_PROMPT = `You are an agent for Claude Code, Anthropic's official CLI for Claude. Given the user's message, you should use the tools available to complete the task. Complete the task fully—don't gold-plate, but don't leave it half-done. When you complete the task, respond with a concise report covering what was done and any key findings — the caller will relay this to the user, so it only needs the essentials.`

// Notes appended by enhanceSystemPromptWithEnvDetails()
`Notes:
- Agent threads always have their cwd reset between bash calls — use absolute file paths.
- In your final response, share file paths (always absolute, never relative) ...
- For clear communication the assistant MUST avoid using emojis.
- Do not use a colon before tool calls.`

서브에이전트 경로의 핵심 차이점:

10

Simple 모드 & 이스케이프 해치

CLAUDE_CODE_SIMPLE=1

이 환경 변수를 설정하면, 시스템 프롬프트가 CWD와 날짜만 포함하는 3줄 스텁으로 축소됩니다. 지시사항, CLAUDE.md, 도구 가이드 없이 -- 원시 모델 능력을 벤치마킹할 때 Claude Code의 프롬프트 오버헤드를 제거하는 용도입니다.

// constants/prompts.ts — CLAUDE_CODE_SIMPLE escape hatch
if (isEnvTruthy(process.env.CLAUDE_CODE_SIMPLE)) {
  return [`You are Claude Code, Anthropic's official CLI for Claude.\n\nCWD: ${getCwd()}\nDate: ${getSessionStartDate()}`]
}

Proactive / KAIROS 모드

feature('PROACTIVE') 또는 feature('KAIROS')가 활성화되고 proactive 모드가 동작 중이면, getSystemPrompt()는 전체 인터랙티브 세션 프롬프트 대신 짧은 자율 에이전트 정체성 프롬프트를 반환합니다. Memory, 환경 정보, MCP 지시사항, proactive 섹션은 포함하되, 인터랙티브 코딩 태스크 가이드는 제외합니다.

// constants/prompts.ts — proactive/KAIROS mode prompt assembly
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),
  getScratchpadInstructions(),
  getProactiveSection(),
].filter(s => s !== null)

에이전트에 proactive 모드가 결합된 경우, 에이전트 지시사항은 기본 프롬프트를 대체하는 것이 아니라 추가됩니다. 이는 "팀원" 패턴을 반영합니다: proactive 기본값은 이미 간결한 자율 에이전트 정체성이고, 도메인 에이전트가 그 위에 추가됩니다.

// utils/systemPrompt.ts — proactive mode branch
if (agentSystemPrompt && (feature('PROACTIVE') || feature('KAIROS')) && isProactiveActive()) {
  return asSystemPrompt([
    ...defaultSystemPrompt,
    `\n# Custom Agent Instructions\n${agentSystemPrompt}`,
    ...(appendSystemPrompt ? [appendSystemPrompt] : []),
  ])
}
11

핵심 요약

핵심 포인트

  • 시스템 프롬프트는 6개 레이어로 조립됩니다: 우선순위 해석기, 콘텐츠 팩토리, 섹션 레지스트리, CLAUDE.md 로더, 메모리 시스템, 캐시 경계
  • buildEffectiveSystemPrompt()는 override > coordinator > agent > custom > default 폭포수로 소스를 결정합니다
  • overrideSystemPrompt 설정 시 appendSystemPrompt를 포함한 모든 것이 대체됩니다
  • __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__가 전역 캐시 가능 정적 접두사와 세션별 동적 꼬리를 분리합니다
  • 캐시 경계 앞의 런타임 조건식은 캐시 접두사 해시를 2조건 수배로 늘려 캐시 적중률을 떨어뜨립니다
  • mcp_instructionsDANGEROUS_uncached인 이유는 MCP 서버가 세션 도중 연결/해제될 수 있기 때문입니다
  • CLAUDE.md는 managed > user > project > local 4가지 범위로 로드되며, CWD에 가장 가까운 CLAUDE.local.md가 최고 효력입니다
  • proactive/KAIROS 모드는 전체 대화형 프롬프트 대신 짧은 자율 에이전트 정체성 프롬프트를 반환합니다
12

지식 확인

퀴즈 — 5문제

Q1. overrideSystemPrompt이 설정되면 어떻게 되는가?

  • A) 기본 프롬프트에 추가됨
  • B) appendSystemPrompt를 포함한 모든 것이 대체됨
  • C) 기본 프롬프트를 대체하되 appendSystemPrompt는 추가됨
  • D) custom 설정 시 무시됨
overrideSystemPrompt은 최고 우선순위입니다. 설정되면 폭포수의 첫 번째 분기에서 즉시 반환하므로 appendSystemPrompt를 포함한 다른 모든 소스가 무시됩니다.

Q2. mcp_instructionsDANGEROUS_uncached인 이유는?

  • A) 크기가 너무 크기 때문
  • B) MCP 서버가 턴 간에 연결/해제될 수 있으므로 캐시하면 낡은 데이터를 반환
  • C) API가 캐싱을 지원하지 않기 때문
  • D) 도구 설명으로 주입되기 때문
MCP 서버는 세션 도중 동적으로 연결/해제됩니다. 메모이즈하면 세션 시작 시점의 서버 상태가 고정되어 새로 연결된 서버의 지시가 무시되거나 해제된 서버의 지시가 남게 됩니다.

Q3. CLAUDE.md 로딩 우선순위에서 최고 효력을 가진 파일은?

  • A) managed CLAUDE.md
  • B) user CLAUDE.md (~/.claude/CLAUDE.md)
  • C) CWD에 가장 가까운 CLAUDE.local.md
  • D) 파일 시스템 루트의 CLAUDE.md
4가지 범위 중 local 범위의 CLAUDE.local.md가 최고 효력을 가지며, 그 중에서도 CWD에 가장 가까운 파일이 우선합니다. git에 커밋되지 않는 개인 지시용으로 설계되었습니다.

Q4. __SYSTEM_PROMPT_DYNAMIC_BOUNDARY__의 목적은?

  • A) 사용자 콘텐츠와 내장 콘텐츠를 분리
  • B) /compact 명령의 잘라내기 표시
  • C) 전역 캐시 가능 정적 접두사와 세션별 동적 꼬리를 분리
  • D) 계획 모드에서 실행 모드로의 전환 신호
경계 앞의 정적 섹션은 모든 세션에서 동일하므로 Anthropic API 프롬프트 캐싱으로 전역 캐시가 가능합니다. 경계 뒤의 동적 섹션은 세션마다 달라지므로 캐시에서 제외됩니다.

Q5. proactive/KAIROS 모드가 시스템 프롬프트를 변경하는 방법은?

  • A) 표준 프롬프트에 추가 지시를 덧붙임
  • B) 전체 대화형 프롬프트 대신 짧은 자율 에이전트 정체성 프롬프트를 반환
  • C) 코디네이터 프롬프트를 사용
  • D) CLAUDE.md 주입을 비활성화
KAIROS 모드는 대화형 어시스턴트가 아닌 자율 에이전트로 동작합니다. 기존 프롬프트(Intro, System, DoingTasks 등) 전체를 대체하여 자율적 계획 및 실행에 최적화된 짧은 프롬프트를 반환합니다.
0 / 5