47 lines
3.0 KiB
Markdown
47 lines
3.0 KiB
Markdown
# 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.
|