에이전트 시스템 프롬프트 작성 완벽 가이드 — Claude 코드 리버스 엔지니어링에서 배운 점
Claude Code의 시스템 프롬프트를 디컴파일하고, DeepAgents의 소스 코드를 분석해 밑바닥부터 나만의 AI 에이전트를 직접 만들었습니다. 시중의 프롬프트 가이드들은 대부분 뜬구름 잡는 소리에 불과하더군요.

지금 AI 업계에는 집단적인 착각이 일어나고 있습니다.
모든 튜토리얼이 시스템 프롬프트를 마치 마법 주문을 외우듯 작성하라고 가르칩니다. 적절한 주문만 찾으면 모델이 복종할 것이라는 식이죠. "당신은 20년 경력의 엄청난 재능을 가진 시니어 엔지니어입니다..." 익숙하신가요?
저는 지난 몇 달 동안 심층적인 시장 조사를 수행하고 VC 수준의 분석을 생성하는 AI 스타트업 어드바이저인 VibeCom을 빌딩해왔습니다. 그 과정에서 Claude Code의 시스템 프롬프트를 리버스 엔지니어링하고, DeepAgents의 미들웨어 소스를 뜯어보고, 차마 입에 담기 부끄러울 만큼 많은 API 크레딧을 태웠습니다. 거기서 얻은 가장 큰 교훈이요? 사람들이 시스템 프롬프트에서 중요하다고 생각하는 것들 대부분은 사실 중요하지 않다는 겁니다. 그리고 진짜 중요한 것들에 대해서는 아무도 이야기하지 않죠.
이 글은 완벽한 플레이북입니다. 5분짜리 요약본이 아니라, 제가 이 여정을 시작하기 전에 누군가 제게 꼭 말해주었으면 했던 모든 것을 담았습니다. 커피 한 잔 준비하시고, 시작해볼까요.
1. 설계 철학: 모델을 믿으세요
"에이전트는 모델입니다. 프레임워크도, 프롬프트 체인도 아닙니다." — shareAI-lab/learn-claude-code
이 생각은 제 모든 것을 바꿔놓았습니다. LLM은 이미 추론하고, 계획하고, 실행하는 방법을 알고 있습니다. 시스템 프롬프트는 모델에게 생각하는 법을 가르치는 것이 아닙니다. 모델이 일할 수 있는 '환경'을 설정해주는 것이죠.
시니어 엔지니어를 채용한다고 생각해보세요. 모든 작업에 대해 20단계짜리 체크리스트를 쥐여주진 않잖아요. 대신 이렇게 말하죠. "우리는 이런 팀이고, 지켜야 할 선은 여기까지며, 우리가 생각하는 '성공적인 결과물'은 이런 모습입니다." 그리고는 알아서 일하게 둡니다.
시스템 프롬프트의 역할은 정확히 네 가지뿐입니다:
- 정체성 부여 — 역할과 정체성
- 경계 설정 — 안전 제약 조건
- 품질 기준 제시 — 좋은 결과물의 정의
- 도구 제공 — 기능과 지식
이게 전부입니다. 나머지는 다 노이즈일 뿐이죠.
하네스(Harness) 마인드셋
Harness = Tools + Knowledge + Observation + Action Interfaces + Permissions
(하네스 = 도구 + 지식 + 관찰 + 액션 인터페이스 + 권한)
시스템 프롬프트는 이 하네스(제어 환경)의 운영 매뉴얼입니다. 경직된 파이프라인을 설계하는 것이 아니라, 모델이 자율적으로 최고의 성과를 낼 수 있는 환경을 설계하는 것입니다.
시스템 프롬프트를 순서도처럼 쓰지 마세요. 실행 순서는 모델이 스스로 결정할 겁니다.
2. 프롬프트 구조와 섹션 순서
권장 레이아웃 (Claude Code v2.0.14 리버스 엔지니어링 기반)
┌─────────────────────────────────────────────┐
│ 1. Identity (정체성) │ ← 가장 먼저 읽히며, 행동의 기준점이 됨
│ 2. Security & Safety (보안 및 안전) │ ← 중요한 마커, 타협 불가
│ 3. Tone & Style (톤 앤 매너) │ ← 출력 형식 제어
│ 4. Core Workflow (핵심 워크플로우) │ ← 작업 수행 방식
│ 5. Tool Usage Policy (도구 사용 정책) │ ← 도구 선택 우선순위
│ 6. Domain Knowledge (도메인 지식) │ ← 사전 로드가 아닌 온디맨드 방식
│ 7. Environment Info (환경 정보) │ ← 런타임 컨텍스트, 동적으로 주입됨
│ 8. Reminders (리마인더) │ ← 핵심 규칙 재강조
├─────────────────────────────────────────────┤
│ [Tool Definitions — 시스템 주입 도구 정의] │ ← 수정 불가, 보통 매우 김
├─────────────────────────────────────────────┤
│ [User Message — 사용자 메시지] │
└─────────────────────────────────────────────┘
이 순서가 중요한 이유
LLM은 U자형 주의력 곡선(U-shaped attention curve)을 가집니다. 프롬프트의 처음과 끝에 가장 많은 주의를 기울이고, 중간 부분에서는 집중력이 떨어집니다. 이는 잘 알려진 "Lost in the Middle(중간 누락)" 현상입니다.
- 최상단의 정체성 + 안전: 모델이 역할과 경계를 가장 먼저 확립합니다 (초두 효과).
- 중상단의 핵심 워크플로우: 가장 중요한 섹션입니다. 에이전트가 작업하는 방식을 정의합니다.
- 프롬프트 뒤에 시스템이 주입하는 도구 정의: Claude Code의 도구 정의는 약 11,438 토큰을 차지합니다. 즉, 여러분이 작성한 커스텀 프롬프트는 생각보다 '처음'에 가깝게 위치하게 되며, 이는 지시사항 준수율을 높이는 데 도움이 됩니다.
- 최하단의 리마인더: 최신 효과(Recency bias)를 활용해 핵심 규칙을 다시 한 번 각인시킵니다.
3. 각 섹션 작성법
3.1 정체성 (Identity) — 이 에이전트는 누구인가?
목표: 1~3문장으로 모델의 역할을 확실히 고정합니다.
You are Claude Code, Anthropic's official CLI for Claude.
You are an interactive agent that helps users with software engineering tasks.
(당신은 Anthropic의 공식 Claude CLI인 Claude Code입니다.
당신은 사용자의 소프트웨어 엔지니어링 작업을 돕는 대화형 에이전트입니다.)
가이드라인:
- 간결하게 유지하세요 — 최대 1~3문장.
- 역할을 명시적으로 지정하세요 (모델이 컨텍스트를 구분하는 데 도움이 됩니다).
- 모호하게 "당신은 유용한 어시스턴트입니다"라고 하지 말고, 핵심 책임("X를 돕습니다")을 명시하세요.
- 해당되는 경우 SDK/플랫폼을 언급하세요 ("Anthropic의 Claude Agent SDK 기반으로 구축됨").
안티 패턴:
- "당신은 유용하고, 무해하며, 정직한 AI 어시스턴트입니다" — 너무 일반적이고 역할의 기준점이 없습니다.
- 배경 스토리와 세계관으로 꽉 찬 문단 — 토큰 낭비입니다. 모델에게는 캐릭터 설정이 필요하지 않습니다.
3.2 보안 및 안전 (Security & Safety) — 절대적인 경계선
목표: 절대 깨져서는 안 될 행동 제약을 설정합니다.
IMPORTANT: Assist with defensive security tasks only.
Refuse to create, modify, or improve code that may be used maliciously.
IMPORTANT: You must NEVER generate or guess URLs for the user.
(중요: 방어적인 보안 작업만 지원하세요.
악의적으로 사용될 수 있는 코드를 생성, 수정 또는 개선하는 것을 거부하세요.
중요: 사용자를 위해 URL을 생성하거나 추측해서는 절대 안 됩니다.)
가이드라인:
IMPORTANT:접두사를 사용하세요 — Claude의 지시사항 계층 구조 학습은 이에 특별한 가중치를 부여합니다.- 절대적인 언어를 사용하세요:
NEVER(절대 ~않다),MUST NOT(~해서는 안 된다),Refuse to(~를 거부하다). - 허용되는 것과 금지되는 것을 모두 명시하세요 (양방향 제약이 더 명확합니다).
- 중간에 숨기지 말고 맨 위에 배치하세요.
- 핵심 안전 규칙은 마지막에 반복하세요 — Claude Code가 정확히 이렇게 합니다.
왜 반복할까요? 초두 효과(처음) + 최신 효과(끝) = 이중 강화. Claude Code의 보안 선언은 프롬프트의 시작과 끝 양쪽에 등장합니다. 엔지니어들이 깜빡해서가 아닙니다. U자형 주의력 곡선을 정확히 이해하고 있기 때문이죠.
3.3 톤 앤 매너 (Tone & Style) — 출력 제어하기
목표: 출력 형식과 목소리를 제어합니다.
## Tone and style
- Your responses should be short and concise.
- Only use emojis if the user explicitly requests it.
- Use Github-flavored markdown for formatting.
- NEVER create files unless absolutely necessary.
(## 톤 앤 스타일
- 답변은 짧고 간결해야 합니다.
- 사용자가 명시적으로 요청한 경우에만 이모지를 사용하세요.
- 포맷팅에는 Github 스타일 마크다운을 사용하세요.
- 절대적으로 필요한 경우가 아니면 파일을 생성하지 마세요.)
가이드라인:
- 모호하게 "전문적으로 행동하세요"라고 하지 말고 구체적인 행동을 나열하세요.
- 모든 규칙은 참/거짓으로 테스트할 수 있어야 합니다 ("짧고 간결하게" vs "최대한 짧게 노력할 것").
- 출력 형식 요구사항을 포함하세요 (마크다운? JSON? 일반 텍스트?).
- 하지 말아야 할 것도 포함하세요 — 많은 스타일 문제는 행동을 금지함으로써 해결됩니다.
Claude Code의 숨은 보석 — 전문적 객관성(Professional Objectivity):
Prioritize technical accuracy and truthfulness over validating the user's beliefs.
Focus on facts and problem-solving, providing direct, objective technical info
without any unnecessary superlatives, praise, or emotional validation.
(사용자의 신념을 검증하는 것보다 기술적 정확성과 진실성을 우선시하세요.
사실과 문제 해결에 집중하고, 불필요한 최상급 표현, 칭찬 또는 감정적 동조 없이
직접적이고 객관적인 기술 정보를 제공하세요.)
이 단락은 정말 중요합니다. 모델 특유의 '아부하는(sycophancy)' 성향을 차단하거든요. 코드 리뷰, 아이디어 평가, 아키텍처 결정 등 객관적인 판단을 내려야 하는 에이전트라면 이런 조항이 반드시 필요합니다.
3.4 핵심 워크플로우 (Core Workflow) — 가장 중요한 섹션
목표: 모델에게 일하는 방법을 가르칩니다. 경직된 절차가 아닌 방법론을 제시하세요.
이 부분은 가장 잘 쓰기 어렵지만, 제대로 썼을 때 가장 큰 영향을 미치는 섹션입니다.
핵심 원칙: 절차가 아닌 원칙을 제공하라.
LLM에게 좋은 결과물이란 어떤 것인지, 왜 그것이 좋은지 알려주고, 거기에 도달하는 방법은 스스로 찾게 하세요. 결과물이 다운스트림에서 기계에 의해 소비되는 경우가 아니라면, 정확한 필드 수, 단계 순서, 포맷 등을 강제하지 마세요.
Claude Code의 접근 방식:
## Doing tasks
The user will primarily request software engineering tasks.
For these tasks the following steps are recommended:
- Use the TodoWrite tool to plan the task if required
(## 작업 수행
사용자는 주로 소프트웨어 엔지니어링 작업을 요청할 것입니다.
이러한 작업에는 다음 단계가 권장됩니다:
- 필요한 경우 TodoWrite 도구를 사용하여 작업을 계획하세요)
"권장됩니다(recommended)"라는 단어에 주목하세요. "반드시 이 정확한 단계를 따라야 한다"가 아닙니다. 이 단어 하나가 모델에게 적응할 수 있는 여유를 줍니다.
좋은 워크플로우 정의:
1. 먼저 이해하기 — 코드를 수정하기 전에 기존 코드를 읽으세요.
2. 먼저 계획하기 — 복잡한 작업은 실행하기 전에 여러 단계로 나누세요.
3. 최소한의 변경 — 필요한 것만 변경하고, "손대는 김에 리팩토링"하지 마세요.
4. 검증하기 — 변경 사항이 제대로 작동하는지 확인하세요 (테스트 실행, 린트 등).
각 규칙에는 암묵적인 "이유(why)"가 포함되어 있습니다. 모델은 그 의도를 이해하고 새로운 상황에 일반화하여 적용할 수 있습니다.
안티 패턴:
- 경직된 20단계 절차 — 모델은 기계적으로 실행하다가 예상치 못한 입력이 들어오면 멈춰버릴 것입니다.
- "먼저 A를 하고, 그다음 B를 하고, 그다음 C를 하라" — 이건 프롬프트 체인이지 에이전트 프롬프트가 아닙니다.
- LLM이 이미 잘하는 것을 과도하게 가이드하기 — 토큰 낭비입니다.
저는 VibeCom을 만들면서 이 사실을 뼈저리게 깨달았습니다. 초기 버전에는 10단계의 리서치 워크플로우가 있었죠. 모델은 3단계에서 이미 사용자의 질문에 대한 답을 찾았음에도 불구하고 충실하게 10단계를 모두 실행했습니다. 이를 원칙("충분한 증거를 확보할 때까지 리서치하고, 그 후 종합하라")으로 바꾸자 품질은 올라가고 토큰 비용은 줄어들었습니다.
예외 상황: 출력 결과가 다운스트림에서 기계에 의해 소비될 때(에이전트 간 통신, API 응답 포맷 등)는 엄격한 포맷을 정의해야 합니다. 원칙은 행동을 위한 것이고, 스키마는 인터페이스를 위한 것입니다.
3.5 도구 사용 정책 (Tool Usage Policy) — 모호함 해결하기
목표: 여러 도구가 같은 작업을 수행할 수 있을 때, 어떤 것을 우선할지 모델에게 알려줍니다.
## Tool usage policy
- Use specialized tools instead of bash commands:
- Read for reading files instead of cat/head/tail
- Edit for editing instead of sed/awk
- Grep for searching instead of grep/rg
- You can call multiple tools in a single response. If independent, call in parallel.
- Use the Task tool for file search to reduce context usage.
가이드라인:
- 우선순위를 표현할 때 "대신에(instead of)"를 사용하세요 (B 대신 A).
- 특정 도구를 선호해야 하는 이유를 설명하세요 ("더 나은 사용자 경험 제공", "컨텍스트 사용량 감소").
- 병렬 처리 전략을 정의하세요 (독립적 → 병렬, 종속적 → 순차적).
- 도구 사용에 대한 보안 제약 조건을 나열하세요 (경로 검증, 권한 확인).
도구와 프롬프트의 중요한 관계:
도구 정의는 보통 시스템에 의해 주입되며 직접 수정할 수 없습니다. Claude Code의 도구 정의는 약 11,438 토큰입니다. 이는 다음을 의미합니다:
- 도구 정의에 이미 있는 정보를 반복하지 마세요.
- 시스템 프롬프트는 전략적 가이드에 사용하세요: 각 도구를 언제 사용할지, 왜 다른 것보다 선호하는지, 우선순위는 무엇인지.
- 도구 정의의 품질이 에이전트의 성능에 직결됩니다. 자체 에이전트를 구축 중이라면 훌륭한 도구 설명을 작성하는 데 시간을 투자하세요.
3.6 도메인 지식 (Domain Knowledge) — 사전 로드가 아닌 온디맨드 로드
목표: 모델의 학습 데이터에 부족할 수 있는 전문 지식을 제공합니다.
핵심 원칙: 지식 쏟아붓기가 아닌 점진적 공개(Progressive disclosure).
❌ 200개의 API 엔드포인트를 시스템 프롬프트에 모두 붙여넣기 → 토큰 폭발
✅ 모델에게 검색할 수 있는 도구 제공하기 → "필요할 때 지식을 로드하세요"
이 전략은 Claude Code의 Skills 시스템과 DeepAgents의 Progressive Disclosure 미들웨어가 공유하는 방식입니다. 두 시스템 모두 모든 것을 미리 로드하는 대신 도구 호출을 통해 필요할 때 지식을 로드합니다.
구현 방법:
- 시스템 프롬프트에 포인터 두기: "필요할 때 get_api_docs 도구를 사용하여 문서를 검색하세요."
- 프로젝트 컨텍스트에 CLAUDE.md / AGENTS.md 사용하기 — 하드코딩하지 않고 런타임에 로드합니다.
- 기능 발견을 위해 Skills / SKILL.md 사용하기 — 모델이 사용 가능한 스킬 메뉴를 보고 필요할 때 전체 스펙을 가져옵니다.
3.7 환경 정보 (Environment Info) — 런타임 컨텍스트
목표: 모델이 자신이 실행되는 환경을 인지하게 합니다.
<env>
Working directory: /Users/fengliu/Desktop/tfm/vibecom
Is directory a git repo: true
Platform: darwin
Today's date: 2026-03-21
</env>
You are powered by the model named Claude Opus 4.6.
가이드라인:
- 절대 하드코딩하지 말고 동적으로 생성하세요.
- 포함할 내용: 작업 디렉토리, 플랫폼, 날짜, 모델 이름, git 상태.
- 파싱하기 쉽도록 구조화된 포맷(XML 태그나 코드 블록)을 사용하세요.
- 날짜는 중요합니다 — 모델은 정보의 최신성을 판단하기 위해 "지금"이 언제인지 알아야 합니다.
3.8 리마인더 (Reminders) — 마지막 강화
목표: 프롬프트의 마지막에 가장 중요한 규칙을 다시 한 번 강조합니다.
Claude Code는 맨 아래에 안전 제약 조건과 TodoWrite 요구사항을 반복합니다:
IMPORTANT: Assist with defensive security tasks only. [반복됨]
IMPORTANT: Always use the TodoWrite tool to plan and track tasks. [반복됨]
가이드라인:
- 가장 중요한 규칙 2~3개만 반복하세요 — 모든 것을 중복해서 쓰지 마세요.
- 최신 효과(Recency bias)를 활용하세요 — 모델은 최근 내용을 더 강하게 기억합니다.
- 추천 항목: 안전 제약 조건, 가장 자주 위반되는 규칙, 핵심 워크플로우 리마인더.
4. 토큰 예산 및 컨텍스트 관리
예산 할당 참고표
| 섹션 | 권장 토큰 수 | 비고 |
|---|---|---|
| 정체성 + 안전 | 200-500 | 간결하지만 타협 불가 |
| 톤 앤 매너 | 300-800 | 규칙은 구체적이어야 하지만 장황하지 않게 |
| 핵심 워크플로우 | 500-2,000 | 가장 중요한 섹션, 투자할 가치가 있음 |
| 도구 사용 정책 | 300-1,000 | 도구의 수에 따라 다름 |
| 도메인 지식 | 0-1,000 | 온디맨드 로딩 권장 |
| 환경 정보 | 100-300 | 동적으로 생성됨 |
| 리마인더 | 100-300 | 필수적인 것만 반복 |
| 여러분이 작성할 분량 | 1,500-6,000 | |
| 도구 정의 (시스템 주입) | 5,000-15,000 | 제어할 수 없는 영역 |
컨텍스트 저하 곡선
커뮤니티 테스트(Reddit u/CodeMonke_)를 통해 실제 지시사항 준수율 저하 곡선이 매핑되었습니다:
- < 80K 토큰: 프롬프트 준수율이 안정적으로 유지됨
- 80K - 120K 토큰: 지시사항 준수율이 떨어지기 시작함
- > 120K 토큰: 심각한 저하 — 모델이 초기 지시사항을 "잊어버림"
- > 180K 토큰: 매우 심각한 저하
200K의 컨텍스트 윈도우가 200K의 '유효한' 컨텍스트를 의미하지는 않습니다. 이에 맞춰 계획을 세워야 합니다.
완화 전략:
- 시스템 프롬프트를 가볍게 유지하세요 (직접 작성하는 부분은 6,000 토큰 미만으로).
- 요약 기능을 사용하여 대화 기록을 압축하세요 (DeepAgents는 약 80K 문자에서 트리거됨).
- 중요한 규칙은 프롬프트의 양쪽 끝에 배치하세요 (U자형 주의력).
- 대화 중간에
<system-reminder>태그를 주입하세요 (자세한 내용은 섹션 8 참조).
5. 작성 원칙
5.1 절차가 아닌 원칙을 제공하라
❌ "1단계: 파일을 읽는다. 2단계: 버그를 찾는다. 3단계: 수정한다. 4단계: 테스트를 실행한다."
✅ "코드를 수정하기 전에 항상 기존 코드를 이해하세요. 변경 사항이 작동하는지 검증하세요."
원칙은 일반화가 가능하지만, 절차는 기계적으로만 따를 수 있습니다. 모델이 여러분이 예상하지 못한 상황에 직면했을 때, 원칙은 올바른 결정을 내리도록 안내합니다. 절차는 그렇지 못하죠.
예외: 출력 결과가 기계에 의해 소비될 때(에이전트 간 통신, API 포맷)는 엄격한 스키마를 정의하세요.
5.2 강력한 제약에는 절대적인 언어 사용하기
| 강도 | 언어 | 사용처 |
|---|---|---|
| 절대적 금지 | NEVER, MUST NOT | 안전, 되돌릴 수 없는 작업 |
| 강력한 요구 | ALWAYS, MUST | 핵심 워크플로우 규칙 |
| 권장 | recommended, prefer | 예외가 있는 모범 사례 |
| 제안 | consider, you may | 선택적 최적화 |
Claude Code 예시:
NEVER update the git config(git config를 절대 업데이트하지 마세요) — 절대적 금지ALWAYS prefer editing an existing file(항상 기존 파일 편집을 선호하세요) — 강력하지만 예외 존재The following steps are recommended(다음 단계가 권장됩니다) — 제안된 워크플로우
5.3 설명 대신 예시 사용하기
## Code References
When referencing specific functions or pieces of code include
the pattern `file_path:line_number`.
<example>
user: Where are errors from the client handled?
assistant: Clients are marked as failed in the `connectToServer`
function in src/services/process.ts:712.
</example>
백 마디 설명보다 하나의 예시가 낫습니다:
- 모델은 추상적인 설명보다 예시를 통해 패턴을 훨씬 더 안정적으로 학습합니다.
- 규칙과 분리하기 위해
<example>태그로 감싸세요. - 긍정적인 예("이렇게 하세요")와 부정적인 예("이렇게 하지 마세요")를 모두 제공하세요.
- "foo/bar" 같은 플레이스홀더가 아닌 실제적이고 구체적인 예시를 사용하세요.
5.4 양방향 제약 (Bidirectional Constraints)
✅ "전용 도구를 사용하세요: 파일 읽기에는 Read, 파일 편집에는 Edit."
✅ "파일 작업에 bash(cat, head, tail, sed, awk)를 사용하지 마세요."
"이것을 하라"고만 하면 모델은 언제 하지 말아야 할지 모릅니다. "이것을 하지 마라"고만 하면 대안을 모릅니다. 양방향으로 제약하면 명확하고 모호함이 사라집니다.
5.5 '무엇'뿐만 아니라 '왜'를 설명하기
❌ "git commit --amend를 사용하지 마세요."
✅ "git commit --amend를 피하세요. 다음의 경우에만 --amend를 사용하세요:
(1) 사용자가 명시적으로 amend를 요청했거나
(2) pre-commit 훅의 편집 내용을 추가할 때.
이유: amend는 다른 사람의 커밋을 덮어쓸 수 있습니다."
이유를 설명하면 모델이 엣지 케이스에서도 올바른 판단을 내릴 수 있습니다. Claude Code의 git 안전 프로토콜은 정말 훌륭한 교보재입니다. 모든 규칙이 그 근거를 내포하고 있죠.
5.6 산문보다 구조화
- 마크다운 헤더 (
##,###) — 모델은 계층 구조를 인식합니다. - 문단보다 글머리 기호(Bullet lists) — 각 규칙을 독립적으로 테스트할 수 있습니다.
- 특수 콘텐츠를 위한 XML 태그:
<example>,<env>,<system-reminder> - 비교 및 매핑을 위한 표(Tables)
- 구조화되지 않은 텍스트를 그냥 쏟아붓지 마세요 — 지시사항 준수 테스트에서 구조화된 프롬프트는 항상 자연어 산문 형태를 압도합니다.
6. 토큰을 낭비하는 안티 패턴
에이전트로 위장한 프롬프트 체인
"먼저 도구 A를 호출하여 데이터를 가져오세요.
그런 다음 그 결과로 도구 B를 호출하세요.
그런 다음 출력을 JSON으로 포맷팅하세요.
그런 다음 파일에 저장하세요."
이건 에이전트 프롬프트가 아니라 파이프라인 스크립트입니다. 모델은 기계적으로 실행할 것이고 자율적인 계획 능력을 잃게 됩니다.
해결책: 모델에게 목표와 제약 조건을 알려주세요. 단계는 스스로 결정하게 하세요.
아부 엔지니어링 (Flattery Engineering)
"당신은 20년 경력의 엄청난 재능을 가지고 믿을 수 없을 만큼
경험이 풍부한 시니어 소프트웨어 엔지니어입니다..."
칭찬과 최상급 표현은 결과물의 품질을 높이지 않습니다. 모델은 치켜세워줄 자아가 없습니다. 그 15토큰을 아껴서 실제 규칙을 적는 데 쓰세요.
지식 쏟아붓기 (Knowledge Dumps)
"다음은 200개 엔드포인트에 대한 전체 API 문서입니다..."
이것은 컨텍스트 윈도우를 집어삼키고 컨텍스트 부패를 가속화합니다. 온디맨드 로딩으로 교체하세요:
"필요할 때 get_api_docs 도구를 사용하여 API 문서를 검색하세요."
도구 설명 반복하기
도구 정의에 이미 "Read 도구는 파일 시스템에서 파일을 읽습니다"라고 적혀 있다면, 시스템 프롬프트에서 다시 말하지 마세요. 도구 정의에서 다루지 않는 전략적 가이드(언제 사용할지, 왜 선호하는지, 우선순위)만 추가하세요.
실패 처리 누락
명시적인 가이드가 없으면 모델은 실패한 도구 호출을 무한 루프로 재시도할 것입니다. 항상 다음을 포함하세요:
"도구 호출이 거부되면 정확히 동일한 호출을 다시 시도하지 마세요.
왜 거부되었는지 생각하고 접근 방식을 조정하세요."
컨텍스트 윈도우 저하 무시하기
200K 컨텍스트 윈도우 ≠ 200K의 유효한 컨텍스트. 실제 테스트에 따르면 80K부터 저하가 시작됩니다. 요약 전략이 필요합니다.
7. 주입 지점과 우선순위
Claude Code의 세 가지 커스터마이징 방법
| 방법 | 대체하는 영역 | 위치 | 최적의 용도 |
|---|---|---|---|
| Output Styles | "톤 앤 스타일" + "작업 수행" 섹션 | 도구 정의 바로 앞 | 상호작용 스타일 변경 |
| --append-system-prompt | 없음 (추가됨) | Output Style 뒤, 도구 정의 앞 | 특정 동작 추가 |
| --system-prompt | 전체 시스템 프롬프트 | 도구 정의 + 정체성 한 줄만 유지 | 전체 커스터마이징 (핵옵션) |
여러 개를 사용할 경우: Output Style → Append Prompt → 도구 정의 순서로 적용됩니다.
지시사항 계층 구조 (Instruction Hierarchy)
Claude는 특별히 지시사항 계층 구조를 따르도록 훈련되었습니다:
1. 사용자의 명시적 지시 (CLAUDE.md, 직접적인 요청) ← 최우선 순위
2. 커스텀 시스템 프롬프트 추가 사항 ← 높음
3. 기본 시스템 프롬프트 ← 중간
4. 도구 정의 ← 참고 수준
이것이 의미하는 바는 다음과 같습니다:
- CLAUDE.md 규칙은 기본 시스템 프롬프트 동작을 덮어씁니다.
- 사용자의 직접적인 요청은 모든 것을 덮어씁니다.
- 여러분의 커스텀 프롬프트는 기본 프롬프트를 덮어씁니다.
동적 주입 메커니즘
<system-reminder>— 대화 중간에 어떤 메시지에든 주입하여 모델에게 중요한 규칙을 상기시킵니다.- CLAUDE.md / AGENTS.md — 런타임에 파일에서 로드되어 시스템 프롬프트에 추가됩니다.
- Skills / SKILL.md — 도구 호출을 통해 온디맨드로 로드되며, 시스템 프롬프트 공간을 차지하지 않습니다.
8. 대화 중간 주입 (Mid-Conversation Injection) — 비밀 무기
시스템 프롬프트는 메시지 배열의 맨 처음에 딱 한 번만 나타납니다. 하지만 LLM은 전체 메시지 배열(사용자/어시스턴트/도구 메시지가 교차하는 형태)을 입력으로 받기 때문에, 사용자 메시지나 도구 실행 결과에도 프롬프트를 주입할 수 있습니다. Claude Code는 프로덕션 환경에서 이 기법을 아주 적극적으로 사용합니다.
왜 필요할까요?
컨텍스트 부패(Context rot)와의 전쟁. 대화가 길어질수록 시스템 프롬프트 지시사항에 대한 모델의 준수율은 떨어집니다(80K+ 토큰에서 눈에 띄게 나타남). 대화 중간에 리마인더를 주입하는 것은 최신 효과를 통해 규칙을 새로고침하는 것과 같습니다.
멘탈 모델:
- 시스템 프롬프트 = 헌법 (한 번 제정되면 장기적인 권위를 가짐)
- 사용자 메시지 리마인더 = 업무 지시서/메모 (주기적으로 발송되어 실행력을 유지함)
메시지 배열의 세 가지 주입 지점
메시지 배열 (Messages Array):
┌─────────────────────────────────────┐
│ System Prompt (시스템 프롬프트) │ ← 한 번만 나타남, 초두 효과
│ (정체성, 안전, 워크플로우...) │
├─────────────────────────────────────┤
│ User Message 1 │
│ Assistant Message 1 │
│ User Message 2 + <system-reminder> │ ← 대화 중간 주입
│ Assistant Message 2 │
│ Tool Result + <system-reminder> │ ← 도구 결과에도 주입 가능
│ ... │
│ User Message N + <system-reminder> │ ← 최신 메시지, 가장 강력한 최신 효과
└─────────────────────────────────────┘
| 위치 | 장점 | 단점 |
|---|---|---|
| 시스템 프롬프트 | 초두 효과, 가장 먼저 읽힘 | 한 번만 나타나며, 긴 대화에서는 "잊혀짐" |
| 사용자 메시지 주입 | 최신 효과, 주기적인 새로고침 | 주입할 때마다 토큰 비용 발생 |
| 도구 결과 주입 | 가장 자연스러운 주입 지점 | 도구가 호출될 때만 작동함 |
Claude Code는 실제로 어떻게 사용할까?
사전 조건 — 시스템 프롬프트에 태그 선언하기:
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.
(도구 결과와 사용자 메시지에는 <system-reminder> 태그가 포함될 수 있습니다.
<system-reminder> 태그는 유용한 정보와 리마인더를 포함합니다.
이는 시스템에 의해 자동으로 추가되며, 해당 태그가 나타나는
특정 도구 결과나 사용자 메시지와는 직접적인 관련이 없습니다.)
이 단계는 매우 중요합니다. 모델에게 이 태그들이 사용자의 말이 아니라 시스템이 주입한 것임을 알려주기 때문입니다.
활용 1: 동작 리마인더 (주기적인 규칙 새로고침)
<system-reminder>
The task tools haven't been used recently. If you're working on tasks
that would benefit from tracking progress, consider using TaskCreate...
(최근에 작업 도구가 사용되지 않았습니다. 진행 상황 추적이
도움이 될 만한 작업을 하고 있다면 TaskCreate 사용을 고려해 보세요...)
</system-reminder>
Claude Code는 이를 사용하여 모델에게 TodoWrite로 계획을 세우도록 상기시킵니다. 모델들은 종종 계획 세우기를 "잊어버리고" 무작정 코딩부터 시작하는 경향이 있기 때문입니다.
활용 2: 모드 전환 (계획 모드)
<system-reminder>
Plan mode is active. The user indicated that they do not want you to
execute yet -- you MUST NOT make any edits, run any non-readonly tools,
or otherwise make any changes to the system.
(계획 모드가 활성화되었습니다. 사용자는 아직 실행을 원하지 않는다고
표시했습니다 -- 편집을 하거나, 읽기 전용이 아닌 도구를 실행하거나,
시스템에 어떠한 변경도 가해서는 안 됩니다.)
</system-reminder>
계획 모드는 시스템 프롬프트에 구현되어 있지 않습니다. 다음 사용자 메시지에 주입되는 태그일 뿐이죠. 이를 통해 시스템 프롬프트를 수정하지 않고도 동적으로 모드를 전환할 수 있습니다. 정말 기발하죠.
활용 3: 파일 변경 알림
<system-reminder>
Note: /path/to/file.ts was modified, either by the user or by a linter.
This change was intentional, so make sure to take it into account.
(참고: /path/to/file.ts가 사용자나 린터에 의해 수정되었습니다.
이 변경은 의도적인 것이므로 반드시 고려하시기 바랍니다.)
</system-reminder>
외부 프로세스(린터, 포매터, 수동 편집)가 파일을 수정하면 시스템은 리마인더를 통해 모델에게 알립니다. 이를 통해 오래된 파일 내용을 바탕으로 잘못된 결정을 내리는 것을 방지합니다.
활용 4: 동적 컨텍스트 (날짜, 프로젝트 규칙)
<system-reminder>
Today's date is 2026-03-21.
Current branch: dev
claudeMd: [CLAUDE.md 내용이 여기에 주입됨]
</system-reminder>
런타임 컨텍스트(날짜, git 상태, 프로젝트 규칙)는 시스템 프롬프트에 하드코딩되지 않고 사용자 메시지를 통해 주입됩니다.
리마인더 작성 가이드라인
- XML 태그로 감싸기 (
<system-reminder>) — 모델이 시스템 주입과 사용자의 말을 구분할 수 있게 합니다. - 시스템 프롬프트에 태그 사전 선언하기 — 그렇지 않으면 모델이 리마인더에 대답하려고 시도할 수 있습니다.
- 모든 메시지에 주입하지 않기 — 각 주입마다 토큰 비용이 발생하므로 필요할 때만 주입하세요.
- 짧게 유지하기 — 리마인더는 두 번째 시스템 프롬프트가 아닙니다. 1~2개의 핵심 규칙만 담으세요.
- 시스템 프롬프트와 모순되지 않게 하기 — 리마인더는 보완하고 강화하는 것이지 덮어쓰는 것이 아닙니다.
- 동적 토글에 사용하기 — 계획 모드, 읽기 전용 모드, 기능 플래그(Feature flags) 등에 활용하세요.
시스템 프롬프트 vs 사용자 메시지 리마인더 언제 사용할까?
| 시나리오 | 시스템 프롬프트 | 사용자 메시지 리마인더 |
|---|---|---|
| 역할 정의 | ✅ | ❌ |
| 안전 제약 조건 | ✅ 최초 선언 | ✅ 주기적 반복 |
| 워크플로우 방법론 | ✅ | ❌ |
| 모드 전환 (계획 모드) | ❌ | ✅ |
| 파일 변경 알림 | ❌ | ✅ |
| 날짜 / 환경 정보 | ✅ 초기값 | ✅ 업데이트된 값 |
| 행동 교정 | ❌ | ✅ |
| 도구 사용 리마인더 | ✅ 규칙 정의 | ✅ 실행 유도 |
9. 프롬프트 캐시 — 반복 토큰 비용 90% 절감하기
Anthropic의 프롬프트 캐싱을 사용하면 메시지 배열의 **정적 접두사(static prefix)**를 캐시할 수 있습니다. 후속 요청이 동일한 접두사를 공유하면 캐시가 적중(hit)하여 비용을 절감하고 지연 시간을 줄여줍니다.
에이전트의 경우 이는 매우 중요합니다. 대화 내의 모든 LLM 호출마다 시스템 프롬프트와 도구 정의를 계속해서 다시 전송하고 있기 때문입니다.
핵심 수치
| 지표 | 값 |
|---|---|
| 캐시 적중 비용 | 정상 가격의 10% (90% 절감) |
| 캐시 쓰기 비용 | 정상 가격의 125% (첫 쓰기 시 25% 할증) |
| 캐시 TTL | 5분 (요청이 없으면 만료됨) |
| 최소 캐시 가능 길이 | 1,024 토큰 (Claude 3.5+) |
| 캐시 단위 | 접두사 매칭(Prefix matching) — 시작부터 표시된 중단점까지 |
| 최대 중단점(Breakpoints) | 4개 |
이것이 프롬프트 설계를 어떻게 바꾸는가
핵심 원칙: 정적 콘텐츠는 먼저, 동적 콘텐츠는 나중에.
✅ 캐시 친화적인 레이아웃:
시스템 프롬프트 (정적) ← 캐시 중단점 1
도구 정의 (정적) ← 캐시 중단점 2
CLAUDE.md / 프로젝트 규칙 ← 캐시 중단점 3 (가끔 변경됨)
대화 기록 ← 롤링 윈도우를 위한 중단점 4
❌ 캐시를 파괴하는 레이아웃:
시스템 프롬프트
동적 타임스탬프 ← 매 요청마다 변경됨, 이 뒤의 모든 것은 캐시 미스
도구 정의
대화 기록
아무도 경고해주지 않는 함정: 시스템 프롬프트 중간에 동적으로 변하는 타임스탬프를 넣으면, 그 뒤에 오는 모든 내용이 캐시 미스(Cache miss)가 됩니다. 매. 요청마다요. 잘못된 위치에 들어간 타임스탬프 하나 때문에 수천 토큰에 대한 비용을 고스란히 지불하게 되는 겁니다.
API 사용법
const response = await anthropic.messages.create({
model: "claude-sonnet-4-6",
system: [
{
type: "text",
text: "You are a startup advisor...",
cache_control: { type: "ephemeral" } // ← 캐시 중단점을 표시함
}
],
messages: [...]
});
다중 중단점 전략 (Multi-Breakpoint Strategy)
중단점 1: 시스템 프롬프트 ← 거의 변하지 않음
중단점 2: 도구 정의 ← 거의 변하지 않음
중단점 3: 프로젝트 규칙 / CLAUDE.md ← 가끔 변경됨
중단점 4: 처음 N개의 대화 기록 ← 롤링 윈도우 캐시
대화 기록이 변경되더라도 처음 3개의 중단점은 여전히 캐시에 적중합니다. 10턴의 대화에서 입력 토큰 비용을 대략 40-60% 절감할 수 있습니다.
설계 권장 사항
- 시스템 프롬프트에 고빈도 동적 값을 넣지 마세요 — 날짜는 괜찮지만(매일 변경됨), 정확한 타임스탬프는 안 됩니다.
- 동적 컨텍스트(git 상태 등)는 사용자 메시지 주입에 넣으세요 — 시스템 프롬프트에 넣으면 캐시가 파괴됩니다.
- 도구 정의를 안정적으로 유지하세요 — 런타임에 동적으로 도구를 추가/제거하지 마세요.
- 대화 기록에 롤링 윈도우를 사용하세요 — 처음 N개의 메시지를 캐시하고, 가장 최신 메시지만 캐시 미스가 되도록 하세요.
10. 체크리스트
시스템 프롬프트를 작성한 후, 다음 체크리스트를 통해 검토해 보세요:
구조
- 정체성이 맨 위에 있나요?
- 안전 제약 조건이 IMPORTANT로 표시되어 있고 마지막에 반복되나요?
- 헤더를 통해 섹션 구분이 명확한가요?
- 예시가
<example>태그로 감싸져 있나요?
토큰 예산
- 직접 작성한 부분이 6,000 토큰 미만인가요?
- 도구 정의에 이미 있는 정보를 반복하지 않았나요?
- 도메인 지식이 미리 로드되지 않고 온디맨드로 로드되나요?
- 장황한 세계관이나 캐릭터 배경 스토리가 없나요?
규칙 품질
- 모든 규칙을 참/거짓으로 테스트할 수 있나요?
- 강력한 제약에 절대적인 언어(NEVER/MUST)를 사용했나요?
- 부드러운 제안에 권장 언어(recommended/prefer)를 사용했나요?
- 핵심 규칙이 무엇뿐만 아니라 왜를 설명하고 있나요?
- 양방향 제약(이것을 하라 + 저것을 하지 마라)을 사용했나요?
에이전트 행동
- 경직된 단계별 절차가 아닌 원칙을 제공했나요?
- "도구 호출 거부" 시나리오를 처리했나요?
- "장애물 조우" 전략을 처리했나요 (무식하게 재시도하지 않기)?
- 컨텍스트 관리 전략이 마련되어 있나요 (요약 임계값)?
하지 말아야 할 것
- 아부나 최상급 형용사가 없나요?
- 불필요한 "당신은 유용한 AI입니다" 같은 선언이 없나요?
- 프롬프트 체인처럼 작성되지 않았나요?
- 오버엔지니어링(아무도 요구하지 않은 기능)이 없나요?
만약 제가 오늘 다시 시작한다면
제가 정확히 취할 행동은 다음과 같습니다:
-
처음 세 줄에 정체성 + 안전을 배치합니다. 에이전트가 누구인지 두 문장으로 적습니다. NEVER/MUST를 사용하여 강력한 제약을 설정합니다. 안전 규칙은 마지막에 반복합니다.
-
핵심 워크플로우를 단계가 아닌 원칙으로 작성합니다. 최대 4~5개의 글머리 기호를 사용합니다. 부드러운 규칙에는 "권장(recommended)"과 "선호(prefer)"를, 강력한 규칙에는 "절대(NEVER)"와 "반드시(MUST)"를 사용합니다.
-
직접 작성할 부분의 예산을 1,500~6,000 토큰으로 잡습니다. 도구 정의가 5,000~15,000 토큰을 더 차지할 것입니다. 6K를 넘는다면 온디맨드로 로드해야 할 지식을 쏟아붓고 있을 가능성이 높습니다.
-
모든 것을 구조화합니다. 마크다운 헤더, 글머리 기호, 예시를 위한 XML 태그를 사용합니다. 구조화된 프롬프트는 언제나 자연어 산문 형태를 압도합니다.
-
첫날부터 대화 중간 리마인더를 구축합니다. 시스템 프롬프트에
<system-reminder>를 선언합니다. 핵심 규칙, 모드 전환, 컨텍스트 업데이트를 위해 리마인더를 주입합니다. -
캐시를 고려하여 설계합니다. 정적 콘텐츠는 먼저, 동적 콘텐츠는 나중에 배치합니다. 시스템 프롬프트 본문에 변하는 값을 절대 넣지 마세요.
이 모든 작업의 아이러니가 뭔지 아시나요? 최고의 시스템 프롬프트는 짧다는 겁니다. Claude Code의 커스텀 지시사항(도구 정의 제외)은 놀라울 정도로 간결합니다. 모든 문장이 그 자리에 있어야 할 이유를 증명하고 있죠.
예전에는 프롬프트 엔지니어링이 기발한 꼼수를 찾는 일이라고 생각했습니다. 하지만 지금은 **절제(discipline)**의 문제라고 생각합니다. 말을 줄이고, 정확하게 말하며, 나머지는 모델이 알아서 해낼 것이라고 믿는 것이죠. 모델은 여러분의 프롬프트보다 똑똑합니다. 행동을 설계하지 말고, 환경을 설계하세요.
참고 자료
| 출처 | 핵심 인사이트 |
|---|---|
| Claude Code v2.0.14 System Prompt | 프로덕션 에이전트 프롬프트 구조 전체 레퍼런스 |
| Reddit: Understanding Claude Code's 3 System Prompt Methods | Output Styles / --append / --system-prompt 심층 분석, 컨텍스트 부패 실제 데이터 |
| shareAI-lab/learn-claude-code | "모델이 곧 에이전트다" 철학, 하네스 엔지니어링 방법론 |
| Anthropic Prompt Engineering Docs | 공식 프롬프트 모범 사례 |
| DeepAgents Framework | 요약 미들웨어, 스킬 점진적 공개(Progressive disclosure) |
공유하기

작성자 Feng Liu
shenjian8628@gmail.com