MINERVA Phase C-7 — 스킬 정의 (프롬프트 + 도구 + 파라미터 + 메타의 표준 단위)

150+ 프롬프트를 관리할 때 한 번 단위를 정확히 정의해야 한다 — SKILL.md 명세, 스킬 vs 에이전트, 합성 가능성

150개를 넘기는 프롬프트·도구 변형을 관리하려면 단순 prompt 카탈로그로는 부족하다. 본 편은 스킬을 4 컴포넌트(System prompt + Tools + Parameters + Metadata)의 표준 단위로 정의하고, SKILL.md 명세, 스킬 vs 에이전트의 단위 차이, 런타임 instantiation, 합성 가능성, C24 하네싱·C26 생명주기 통합, MINERVA 적용을 정리한다. Phase C-7의 첫 토대.

Agent
저자

Kwangmin Kim

공개

2026년 05월 06일

1 왜 “스킬”이라는 단위인가

운영 초반의 MINERVA는 prompt 1~5개. 1년 차에 30개, 2년 차에 150개를 넘긴다. 단순 카탈로그로는 다음을 못 한다.

단순 prompt 카탈로그의 한계 결과
같은 prompt가 여러 에이전트에 복사·붙여넣기 한 군데 수정 → 다른 곳 누락
prompt마다 필요한 도구·파라미터·예시가 흩어짐 사용 시마다 직접 조립
같은 의도에 비슷한 prompt가 5개 — 차이 모름 중복·쇠퇴·의사결정 어려움
prompt 단위 테스트·생명주기 관리 불가 C26 6단계 게이트 적용 못 함
LLM이 동적으로 스킬 선택할 토대 없음 C29 동적 라우팅 어려움

스킬(Skill)은 “독립 사용 가능한 의미 단위”로 prompt를 묶는다. 4 컴포넌트가 하나의 패키지로 다닌다.

정의: 스킬 (Skill)

LLM이 특정 작업을 수행할 수 있도록 하는 자족적 단위. 시스템 프롬프트·필요한 도구·받는 파라미터·메타데이터가 한 묶음으로 정의된다.

  • 자족성: 다른 정보 없이 호출 가능
  • 합성 가능성: 다른 스킬·에이전트의 부품으로 사용
  • 버전·생명주기: C26 6단계로 독립 관리

2 4 컴포넌트

SKILL = (System Prompt, Tools, Parameters, Metadata)
         └────┬────┘  └─┬─┘  └────┬────┘  └────┬────┘
              │         │         │              │
              │         │         │              └── owner·version·examples·tags
              │         │         └── Pydantic 입력 schema
              │         └── allow list (C24 Tool Guard 통합)
              └── role·instruction·few-shot·output 형식

각 컴포넌트가 명시적이고 버전 관리됨. 묵시적 의존(예: “이 prompt는 reranker가 BGE일 때만 작동”)은 컴포넌트로 끌어올린다.

3 SKILL.md 표준

각 스킬은 한 디렉토리. SKILL.md가 단일 진실:

skills/
├── summarize_doc/
│   ├── SKILL.md              # 명세 (사람·도구가 읽음)
│   ├── prompt.j2              # 시스템 프롬프트 (Jinja2 템플릿)
│   ├── fewshot.yaml          # few-shot 예시
│   ├── tests/
│   │   ├── golden.yaml        # C30 골든셋
│   │   └── eval.py
│   └── CHANGELOG.md          # 버전 변경 기록
└── extract_facts/
    └── ...
# skills/summarize_doc/SKILL.md (frontmatter)
---
id: summarize_doc
version: 1.3.0
owner: kmkim@example.com
description: 긴 문서를 청크 단위로 요약하고 통합한다
tags: [summarization, multi-step]

inputs:
  document: {type: string, required: true}
  target_length: {type: integer, default: 500}
  language: {type: string, default: ko, enum: [ko, en]}

outputs:
  summary: {type: string}
  citations: {type: array, items: {chunk_id: string}}

tools_required: [chunk_text, llm_summarize]
tools_optional: [translate]

prompt_file: prompt.j2
fewshot_file: fewshot.yaml

stage: active                    # C26 lifecycle stage
governance: pending_review

dependencies:
  models: [gpt-4o, claude-3.5]    # 어느 LLM 모델에서 검증됨
  reranker: any
  min_context_length: 32000

examples:
  - input:
      document: "..."
      target_length: 200
    expected_summary_traits: [factual, no_quotes, citation_per_paragraph]
---

# Summarize Document Skill

## 목적

긴 문서(>4k 토큰)를 청크 단위로 요약 후 통합 요약 생성.

## 사용 사례

- 사내 정책 문서 요약
- 회의록·논문·보고서

## 한계

- 표·이미지가 많은 문서는 정확도 하락
- 매우 기술적인 도메인은 별도 fine-tuned 스킬 권장 (예: code_summarize)

이 frontmatter가 자동 도구·검색·검증·라우팅의 단일 진실. 사람이 읽을 본문은 그 아래.

4 스킬 vs 에이전트 — 단위 차이

차원 스킬 (Skill) 에이전트 (Agent)
단위 한 가지 능력 여러 능력의 조합
라우팅 호출자가 명시 또는 LLM이 선택 외부 라우터(supervisor)가 결정
State 무상태 또는 단순 다중 단계 state 보유 (LangGraph)
호출 함수처럼 (input → output) session·trace 보유
권한 스킬 자체 권한 + 호출 에이전트 권한 자체 권한 매트릭스
예시 summarize_doc·extract_facts·translate qna_chatbot·code_agent·finance_analyst

스킬 = 함수, 에이전트 = 그 함수들을 사용하는 워크플로

02-1 BaseAgent v2에이전트 contract다. 스킬은 그 안에서 호출되는 더 작은 단위.

5 런타임 Instantiation

선언된 스킬을 실제 호출 가능한 객체로 만드는 단계.

# app/skills/loader.py
from pathlib import Path
import yaml
from jinja2 import Template


class Skill:
    def __init__(self, manifest: dict, prompt_template: Template, fewshot: list):
        self.id = manifest["id"]
        self.version = manifest["version"]
        self.manifest = manifest
        self.prompt_template = prompt_template
        self.fewshot = fewshot

    def build_prompt(self, **kwargs) -> str:
        # 입력 schema 검증
        InputModel = self._build_input_model()
        validated = InputModel(**kwargs)

        # 프롬프트 렌더링
        return self.prompt_template.render(
            **validated.dict(),
            fewshot=self.fewshot,
        )

    def required_tools(self) -> list[str]:
        return self.manifest["tools_required"]

    def _build_input_model(self):
        # frontmatter inputs → Pydantic model 동적 생성
        ...


def load_skill(skill_dir: Path) -> Skill:
    manifest = parse_manifest(skill_dir / "SKILL.md")
    prompt = Template((skill_dir / manifest["prompt_file"]).read_text())
    fewshot = yaml.safe_load((skill_dir / manifest["fewshot_file"]).read_text())
    return Skill(manifest, prompt, fewshot)

이 instantiation이 매번 호출 시 발생하면 비효율 — registry 단위 캐시:

# app/skills/registry.py
class SkillRegistry:
    def __init__(self):
        self._skills: dict[str, Skill] = {}

    def get(self, skill_id: str, version: str | None = None) -> Skill:
        key = f"{skill_id}@{version or 'active'}"
        if key not in self._skills:
            self._skills[key] = load_skill(resolve_path(skill_id, version))
        return self._skills[key]

6 스킬 ↔︎ Tool ↔︎ Agent 관계

[Tool]
  └─ 외부 시스템 호출 (search, db_query, http_get, send_email)

[Skill]
  ├─ system prompt
  ├─ Tools 사용 (allow list)
  ├─ Parameters 받음
  └─ output 반환

[Agent]
  ├─ 여러 Skill 호출
  ├─ State 관리
  ├─ 라우팅·composition
  └─ user-facing

Tool은 가장 작은 단위 (외부 호출), Skill은 한 의미 작업, Agent는 여러 Skill의 워크플로.

18-LangGraph 폴더 #16 SKILL.md 명세 가 이 단위를 처음 정의한 reference.

7 합성 가능성 — Skill Composition

스킬이 다른 스킬을 호출할 수 있다.

# Pseudo — answer_with_summary 스킬이 summarize_doc + qa 두 스킬 사용
class AnswerWithSummary(Skill):
    def execute(self, query: str, document: str):
        summary = registry.get("summarize_doc").execute(document=document, target_length=500)
        return registry.get("qa_chatbot").execute(query=query, context=summary)

조합 패턴 — C29 스킬 조합에서 깊이 다룸. 본 편은 단위 단위 정의에 집중.

8 C24 하네싱과 통합

스킬별 도구 권한이 자동으로 Tool Guard에 들어감:

# app/skills/runtime.py
def execute_skill(skill_id: str, agent_context: AgentContext, **kwargs):
    skill = registry.get(skill_id)

    # 1. 호출 에이전트가 이 스킬을 쓸 권한 있나
    if not has_skill_access(agent_context.agent_id, skill_id):
        raise PermissionError()

    # 2. 스킬이 요구하는 도구가 에이전트 권한에 있나
    for tool in skill.required_tools():
        if tool not in agent_context.allowed_tools:
            raise PermissionError(f"tool {tool} not allowed for agent")

    # 3. 입력 검증 (스킬의 Pydantic schema)
    prompt = skill.build_prompt(**kwargs)

    # 4. LLM 호출 (C25 데코레이터로 wrap)
    response = with_retry(with_timeout(llm.complete))(prompt)

    # 5. 출력 검증 (Output Guard + 스킬 output schema)
    return validate_output(response, skill.manifest["outputs"])

스킬이 새 도구를 요구하면 — 호출 에이전트의 권한 매트릭스 추가 필요. governance 게이트 적용.

9 C26 생명주기 — 스킬 단위 적용

C26 6단계 그대로 스킬에:

draft       → 작성 중, golden test 작성
canary      → 5% 사용 (특정 에이전트의 일부 query)
staged      → 50% A/B
active      → 100%
deprecated  → 후속 스킬 도입, sunset 30일
retired     → 호출 불가

스킬은 에이전트보다 변경 빈도가 높음 — 일주일에 5~10개 promote가 흔함. 자동화 필수.

# scripts/skill_promote.py
def promote_skill(skill_id: str, target_stage: str):
    record = db.skills.get(skill_id)
    # ... C26 패턴 그대로 적용

10 MINERVA 적용

skills/
├── summarize_doc/             # 단순 요약
├── extract_facts/              # 사실 추출
├── compare_documents/          # 문서 비교
├── translate/                  # 번역
├── code_explain/               # 코드 설명
├── code_review/                # 코드 리뷰
├── meeting_summary/            # 회의록 요약
├── citation_check/             # 인용 검증
└── ...                         # 150+개

app/skills/
├── manifest.py                # SKILL.md 파서·검증
├── loader.py                   # 디렉토리 → Skill 객체
├── registry.py                 # 캐시·resolve
├── runtime.py                  # 권한·검증·실행
├── input_schema.py             # 동적 Pydantic 생성
└── output_guard.py             # output schema 검증

scripts/
├── skill_register.py          # CLI 등록
├── skill_promote.py            # CLI 단계 전이
└── skill_audit.py              # 사용 빈도·중복·deprecated 탐지

기존 02-1 BaseAgent v2 contract와 결합 — 모든 도구 호출이 본질적으로 스킬 호출.

11 자주 발생하는 함정

11.1 Skill Bloat

같은 일에 5~10개 스킬 — summarize_doc·summarize_pdf·summarize_meeting·make_summary·brief. 사용자·LLM 모두 어느 것 쓸지 결정 부담.

해법: - 스킬 신규 도입은 사전 등록 게이트 — 기존 스킬과의 차이 명시 - 분기마다 사용 빈도 audit — 6개월 사용 < 100회면 deprecation 검토 - 비슷한 스킬은 동일 스킬 + parameter 형태로 통합 권장

11.2 Implicit Dependencies

SKILL.md에 명시 안 된 의존이 prompt 안에 숨어 있음 — 예: “회사명을 ’AnyCorp’로 가정하고 답하라”는 prompt가 다른 회사 도입 시 깨짐.

해법: - 가정·환경·외부 사실은 parameters로 끌어올려 명시 - prompt에서 변수가 hardcode되어 있으면 SKILL.md inputs로 이동 - 분기마다 prompt audit — 회사·날짜·이름 hardcode 검출

11.3 Tool Overlap

스킬 A·B가 같은 외부 호출을 다르게 wrap. 한쪽 외부 API 변경 시 한쪽만 fix.

해법: 외부 호출은 공통 Tool로 분리. Skill은 Tool을 사용만, 직접 wrap 안 함.

11.4 Prompt Drift

같은 의도의 prompt가 시간 지나며 점차 달라짐 — 한 번 누가 ad-hoc 수정 후 다음 사람이 그 위에 또 ad-hoc.

해법: - Skill 변경은 PR + CHANGELOG.md 강제 - C30 골든셋이 회귀 catch - 분기마다 비슷한 스킬간 prompt 유사도 dashboard

11.5 “Active 스킬을 직접 수정”

draft·canary 단계 안 거치고 active SKILL.md를 직접 PR 머지 → production 즉시 영향, 회귀 발견 늦음.

해법: - active 스킬 SKILL.md는 protected (사람 게이트 강제) - 새 변경은 새 version으로 → C26 lifecycle 진입

11.6 Schema Drift

Skill의 output schema가 변경되면 호출 에이전트들이 깨짐. C20 대화 로깅 schema drift와 같은 문제.

해법: - output schema는 backward compat 의무 (필드 추가 OK, 제거 X) - 큰 변경은 새 스킬 (summarize_doc_v2) - 호출자는 schema version pinning

12 정리

영역 핵심
4 컴포넌트 system prompt·tools·parameters·metadata
표준 SKILL.md frontmatter (단일 진실)
스킬 vs 에이전트 능력 단위 vs 워크플로, 함수 vs 워크플로
Instantiation manifest + Jinja2 + Pydantic = Skill 객체
Tool·Skill·Agent 외부 호출 → 의미 작업 → 워크플로
Composition 스킬이 다른 스킬 호출 (C29)
통합 C24 권한·C26 생명주기·C30 테스트
함정 bloat·implicit deps·tool overlap·prompt drift·active 직접 수정·schema drift

13 응용 분야

시나리오 활용
새 사내 도구 도입 스킬 1개 신규 + tools_required에 매핑
다국어 지원 같은 스킬 + parameters.language로 분기
도메인 특화 (재무·R&D) 도메인별 스킬 그룹 (skills/finance/, skills/rnd/)
사용자 권한별 분기 C24 권한 매트릭스에 스킬별 entry
프롬프트 hot-fix 새 version (1.3.0 → 1.3.1) → canary 5%
스킬 회귀 회피 C30 골든셋 자동 회귀

14 관련 주제

선행 학습 (선수)

18-LangGraph 시리즈 cross-reference

  • #14 스킬 설계와 생성 — 스킬 단위 정의의 이론적 토대
  • #16 SKILL.md 명세 — 본 편 SKILL.md 표준의 origin
  • #21 스킬 프롬프트 아키텍처 — prompt·fewshot 구조

후속 (Phase C-7)

Cross-reference

Subscribe

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