microsoft/SkillOpt — 모델 가중치는 얼려둔 채, 자연어 스킬 문서(best_skill.md)를 에폭·러닝레이트·검증 게이트로 최적화하는 텍스트 공간 옵티마이저. Python 85.9%·HTML 13.0%·Shell 1.1%, MIT.
SkillOpt가 한마디로 뭔지부터.
SkillOpt는 마이크로소프트 리서치가 공개한 텍스트 공간 옵티마이저(text-space optimizer)다. 가중치를 학습하는 게 아니라, 에이전트에게 주는 스킬 문서(자연어 마크다운)를 학습한다. 롤아웃 → 반추 → 패치 집계 → 선택 → 적용 → 검증 게이트로 이어지는 루프를 에폭·러닝레이트·스케줄러·검증 게이트로 돌린다.
최종 산출물은 한 장의 best_skill.md — 다른 에이전트가 그대로 갖다 붙이면 같은 효과를 내는 재사용 가능한 자연어 가중치다.
레포 트리에서 확인 가능한 사실:
트렌딩 이유 + 경쟁 제품 대비 강점.
지난 1년간 "프롬프트 최적화" 영역에는 이미 강력한 선행 연구가 있었다 — 스탠퍼드의 DSPy(파이프라인 자동 컴파일), TextGrad(텍스트 그래디언트), PromptBreeder(돌연변이 기반 진화)가 대표 주자다. 그런데 SkillOpt는 한 발 다른 각도로 들어왔다.
RLHF·DPO·GRPO 같은 가중치 학습은 한 번 돌리는 데 수십만 달러가 든다. SkillOpt는 GPT-4o·Claude·Qwen이 그대로 동결된 상태에서 스킬 문서만 학습한다. 모델을 못 건드리는 클로즈드 API 환경에서도 그대로 적용된다.
DSPy는 자체 컴파일러 모델을, PromptBreeder는 진화 알고리즘 어휘를 쓴다. SkillOpt는 정확히 딥러닝 메탈모델을 그대로 빌려왔다 — epoch, batch_size, learning_rate, lr_scheduler(cosine/linear/constant), gradient clipping, slow update(=momentum), meta skill(=meta-learning)까지. 그래서 ML 엔지니어가 처음 보고 5분 만에 하이퍼파라미터를 튜닝할 수 있다.
단순 진화 알고리즘은 좋아 보이는 변이를 그대로 받아들이다 보면 직전 에폭에서 얻은 성능이 깨진다. SkillOpt는 매 스텝마다 selection split(검증셋)에서 점수 비교 후 통과해야만 패치를 채택하고, 에폭 경계에서는 slow update로 직전 에폭과 종단적(longitudinal) 비교까지 한다. ML에서 검증된 메커니즘 그대로다.
QA(SearchQA), 임바디드 에이전트(ALFWorld), 문서 QA(DocVQA), 수학(LiveMathematicianBench), 코드 생성(SpreadsheetBench), 툴 사용 QA(OfficeQA) — 도메인이 완전히 다른 6개 작업에서 동일한 옵티마이저가 동작한다. 새 벤치마크 추가는 skillopt/envs/_template/ 디렉토리 복사로 시작할 수 있다.
모델을 가르치는 두 가지 방법: ① 학생의 뇌(=가중치)를 수술해서 똑똑하게 만든다(RLHF) ② 학생의 머리는 그대로 두고, 옆에 두는 참고 노트를 계속 다듬는다(SkillOpt). 후자는 학생을 갈아끼워도 노트 그대로 재사용된다.
pyproject.toml과 requirements.txt에서 추출한 진짜 의존성.
SkillOpt 자체는 의존성이 놀랍도록 가볍다. pyproject.toml의 [project] dependencies를 보면 6개뿐이다.
# pyproject.toml — 핵심 의존성
openai >= 1.30.0 # OpenAI / Azure OpenAI SDK
pyyaml >= 6.0 # config YAML 파싱
numpy >= 1.24.0 # 점수/배치 산술
openpyxl >= 3.1.0 # SpreadsheetBench용 .xlsx 처리
azure-identity >= 1.15.0 # Azure CLI / Managed Identity 인증
azure-core >= 1.30.0 # Azure 트랜스포트
httpx >= 0.27.0 # 비동기 HTTP
여기엔 PyTorch도, transformers도, 어떤 ML 프레임워크도 없다. 이게 핵심이다 — SkillOpt는 학습 알고리즘이지만 모델을 학습시키는 게 아니라 텍스트 문서를 학습시키기 때문에, 텐서 라이브러리가 필요 없다. 모든 "그래디언트"는 LLM 호출의 결과로 나오는 문자열 패치다.
skillopt/model/ 디렉토리는 옵티마이저와 타겟에 서로 다른 모델을 자유롭게 꽂을 수 있게 추상화한다.
azure_openai.py — Azure OpenAI 엔드포인트 (API key / Azure CLI / Managed Identity 3가지 인증)claude_backend.py — Anthropic Claude (Claude Agent SDK)qwen_backend.py — 로컬 vLLM 서버의 Qwen 모델codex_backend.py + codex_harness.py — OpenAI Codex CLI 실행 래퍼 (sandbox 모드, 네트워크 차단 옵션)minimax_backend.py — MiniMax M2.7 백엔드 지원common.py — 공통 유틸리티router.py — 백엔드 선택 라우팅backend_config.py — 통합 설정skillopt/optimizer/는 SGD 옵티마이저와 일대일 대응되는 파일들이다.
skillopt/optimizer/
├── clip.py # gradient clipping = 편집 개수 캡
├── lr_autonomous.py # LR을 LLM이 자율적으로 결정하는 모드
├── meta_skill.py # 메타 학습 메모리 (에폭 간 전략)
├── rewrite.py # 전체 재작성 모드 (patch 대신)
├── scheduler.py # cosine / linear / constant LR 스케줄러
├── select.py # top-k 패치 선택 = gradient clipping
├── skill.py # 스킬 문서 객체 + 적용 로직
├── slow_update.py # 에폭 경계 종단 비교 (= momentum)
└── update_modes.py # patch / rewrite_from_suggestions / full_rewrite_minibatch
skillopt/gradient/는 LLM이 트레일을 보고 "이 부분 고쳐"라는 패치를 만드는 부분이다.
reflect.py — 트레일을 보고 분석가(analyst) 프롬프트로 편집 제안aggregate.py — 비슷한 패치를 의미 기반으로 묶음 (계층적 머지)대부분의 벤치마크는 dataloader.py + rollout.py + evaluator.py + adapter.py + skills/initial.md + prompts/*.md 패턴을 따른다. 단, alfworld는 예외로 evaluator.py 대신 reflect.py를 사용하고 vendor/ 디렉토리를 포함한다. 신규 벤치마크는 _template/를 복사해 100줄 정도면 추가된다.
alfworld>=0.4.0 + gymnasium>=0.29.0 (임바디드 에이전트 벤치마크)claude-agent-sdk>=0.1.0vllm>=0.4.0 (로컬 GPU 추론 서버)gradio>=4.0.0 (Gradio 기반 대시보드)mkdocs-material + mkdocstrings[python] (문서 사이트)ruff>=0.4.0 + pytest>=8.0.0skillopt_webui/는 Gradio 한 패키지로 만든 모니터링 대시보드다 (포트 7860, --share 옵션으로 공개 링크 생성). 별도 React/Vue 없음. 프로젝트 페이지 index.html은 mkdocs-material로 빌드되어 microsoft.github.io/SkillOpt에 배포된다.
ASCII 시스템 구조도 + 핵심 설계 패턴.
공식 문서 docs/guide/training-loop.md의 의사 코드를 그대로 ASCII로 옮기면 이렇다.
┌──────────────────────────────────────────────────────────────┐
│ SkillOpt Training Loop │
├──────────────────────────────────────────────────────────────┤
│ │
│ for epoch in range(num_epochs): # ← 4 epochs 기본 │
│ for step in range(steps_per_epoch): # ← batch_size=40 │
│ │
│ ① Rollout (Forward Pass) │
│ target_model(task, skill.md) → trajectory + score │
│ │
│ ② Reflect (Backward Pass) │
│ optimizer_model(failed_trajs) → edit patches │
│ │
│ ③ Aggregate │
│ cluster similar patches → merged patches │
│ │
│ ④ Select (Gradient Clipping) │
│ top_k(patches, k=learning_rate) ← 기본 LR=4 │
│ │
│ ⑤ Update │
│ apply(skill.md, selected_patches) → skill_v(t+1).md │
│ │
│ ⑥ Gate (Validation) │
│ if score(skill_v+1, val) > score(skill_v, val): │
│ accept │
│ else: │
│ reject (← early stopping like) │
│ │
│ ───── Epoch Boundary ───── │
│ ⓐ Slow Update (= Momentum) │
│ compare skill_epoch_t vs skill_epoch_t-1 on same tasks │
│ → 종단 가이던스를 스킬에 주입 │
│ │
│ ⓑ Meta Skill (= Meta-Learning) │
│ optimizer reflects on what changed this epoch │
│ → cross-epoch 전략 메모리 누적 │
└──────────────────────────────────────────────────────────────┘
scripts/train.py
│
▼
skillopt/engine/trainer.py ← 메인 루프 오케스트레이션
│
├──▶ skillopt/envs/<benchmark>/rollout.py [① Rollout]
│ └──▶ skillopt/model/router.py (LLM 호출)
│
├──▶ skillopt/gradient/reflect.py [② Reflect]
├──▶ skillopt/gradient/aggregate.py [③ Aggregate]
│
├──▶ skillopt/optimizer/select.py + clip.py [④ Select]
│ └──▶ skillopt/optimizer/scheduler.py (cosine LR)
│
├──▶ skillopt/optimizer/skill.py [⑤ Update]
│ └──▶ skillopt/optimizer/update_modes.py
│
└──▶ skillopt/evaluation/gate.py [⑥ Gate]
── 에폭 끝 ──
├──▶ skillopt/optimizer/slow_update.py [ⓐ Slow Update]
└──▶ skillopt/optimizer/meta_skill.py [ⓑ Meta Skill]
가중치 동결 + 문서 학습. GPU 메모리·학습 인프라 제로. gpt-5.5·claude-3.5-sonnet·Qwen3.5-4B 어느 백엔드든 동일하게 동작한다.
그래디언트가 실수 텐서가 아니라 구조화된 편집 패치(JSON). 패치는 "addition/modification/deletion" 타입을 가지고, 의미가 비슷하면 머지된다. analyst_workers=16으로 병렬 반추.
모든 스텝이 자동 채택되는 게 아니라, evaluation/gate.py가 검증 분할에서 점수를 재고 통과해야 채택. 통과 못 하면 reject → 직전 스킬로 롤백. 초기 탐색은 공격적, 후반 갱신은 보수적이라는 코사인 LR과 시너지.
에폭 경계에서 직전 에폭과 같은 샘플 20개(slow_update_samples)로 롤아웃하고 improved/regressed/persistent_fail/stable_success로 분류 → 종단 가이드를 스킬에 주입. catastrophic forgetting 방지용 모멘텀이다.
일반 ML 모멘텀이 가중치 업데이트 방향을 기억한다면, 메타 스킬은 옵티마이저의 전략을 기억한다. "이전 에폭에서 X 같은 편집은 reject 됐다 → 비슷한 시도 자제" 같은 메타 노트를 누적해서 다음 반추 단계에 컨텍스트로 제공.
레포 트리(46 commit 시점)에서 직접 추출한 실제 구조.
SkillOpt/
├── README.md
├── pyproject.toml # 빌드 설정, 의존성, 콘솔 스크립트
├── requirements.txt
├── mkdocs.yml # 문서 사이트 빌드 설정
├── index.html # 프로젝트 페이지 (mkdocs로 합쳐짐)
├── .env.example # API 키 템플릿
│
├── configs/ # YAML 기반 하이퍼파라미터
│ ├── _base_/default.yaml # 모든 벤치마크의 베이스
│ ├── searchqa/default.yaml
│ ├── alfworld/default.yaml
│ ├── docvqa/default.yaml
│ ├── livemathematicianbench/default.yaml
│ ├── officeqa/default.yaml
│ └── spreadsheetbench/default.yaml
│
├── docs/ # mkdocs 사이트 소스
│ ├── index.md
│ ├── guide/ # installation, training-loop, dl-analogy …
│ └── reference/ # api, cli, config
│
├── scripts/ # CLI 진입점
│ ├── train.py # 메인 학습 스크립트
│ ├── eval_only.py # 학습된 스킬 평가만
│ └── run_*.sh # 벤치마크별 셸 헬퍼
│
├── skillopt/ # 코어 패키지
│ ├── config.py # YAML 로더 + _base_ 인헤리턴스
│ ├── types.py # 도메인 타입 정의
│ │
│ ├── engine/trainer.py # 메인 트레이닝 루프
│ │
│ ├── model/ # LLM 백엔드 추상화
│ │ ├── router.py
│ │ ├── azure_openai.py, claude_backend.py
│ │ ├── qwen_backend.py, codex_backend.py
│ │ └── codex_harness.py
│ │
│ ├── gradient/ # = backward pass
│ │ ├── reflect.py # 트레일 → 편집 패치
│ │ └── aggregate.py # 패치 머지
│ │
│ ├── optimizer/ # = SGD optimizer
│ │ ├── skill.py, select.py, clip.py
│ │ ├── scheduler.py # cosine / linear / constant
│ │ ├── lr_autonomous.py
│ │ ├── meta_skill.py, slow_update.py
│ │ ├── update_modes.py, rewrite.py
│ │
│ ├── evaluation/gate.py # 검증 게이트
│ │
│ ├── envs/ # 벤치마크별 어댑터
│ │ ├── _template/ # 신규 벤치마크 시작점
│ │ ├── alfworld/
│ │ │ ├── adapter.py, dataloader.py, rollout.py
│ │ │ ├── reflect.py
│ │ │ ├── prompts/ # analyst_*.md, rollout_*.md
│ │ │ ├── skills/initial.md
│ │ │ └── vendor/ # ALFWorld 본가 코드 베포
│ │ ├── searchqa/
│ │ ├── docvqa/
│ │ ├── livemathematicianbench/
│ │ ├── officeqa/
│ │ │ └── tool_runtime.py # 툴 사용 QA 전용
│ │ └── spreadsheetbench/
│ │ ├── codegen_agent.py, react_agent.py
│ │ └── executor.py # 코드 실행 샌드박스
│ │
│ ├── prompts/ # 공용 프롬프트 (분석가/머지/메타)
│ │ ├── analyst_*.md
│ │ ├── merge_*.md
│ │ ├── meta_skill.md
│ │ └── lr_autonomous.md
│ │
│ └── utils/ # json_utils, scoring
│
├── skillopt_webui/ # Gradio 대시보드
│ ├── __main__.py, app.py
│
└── skillopt-assets/ # 논문 그림, 로고, 데모 png
가장 인상적인 건 prompts/ 디렉토리에 analyst_success.md·analyst_error.md·merge_final.md·meta_skill.md 같은 마크다운 프롬프트가 그 자체로 코드와 동급으로 관리된다는 점. 환경별로도 envs/<name>/prompts/가 따로 있다. 이건 "프롬프트도 코드"라는 문화의 살아있는 예다.
이 레포에서 진짜 배워갈 만한 기술별 인사이트.
이 레포의 가장 큰 교훈은 익숙한 추상을 새 영역에 그대로 옮겨도 작동한다는 것이다. docs/guide/dl-analogy.md의 매핑 표를 한국어로 옮기면:
문서가 직접 말하는 "전이되는 직관"과 "전이 안 되는 직관":
① 코사인 스케줄 > 상수 — DL과 동일하게 코사인 어닐링이 수렴에 유리
② 중간 LR(4~16)이 최적 — 너무 작으면 학습 느림, 너무 크면 노이즈
③ 슬로우 업데이트가 catastrophic forgetting 방지
④ 메타 스킬 메모리가 옵티마이저의 반추 품질을 올림
① 배치 크기 ≠ 좋음 — API 비용 때문에 큰 롤아웃 배치는 수익체감
② 에폭 많이 ≠ 좋음 — 스킬은 신경망보다 빨리 수렴, 보통 2~4 에폭이면 충분
configs/_base_/default.yaml을 모든 벤치마크가 상속하는 패턴 — mmcv·detectron2 같은 비전 프레임워크에서 익숙한 그 구조다. configs/searchqa/default.yaml은 첫 줄이 _base_: ../_base_/default.yaml로 시작하고, 위에서 필요한 값만 오버라이드한다.
설정의 model.optimizer와 model.target이 분리돼 있어, 큰 모델로 학습한 스킬을 작은 모델에 배포하는 시나리오가 자연스럽게 가능하다. 옵티마이저는 GPT-5.5, 타겟은 Qwen3.5-4B 같은 조합도 OK.
skillopt/prompts/에 있는 analyst_success.md, analyst_error.md, analyst_*_rewrite.md, analyst_*_full_rewrite.md 3단 구성은 한 가지 작업을 3가지 "편집 강도"로 다룰 수 있게 한 정교한 프롬프트 엔지니어링 사례다. 분석 깊이가 다른 프롬프트를 여러 버전으로 두고 동적 선택하는 패턴.
optimizer/lr_autonomous.py + prompts/lr_autonomous.md가 흥미롭다. lr_control_mode: autonomous로 두면, 일반적인 코사인 스케줄러가 아니라 옵티마이저 LLM 자신이 현재 진행 상황을 보고 다음 스텝의 LR을 제안한다. "메타 옵티마이저"가 하이퍼파라미터를 자기조절하는 셈.
실제로 돌리려면 뭐가 필요한가.
pyproject.toml의 requires-python = ">=3.10". 3.10/3.11/3.12 분류자만 명시. Walrus operator, structural pattern matching 등 신문법 사용 추정.
Azure OpenAI (권장), OpenAI 직접, Anthropic Claude 중 하나의 API 키 또는 Azure CLI 인증. .env.example에 모든 변수가 정리돼 있다. 학습 한 번에 LLM 호출이 수천 번 발생하므로 비용을 사전에 가늠해야 한다 — batch_size=40, 4 epochs, 6개 작업 평균 분석 라운드 3회 기준 적게 잡아도 수만 토큰 × 수백 스텝.
Qwen/Qwen3.5-4B 등 로컬 모델을 타겟으로 쓰려면 vLLM 서버 + 4B급은 24GB VRAM(예: RTX 4090), 더 큰 모델은 A100/H100급 필요. 옵티마이저는 클라우드(GPT/Claude), 타겟은 로컬 Qwen 같은 하이브리드 구성이 비용 최적.
pip install -e ".[alfworld]" 후 alfworld-download 명령으로 데이터셋 받음. SearchQA·DocVQA 등은 데이터 자동 포함이 아니라 사용자가 직접 train/val/test 분할 JSON을 준비해야 한다(README 명시).
코드 생성 작업이라 spreadsheetbench/executor.py가 생성된 Python을 안전 실행해야 함. openpyxl로 .xlsx 검증. 별도 컨테이너 권장.
SkillOpt는 PyTorch보다 scikit-learn 같은 무게감이다. GPU 없이도 코드는 돌아간다. 다만 진짜 비용은 OpenAI/Claude API 청구서로 나간다. "1 epoch ≈ 한 끼 점심값"이라는 농담이 통하는 카테고리.
난이도별 5개 — 위에서 아래로 차근차근.
configs/searchqa/default.yaml의 train.num_epochs를 1, batch_size를 8, train_size를 32로 줄여 가벼운 실행. outputs/<run>/skills/skill_v0001.md가 빈 스킬에서 어떻게 자라는지를 diff로 본다. 학습 포인트: rollout → reflect → gate 전체 사이클을 한 번 직접 보기.
skillopt/envs/alfworld/skills/initial.md를 읽고, "Common Mistakes to Avoid" 섹션의 항목 하나를 일부러 삭제한 뒤 학습 돌리기. 옵티마이저가 삭제한 항목을 다시 발견해서 추가하는지 관찰. 학습 포인트: 반추(reflect) 능력의 실체.
같은 시드(train.seed=42)로 lr_scheduler를 constant, linear, cosine, autonomous 4종 돌려서 history.json의 단계별 검증 점수를 플롯. DL에서 봤던 곡선과 비슷한 패턴이 나오는지 확인. 학습 포인트: 하이퍼파라미터가 LLM 영역에서도 ML과 똑같이 작동한다는 직관.
skillopt/envs/_template/를 kor_qa/로 복사하고, KorQuAD 데이터를 dataloader.py로 로드. evaluator.py는 정답 문자열 부분 일치(F1) 기준으로 작성. configs/kor_qa/default.yaml 추가. 학습 포인트: 옵티마이저는 그대로, 환경 어댑터만 갈아끼우면 새 도메인에 옮겨가는 추상화의 힘.
옵티마이저는 클라우드 GPT-4o, 타겟은 로컬 vLLM의 Qwen3.5-4B로 학습. 학습이 끝난 best_skill.md를 다시 더 작은 Qwen2.5-1.5B에 그대로 붙여 평가 — 학습 때보다 작은 모델에서 얼마나 성능이 유지되는지 측정. 학습 포인트: "자연어 가중치"의 모델 간 전이 가능성을 실증.
SkillOpt를 깊이 이해하려면 어디부터 봐야 하나 — 4주 코스.
SGD → Momentum → Adam의 진화, learning rate scheduling(cosine annealing, warmup), gradient clipping, early stopping, validation-based selection의 ML 이론. 추천 자료: "Deep Learning" (Goodfellow et al.) 8장, fast.ai 코사인 스케줄러 노트. 매핑 표를 외워두면 SkillOpt 코드가 즉시 읽힌다.
DSPy(Khattab et al., Stanford) — 파이프라인 자동 컴파일, BootstrapFewShot/MIPROv2. TextGrad(Yuksekgonul et al.) — LLM이 "텍스트 그래디언트" 생성. PromptBreeder(DeepMind) — 자기진화 프롬프트. OPRO(Google) — Optimization by PROmpting. SkillOpt가 이들과 어떻게 다른지 비교 노트 작성.
SkillOpt가 지원하는 6개 벤치마크의 원본 논문 읽기 — ALFWorld(임바디드 텍스트 게임), SearchQA(open-domain QA), DocVQA(문서 VQA), SpreadsheetBench(코드+표 작업). 평가 메트릭의 차이 — F1, exact match, success rate, pass@1 — 이 옵티마이저 동작에 어떻게 영향을 주는지.
SkillOpt의 Meta Skill은 MAML(Model-Agnostic Meta-Learning) 직계가 아니지만 같은 가족. Karpathy의 "LLM OS" 강연, Anthropic의 "Building effective agents" 글, Microsoft의 AutoGen·Magentic-One 코드를 함께 읽으면 SkillOpt가 큰 그림의 어디에 위치하는지 보인다.
SkillOpt를 읽다 막힐 만한 용어 한 묶음.
split_mode, split_seed: 42.workspace-write, 네트워크 차단, 승인 정책까지 세밀 제어. SpreadsheetBench·SWEBench류 코드 실행 작업용.레포 + 논문 + 관련 선행 연구.