# SPEC-lifecycle: Kandidaten, Graduierung, Training Pakete: `knaster.lifecycle`, `knaster.graduation`. ## 1. Kandidat ``` id, kind: base_model | adapter | quantization | embedding_model | engine slot: str # betroffener Slot (bei adapter: Slot, dessen Basis passt) target: {...} # z. B. {base_model: "qwen3-14b-awq"} oder {cluster: "rechnungen"} baseline: {pass_rate?} # Messwert der aktuellen Version state, metrics{}, history[{ts, from, to, note}] ``` ## 2. Zustände ``` proposed ──(Freigabe)──▶ building ──report(build_ok)──▶ evaluating evaluating ──report(eval: pass_rate ≥ baseline − tolerance)──▶ shadow (sonst ▶ rejected) shadow ──report(shadow: agreement ≥ min_agreement, n ≥ min_n)──▶ canary (sonst ▶ rejected) canary ──report(canary: pass_rate ≥ baseline − tolerance, n ≥ min_n)──▶ awaiting_promotion (sonst ▶ rejected) awaiting_promotion ──(Freigabe)──▶ promoted promoted ──rollback──▶ rolled_back jeder Zustand ──report(error)──▶ failed ``` Grenzwerte stehen in `config/lifecycle.yaml`: `tolerance` 0,02, `min_agreement` 0,85, `min_n` 50, `canary_share` 0,1. Die Freigaben laufen über die Policy (`create_candidate`, `promote_candidate`). **Promote** schreibt in den Registry-Laufzeitzustand bzw. in die Adapterliste: - `base_model`: Slot `base_model` wird umgestellt, alle Adapter der alten Basis werden `retired`, und für jeden Cluster mit Adapter entsteht ein Kandidat `adapter` (Neutraining). - `adapter`: Der neue Adapter wird `active`, der bisher aktive für denselben Cluster und dieselbe Basis wird `retired` (bleibt als Rollback-Ziel erhalten). - **Rollback** stellt den Zustand vor dem Promote wieder her (gespeichert in `history`). Die Jobs selbst (Download, Quantisierung, Training, Eval) laufen **extern**, als Job-Runner auf `gpu-flex`. Sie melden ihr Ergebnis per `POST /v1/lifecycle/candidates/{id}/report`. Der `JobRunner` im Service stößt den Job an: Im Dry-Run wird nur protokolliert, sonst geht ein Webhook an n8n bzw. den Runner. ## 3. Graduierung (`knaster.graduation`) `graduate(skill, variants[], run_fn, verify_fn, threshold=0.9, min_variants=10) -> GraduationReport` - Jede Variante wird mit `stage = 1` auf dem Skill-Slot ausgeführt (`run_fn`) und geprüft (`verify_fn`). - `passed = pass_rate ≥ threshold` und `n ≥ min_variants`. - Bei Erfolg entsteht ein Vorschlag `promote_skill{skill, stage: 1}` (Policy: approve), bei Misserfolg ein Bericht mit den fehlgeschlagenen Varianten. Diese gehen an das starke Modell, das den Skill überarbeitet. - Die Varianten erzeugt das starke Modell (Hermes) und schickt sie an `POST /v1/skills/{name}/graduate`. ## 4. Adapter-Training (extern, Vertrag) Eingang für den Trainingsjob: Export `cases` (split = train, passed = true, skill.cluster = X) plus optional `preference_pairs`. Ausgang: Adapter-Verzeichnis und `report(build_ok, adapter_name)`. Die Eval nutzt die Fälle mit split = eval desselben Clusters.