| name | metis-finetune-eval |
| description | End-to-end playbook to fine-tune and evaluate an LLM on ThakiCloud Metis (kubeflow-llm-training + vLLM serving on compute-h200). Covers served-model data generation, dataset→S3, TrainJob submission (with every admission gotcha), LoRA adapter serving for inference, and gate-based A/B eval. Use when "모델 학습시켜줘", "파인튜닝 돌려줘", "TrainJob 띄워줘", "학습 전후 평가/A/B", "데모 GPU로 학습", "fine-tune on the cluster", "train and evaluate", "serve the adapter and compare". General across models. Do NOT use for local/Colab training (use colab-unsloth-finetune) or serving-only ops (use demo-llm-inference-test). |
Metis Fine-tune & Evaluate (playbook)
ThakiCloud Metis(kubeflow-llm-training + vLLM)에서 데이터 생성 → 학습(TrainJob) → 어댑터 서빙 →
게이트 기반 A/B 평가까지의 검증된 절차. 2026-07-14 TDS-UI-generator 실전에서 추출. 능력은
harness가 아니라 이 계약에 있다([[thin-harness-fat-skills]]). 평가는 실행 게이트로 닫는다
([[close-the-agent-loop]]·[[evaluator-must-act]]).
0. 클러스터 지형 (정본)
| 대상 | 값 |
|---|
| GPU 학습/서빙 컨텍스트 | tkai-prod-compute-h200 (cluster id cc3adee7-4c6b) |
| 서빙 평면 | tkai-prod-mgmt |
| 프로젝트 네임스페이스 | project-<uuid> (Kueue localqueue default → ClusterQueue gpu-cluster-queue) |
| TrainJob runtime | llm-training (trainer.kubeflow.org/v1alpha1, torch), 이미지 cr2.thakicloud.net/ai-platform/kubeflow-llm-training |
| S3 (모델·데이터·출력) | s3://ai-platform/..., endpoint http://objects3-dev.thakicloud.net, 시크릿 tkai-s3-secret(4키) |
| 서빙 이미지 | vllm/vllm-openai (dockerhub, no pull secret), S3 로드 = --load-format=runai_streamer |
| GPU LimitRange | Container/Pod max cpu 8 / mem 80Gi (초과 시 admission 거부) |
unset KUBECONFIG 후 --context tkai-prod-compute-h200 사용. 모델은 s3://ai-platform/global/models/<hf-id> (bf16 학습본 존재; NVFP4는 서빙본).
1. 데이터 생성 (서빙 모델 = 부트스트랩 생성기)
served vLLM은 pod ingress URL로 직접 호출(https://<hash>-8000.demo.thakicloud.net?cluster=<id>).
⚠️ demo.thakicloud.net은 SPA soft-404 — 반드시 pod ingress URL을 쓴다. /v1/models로 모델명 확인.
Qwen3.6은 reasoning 모델 → chat_template_kwargs:{"enable_thinking":false} 안 하면 content=null.
재사용: .claude/skills/tds-ui-generator/scripts/vllm_client.py(enable_thinking off + JSON 추출).
2. 데이터셋 → S3 (⛔ 로더 계약 필수)
- SFT 로더는
messages 컬럼만 유지(prompt/completion은 strip). → {"messages":[{role,content}...]} 포맷.
- DPO는
prompt/chosen/rejected. GRPO는 prompt/answer.
- 전용 prefix에 train split만 격리(로더가 prefix 내 모든 jsonl을 먹으니 report.json 등 섞지 말 것).
- boto3 업로드:
tkai-s3-secret의 4키(OBJECT_ACCESS_KEY_ID/SECRET/ENDPOINT_URL/REGION) + endpoint.
- held-out eval split도 같은 messages 포맷으로 별도 prefix 업로드.
3. 학습 (TrainJob) — env 계약 + admission 게이트
템플릿: assets/trainjob-template.yaml. env는 train_entrypoint.build_config_from_env() 계약:
METHOD(sft|cpt|dpo|grpo|gkd) · MODEL_PATH(s3 bf16) · DATASET_PATH(s3 prefix) · OUTPUT_PATH(s3) ·
STRATEGY(ddp|fsdp) · USE_LORA/LORA_R/LORA_ALPHA/LORA_DROPOUT · QUANTIZATION(none|4bit|8bit) ·
EPOCHS/MAX_STEPS/BATCH_SIZE/GRAD_ACCUM_STEPS/LEARNING_RATE/MAX_LENGTH(⚠️기본 128 과소, IR엔 2048) ·
EVAL_DATASET_PATH+EVAL_STEPS(held-out eval 곡선 = A/B 무료, 어댑터 인메모리라 merge 불요).
⛔ admission 3중 게이트(순서대로 다 걸린다):
- Kyverno: 모든 컨테이너에 cpu·memory requests+limits 필수(GPU만 지정 시
JobCreationFailed).
- LimitRange: limits ≤ cpu 8 / mem 80Gi(초과 시
requests must not be above the limitRange max).
- MultiKueue:
labels: kueue.x-k8s.io/queue-name: default, GPU 여유 없으면 Pending(preempt 금지 — 데모 서빙 보호). GPU 포화면 nvidia.com/gpu: "1"로 축소해 1장만 비면 시작.
제출 전 kubectl apply --dry-run=server로 검증. 상태: get trainjob, get workloads(ADMITTED), pod 로그.
4. 어댑터 서빙 (inference/A/B용) — s3-lora 함정
템플릿: assets/serve-lora-template.yaml. bf16 base + --enable-lora --max-lora-rank <r> --lora-modules=<name>=/adapter.
⛔ vLLM LoRA 로더는 s3:// 미지원(runai_streamer는 --model에만) → --lora-modules=tds=s3://...은
No adapter found로 crash loop. initContainer가 어댑터를 S3→로컬 emptyDir(/adapter)로 다운로드 후
로컬 경로 지정. base는 --model=s3://... + --load-format=runai_streamer로 직접 로드(OK).
포트포워드로 /v1/models 확인(base+<adapter> 노출) → model=<adapter>로 생성.
5. A/B 평가 (게이트 기반, 실행이 유일 유효 지표)
재사용: .claude/skills/tds-ui-generator/scripts/ab_eval.py(held-out → 생성 → 게이트 통과율 + F1).
before=base 서빙, after=어댑터 서빙, 동일 게이트·동일 held-out. 교훈(3중 프록시 실패):
compile-ok·teacher-forced token-acc·light-LoRA의 token-acc는 모두 autoregressive 생성 품질과
어긋난다 → 완전한 registry/스펙에 대한 실행 게이트만 진짜 갭을 드러낸다. 게이트 스펙이
불완전하면 valid 출력을 false-negative로 떨구니, 평가 전 스펙 전수 추출로 완성한다.
6. 정리 (필수)
평가 후 서빙 Deployment·완료 TrainJob 삭제로 GPU 반환([[destructive-batch-guard]]는 삭제 스코프 확인).
포트포워드 kill. 데모 서빙(bench-*)은 건드리지 않는다.
gotchas (실패에서)
- reasoning 모델 → enable_thinking off 안 하면 빈 응답. SPA soft-404 URL 주의.
- SFT 로더
messages-only. MAX_LENGTH 기본 128. Kyverno/LimitRange/Kueue 3중 게이트.
- vLLM LoRA는 로컬 경로만(s3 crash). LoRA는 어댑터만 저장(EVAL은 PEFT 미지원 → merge 또는 서빙).
- token-acc 포화 = 생성 품질 아님. 게이트 스펙 완성도가 판별력을 좌우.
- 300-step 경량 LoRA는 27B 생성을 잘 못 바꿈 — 데이터가 실제 계약을 시연해야, 충분히 길게.
워크드 예제 (전 과정 실동작)
.claude/skills/tds-ui-generator/ — 이 플레이북의 첫 실전 적용(데이터엔진·게이트·학습·서빙·A/B 스크립트 정본).