시스템 프롬프트 분해 6단계

긴 시스템 프롬프트를 Skill 기반 모듈 구조로 분해하는 방법론

하나의 긴 시스템 프롬프트를 코어 + 스킬 모듈 구조로 분해하는 6단계 방법론을 정리한다. 소프트웨어 설계의 모듈화 원칙(SRP, lazy loading, 라우팅)을 프롬프트에 적용하여, 독립 태스크 식별 → 공통 요소 추출 → 코어/스킬 분류 → 라우팅 테이블 → 도메인 분리 → 전/후처리 식별의 순서로 단일 파일 프롬프트를 체계적으로 모듈화하는 과정을 다룬다. 범용 의사결정 트리와 어떤 유형의 시스템 프롬프트에든 적용되는 3가지 원칙도 함께 제시한다.

Agent
Architecture
Prompt Engineering
저자

Kwangmin Kim

공개

2026년 03월 25일

1 배경

이 포스트는 하나의 긴 AGENT_GUIDE.md(v2.0, 단일 파일에 모든 규칙)를 skill-based 구조(v4.0, 코어 + guides/ 스킬 모듈)로 분해한 과정에서 도출한 방법론을 정리한 것이다.

  • v2.0 구조: 질문 응답, 포스트 작성, TBD 전환, 시리즈 정리, 교재 참조, 작성 규칙, index 규칙이 하나의 파일에 모두 있었다
  • 문제: agent가 질문 하나에 답하려 해도 시리즈 정리, TBD 전환 절차까지 전부 컨텍스트에 올라간다. 토큰 낭비와 규칙 간 간섭이 발생한다
  • 해결: 소프트웨어 모듈화 원칙(SRP, lazy loading, 라우팅)을 프롬프트에 적용한다

1.1 왜 분해해야 하는가

단일 파일 프롬프트의 문제를 정량적으로 보면 다음과 같다.

지표 v2.0 (단일 파일) v4.0 (코어 + 스킬)
질문 응답 시 로드되는 규칙량 전체 (100%) 코어 + answer-question.md (~20%)
규칙 간 간섭 가능성 높음 — TBD 전환 절차가 포스트 작성에 영향 낮음 — 스킬이 격리됨
단일 규칙 수정 시 영향 범위 전체 파일 해당 스킬 파일만
agent의 라우팅 정확도 암묵적 판단 (규칙 혼재) 명시적 매핑 (라우팅 테이블)

핵심은 attention dilution 문제이다. LLM은 컨텍스트가 길어질수록 중간 부분의 정보를 놓치는 경향이 있다(Lost in the Middle). 지금 필요하지 않은 규칙이 컨텍스트에 있으면, 정작 필요한 규칙의 attention weight가 희석된다.

2 분해 6단계

2.1 Step 1. 독립 태스크 식별

“이 문서에서 agent가 하는 서로 다른 일이 몇 가지인가?”를 묻는다.

판단 기준 3가지:

  1. 독립 실행 가능성: “이 섹션이 다른 섹션 없이 단독으로 실행 가능한가?”
  2. 변경 이유의 독립성 (SRP): “이 절차를 바꿀 때 다른 절차도 함께 바꿔야 하는가?”
  3. 입출력의 독립성: “이 태스크의 입력과 출력이 다른 태스크와 겹치지 않는가?”

적용 예시:

[v2.0 단일 파일에서 식별한 태스크들]

① 질문 응답         — 사용자 질문 → 블로그 기반 답변
② 포스트 작성       — 주제 → .qmd 파일 생성
③ TBD 전환          — TBD 메모 → 완성 포스트
④ 시리즈 정리       — 기존 포스트 → 시리즈 구조화
⑤ 기존 포스트 교정  — 기존 포스트 → 품질 개선

독립 실행 검증:
- 질문 응답은 포스트 작성 없이 가능한가? → Yes → 별도 스킬
- TBD 전환은 질문 응답 없이 가능한가? → Yes → 별도 스킬
- 포스트 작성 절차를 바꿀 때 TBD 전환 절차도 바꿔야 하는가? → No → 분리

안티패턴 — 과분리:

독립 실행이 가능하더라도 항상 함께 호출되는 것은 분리하지 않는다. 예를 들어 “YAML 헤더 작성”과 “본문 작성”은 독립 실행 가능하지만, 포스트 작성 시 항상 함께 수행된다. 이를 별도 스킬로 분리하면 호출 오버헤드만 늘어난다.

[과분리 — 나쁜 예]
스킬 A: YAML 헤더 생성
스킬 B: 본문 작성
스킬 C: index.qmd 업데이트
→ 포스트 하나 쓰려면 A → B → C를 순서대로 호출해야 한다

[적정 분리 — 좋은 예]
스킬: 포스트 작성 (내부에 Step 1: YAML → Step 2: 본문 → Step 3: index)
→ 하나의 스킬 안에서 단계적으로 수행한다

2.2 Step 2. 공통 요소 추출

“여러 스킬에서 반복되는 절차가 있는가?”를 묻는다.

발견 방법: 1단계에서 식별한 태스크들의 절차를 나열하고, 2회 이상 등장하는 절차를 표시한다.

질문 응답:   [교재 참조] → [블로그 검색] → 답변 생성
포스트 작성: [교재 참조] → [블로그 검색] → 중복 판단 → 작성
TBD 전환:    [교재 참조] → [블로그 검색] → TBD 파싱 → 작성
시리즈 정리: [블로그 검색] → 구조 분석 → 재편성

→ "교재 참조 + 블로그 검색"이 3~4회 반복된다
→ guides/info-search.md로 추출한다

소프트웨어 비유: 중복 함수를 유틸리티로 추출하는 것과 동일하다. DRY(Don’t Repeat Yourself) 원칙이다.

추출 판단 매트릭스:

반복 횟수 절차 복잡도 판단
1회 - 추출하지 않는다 (인라인)
2회 단순 (1~2줄) 추출하지 않는다 (중복 허용)
2회 복잡 (3줄+) 추출을 고려한다
3회+ - 반드시 추출한다

안티패턴 — 성급한 추상화:

반복이 1회인데 “나중에 쓸 것 같아서” 미리 추출하면 안 된다. 실제로 반복될 때까지 기다린다. 소프트웨어에서 “Rule of Three”와 같은 원칙이다.

2.3 Step 3. 코어 vs 스킬 분류

“이 규칙을 모르면 아무 태스크도 못 하는가?”를 묻는다.

  • Yes → 코어 잔류 (YAML 규칙, 스타일, index 패턴, 카테고리 목록)
  • No → 스킬로 분리 (특정 태스크 절차)

구체적 분류 예시:

[코어 잔류 — 모든 태스크에 필수]
- "한다 체로 쓴다" → 질문 응답에도, 포스트 작성에도 필요 → 코어
- "수식 $ 양쪽에 공백을 넣는다" → 모든 .qmd에 적용 → 코어
- "카테고리 목록과 경로" → 어떤 태스크든 파일 위치 파악에 필요 → 코어
- "index.qmd 링크 패턴" → 포스트 추가/수정 시 항상 필요 → 코어

[스킬 분리 — 특정 태스크에만 필요]
- "TBD 메모에서 ## 구분자를 파싱한다" → TBD 전환에만 필요 → 스킬
- "기존 포스트와 80% 이상 겹치면 보강한다" → 포스트 작성에만 필요 → 스킬
- "시리즈 번호를 ## 제목에 부여한다" → 시리즈 정리에만 필요 → 스킬

lazy loading 원칙: 모든 모듈을 미리 import하지 않고 필요 시점에 로드한다. 프로그래밍에서 import 를 파일 상단에 전부 모으는 대신, 실제 사용 시점에 동적으로 로드하는 것과 같다.

[eager loading — 나쁜 예]
Agent 시작 시 → 코어 + write-post + convert-tbd + organize-series + ... 전부 로드
→ 질문 하나에 답하는데 모든 절차가 컨텍스트에 있다

[lazy loading — 좋은 예]
Agent 시작 시 → 코어만 로드
사용자가 "포스트 써줘" → write-post.md 로드
사용자가 "TBD 전환해줘" → convert-tbd.md 로드
→ 현재 태스크에 필요한 규칙만 컨텍스트에 있다

경계 사례 — “거의 모든” 태스크에 필요한 규칙:

3개 태스크 중 2개에서 필요하면 코어인가 스킬인가? 판단 기준은 빈도보다 필수성이다. “없으면 태스크 자체가 불가능한가”로 판단한다. 2개에서 편리하게 쓰이지만 없어도 태스크가 돌아가면 스킬이다.

2.4 Step 4. 라우팅 테이블

“어떤 요청 → 어떤 스킬?” 매핑을 정의한다.

코어에서 스킬을 분리하면 “어떤 상황에 어떤 스킬을 부르는가” 매핑이 필요하다. 이것은 웹 프레임워크의 URL 라우터와 동일한 역할이다.

라우팅 테이블의 구조:

| 사용자 요청 패턴 | 로드할 스킬 | 선행 스킬 |
|----------------|------------|----------|
| "~에 대해 알려줘", "~ 설명해줘" | answer-question.md | info-search.md |
| "~ 포스트 써줘", "~ 작성해줘" | write-post.md | info-search.md |
| "TBD에서 ~ 전환해줘" | convert-tbd.md | info-search.md |
| "시리즈 정리해줘" | organize-series.md | - |
| "기존 포스트 교정해줘" | retrofit-post.md | info-search.md |

라우팅 테이블 설계 원칙:

  1. 명확한 트리거: 각 스킬의 진입 조건이 겹치지 않아야 한다. “써줘”와 “작성해줘”가 서로 다른 스킬로 라우팅되면 agent가 혼동한다
  2. 의존성 명시: 선행 스킬(prerequisite)을 명시하여 실행 순서를 보장한다
  3. 폴백 규칙: 어떤 패턴에도 매칭되지 않을 때의 기본 동작을 정의한다

소프트웨어 비유 — 웹 프레임워크 라우터:

# Flask URL 라우터와의 대응
@app.route("/question")    →  answer-question.md
@app.route("/post/new")    →  write-post.md
@app.route("/tbd/convert") →  convert-tbd.md
@app.errorhandler(404)     →  "어떤 작업을 원하시나요?" (폴백)

안티패턴 — 라우팅 없는 분해:

스킬은 분리했는데 라우팅 테이블이 없으면, agent가 매번 모든 스킬 파일의 description을 읽고 직접 판단해야 한다. 이것은 URL 라우터 없이 모든 핸들러를 순회하며 매칭하는 것과 같다. 스킬이 5개만 돼도 오분류가 발생한다.

2.5 Step 5. 도메인별 분리

“특정 도메인에만 적용되는 규칙인가?”를 묻는다.

판단 흐름:

이 규칙이 특정 카테고리/도메인에만 적용되는가?
  ├─ Yes → 해당 도메인 가이드로 분리 (예: Statistics/GUIDE.md)
  └─ No  → 코어에 유지

적용 예시:

[도메인 전용 규칙 — 분리 대상]
- "수학적 증명은 Theorem → Proof 구조를 따른다" → Statistics에만 해당
- "Agent 시리즈는 서브폴더로 관리한다" → Agent에만 해당
- "역학 용어는 한영 병기한다" → Experimentation에만 해당

[범용 규칙 — 코어 유지]
- "한다 체로 쓴다" → 모든 카테고리에 적용
- "수식 $ 양쪽에 공백" → 모든 카테고리에 적용

정보 은닉 원칙: 상위 모듈(코어)은 하위 모듈(도메인 가이드)의 내부를 몰라도 된다. 코어는 “Statistics 카테고리의 가이드는 Statistics/GUIDE.md에 있다”는 경로만 알고, 그 안의 구체적 규칙(증명 구조, 교재 목록 등)은 모른다.

[코어가 알아야 하는 것]
category_guides:
  - path: docs/blog/posts/Statistics/GUIDE.md
    category: Statistics

[코어가 몰라도 되는 것 — Statistics/GUIDE.md 내부]
- book_sources: [statistics/, glm/, survival/, ...]
- content_structure: [이론 배경 → 수학적 정의 → 예시 → 코드 → ...]
- cross_references: [Math, Experimentation]

이 구조 덕분에 Statistics의 규칙을 변경해도 코어나 다른 카테고리 가이드에는 영향이 없다.

2.6 Step 6. 전처리/후처리 식별

“모든 태스크 전/후에 공통 실행할 것이 있는가?”를 묻는다.

전처리와 후처리의 구분:

[전처리 — 태스크 시작 전 공통 실행]
info-search.md: 교재 참조 + 블로그 기존 콘텐츠 검색
→ 어떤 태스크든 "현재 어떤 정보가 있는지" 파악하고 시작해야 한다

[후처리 — 태스크 완료 후 공통 실행]
audit.md: YAML 헤더 검증, 스타일 검사, 링크 확인
→ 어떤 태스크든 완료 후 품질 기준을 충족하는지 확인해야 한다

AOP/미들웨어 패턴과의 대응:

[웹 프레임워크 미들웨어]           [프롬프트 구조]
인증 미들웨어 (before)     ←→    info-search.md (전처리)
핸들러 (main)              ←→    write-post.md (본처리)
로깅 미들웨어 (after)      ←→    audit.md (후처리)

이 패턴의 핵심은 횡단 관심사(cross-cutting concern)의 분리이다. 전처리/후처리는 특정 태스크에 속하지 않고 모든 태스크를 “횡단”한다. 각 태스크 스킬 안에 검색 절차와 품질 검사를 중복 작성하면, 절차 변경 시 모든 스킬을 수정해야 한다.

전처리/후처리 식별 기준:

기준 전처리 후보 후처리 후보
3개 이상의 태스크에서 “시작 전”에 실행하는가? Yes → 전처리 -
3개 이상의 태스크에서 “완료 후”에 실행하는가? - Yes → 후처리
이것 없이 태스크가 실행 자체는 가능한가? 가능하면 전처리 (선택적) 가능하면 후처리 (선택적)
이것 없이 태스크 품질이 보장되지 않는가? - 보장 안 되면 필수 후처리

3 분해 전후 비교

6단계를 적용하기 전과 후를 구체적으로 비교한다.

Before — v2.0 단일 파일 (약 800줄):

# AGENT_GUIDE.md

## 질문 응답
1. 교재를 검색한다 (docs/book/)
2. 블로그를 검색한다 (docs/blog/posts/)
3. 답변을 생성한다
...

## 포스트 작성
1. 교재를 검색한다 (docs/book/)          ← 질문 응답과 중복
2. 블로그를 검색한다 (docs/blog/posts/)   ← 질문 응답과 중복
3. 중복 판단한다
4. YAML 헤더를 작성한다
...

## TBD 전환
1. 교재를 검색한다 (docs/book/)          ← 또 중복
2. TBD.qmd에서 메모를 파싱한다
...

## 공통 규칙
- 한다 체로 쓴다
- 수식 $ 양쪽에 공백
...

## Statistics 규칙
- Theorem → Proof 구조
- 교재: statistics/, glm/, survival/
...

## Agent 규칙
- 서브폴더로 시리즈 관리
...

After — v4.0 코어 + 스킬 구조:

AGENT_GUIDE.md (코어, ~200줄)
├── 공통 규칙만 남음
├── 카테고리 경로 목록
└── 라우팅 테이블 (요청 → 스킬 매핑)

guides/info-search.md (~50줄) — 공통 전처리 (중복 제거됨)
guides/write-post.md (~100줄) — 포스트 작성 절차만
guides/convert-tbd.md (~60줄) — TBD 전환 절차만
guides/audit.md (~40줄) — 공통 후처리

Statistics/GUIDE.md (~80줄) — Statistics 전용 규칙
Agent/GUIDE.md (~60줄) — Agent 전용 규칙

효과:

  • 질문 응답 시: 코어(200줄) + info-search(50줄) + answer-question(40줄) = ~290줄만 로드
  • v2.0에서는 800줄 전부 로드 → 63% 컨텍스트 절감
  • 교재 검색 절차 수정 시: info-search.md 하나만 수정 → 변경 영향 범위 1파일
  • v2.0에서는 질문 응답, 포스트 작성, TBD 전환 3곳 모두 수정 필요

4 최종 구조

AGENT_GUIDE.md (코어 — 항상 로드)
├── 공통 규칙 (YAML, 스타일, index, 개행, LaTeX)
├── 카테고리 목록 + 경로
└── 태스크 라우팅 테이블

guides/ (스킬 — 필요할 때만 로드)
├── info-search.md      (공통 전처리)
├── write-post.md       (포스트 작성)
├── convert-tbd.md      (TBD 전환)
├── organize-series.md  (시리즈 정리)
├── answer-question.md  (질문 응답)
├── retrofit-post.md    (기존 포스트 교정)
└── audit.md            (공통 후처리)

Category/GUIDE.md (도메인 규칙 — 해당 카테고리 작업 시만 로드)

5 범용 의사결정 트리

위 6단계를 요약하면 다음과 같다. 이 트리는 어떤 시스템 프롬프트에든 적용 가능하다.

단계 질문 SW 설계 원칙 산출물
1. 독립 태스크 식별 서로 다른 일이 몇 가지인가? SRP 태스크 목록
2. 공통 요소 추출 반복되는 절차가 있는가? DRY 공통 모듈
3. 코어 vs 스킬 이것 없이 아무것도 못 하는가? Lazy loading 코어/스킬 분류표
4. 라우팅 테이블 어떤 요청 → 어떤 스킬? URL routing 매핑 테이블
5. 도메인 분리 특정 도메인 전용 규칙인가? 정보 은닉 도메인 가이드
6. 전/후처리 모든 태스크 전/후에 공통 실행할 것이 있는가? AOP/미들웨어 전처리/후처리 스킬

적용 순서가 중요하다. 1→2→3은 “무엇을 분리하는가”를 결정하고, 4→5→6은 “분리한 것을 어떻게 조직하는가”를 결정한다. 순서를 바꾸면 — 예를 들어 라우팅 테이블을 먼저 만들고 태스크를 식별하면 — 라우팅이 태스크를 왜곡한다.

본질: 소프트웨어 설계의 모듈화 원칙(SRP, 응집도, 결합도, lazy loading, 라우팅)을 프롬프트에 그대로 적용한 것이다.

6 진짜 범용 원칙 3가지

6단계는 멀티태스크형의 구체적 레시피이지만, 그 아래 깔린 3가지 원칙은 어떤 유형의 시스템 프롬프트에든 적용된다. 고객 상담 봇, 코드 리뷰 에이전트, 데이터 파이프라인 관리자 — 무엇이든 이 3가지는 성립한다.

6.1 원칙 1. “항상 필요한 것”과 “때때로 필요한 것”을 분리한다

어떤 유형이든 매 대화마다 필요한 규칙(코어)과 특정 상황에서만 필요한 규칙(모듈)이 존재한다. 이 구분만 해도 절반은 정리된다.

[고객 상담 봇]
항상 필요: 톤/매너 규칙, 금지 표현 목록, 에스컬레이션 기준
때때로 필요: 환불 절차, 배송 조회 방법, 기술 지원 스크립트

[코드 리뷰 에이전트]
항상 필요: 코딩 컨벤션, 보안 체크리스트, 리뷰 톤
때때로 필요: Python 린트 규칙, React 패턴, DB 마이그레이션 규칙

6.2 원칙 2. “무엇(what)”과 “어떻게(how)”를 분리한다

  • 코어: “포스트는 한다 체로 쓴다” (what — 규칙, 선언적)
  • 스킬: “Step 1: YAML → Step 2: 본문 → …” (how — 절차, 명령적)

규칙은 잘 바뀌지 않지만 절차는 자주 개선된다. 변경 주기가 다르면 분리해야 한다. 이것은 소프트웨어 설계에서 “인터페이스와 구현을 분리한다”와 같은 원칙이다.

[변경 주기가 다른 예]
규칙 (what): "교재 출처를 반드시 명시한다" → 거의 바뀌지 않는다
절차 (how): "1. docs/book/에서 검색 → 2. 챕터 번호 확인 → 3. 인용 형식 적용"
             → 교재가 추가되거나 검색 방식이 바뀔 때마다 수정된다

규칙과 절차가 같은 파일에 있으면, 절차를 수정할 때마다 규칙도 함께 diff에 잡힌다.
분리하면 변경 이력이 깔끔해지고, 수정 시 영향 범위가 명확해진다.

6.3 원칙 3. “라우팅이 가능한 구조”로 만든다

모듈로 쪼개놓고 “언제 어떤 모듈을 쓰는가”가 불명확하면 agent가 잘못된 모듈을 로드하거나 아예 로드하지 않는다. 분해한 만큼 라우팅 테이블(또는 조건 분기)이 명확해야 한다.

라우팅의 품질을 판단하는 기준:

기준 좋은 라우팅 나쁜 라우팅
트리거 명확성 요청 패턴이 하나의 스킬에만 매칭 여러 스킬에 동시 매칭 가능
커버리지 모든 요청 유형이 최소 하나의 스킬에 매칭 매칭되지 않는 요청 유형 존재
깊이 선택지가 3~5개 이내 선택지가 10개 이상 (오분류 위험)

선택지가 너무 많으면 계층적 라우팅을 도입한다. 이 주제는 시스템 프롬프트 유형 분류와 혼합 설계에서 상세히 다룬다.

7 관련 주제

선행 지식

후속 주제

관련 포스트

Subscribe

Enjoy this blog? Get notified of new posts by email: