radar: Kandidaten messen mit der schnellsten Draft-Variante, Bild-Probe und Betrieb wie heute mit Zwilling
Ampel / ampel (push) Failing after 22s

Das heutige Modell misst mit seinem Draft (Hirn 106 t/s, ohne 68), Kandidaten liefen ohne -
Ornith fiel am 24.09. deshalb durch, obwohl es dieselbe Architektur hat. Jetzt probiert das Radar
kurz: ohne Draft, eingebauten MTP-Kopf und den Draft des heutigen Modells (gleiche Architektur,
Speicher reicht) und testet mit der schnellsten. Spekulatives Dekodieren aendert die Antworten
nicht, nur das Tempo.

Mit Draft laeuft die Bild-Probe auf einem Zwilling ohne Draft (llama.cpp: Draft und Bild = HTTP
500). Uebernehmen baut wie der Betrieb seit 24.09.: Hauptmodell mit dem gewaehlten Draft ohne
Projektor, dazu ein Bild-Zwilling; der alte Zwilling fliegt raus.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-24 13:26:08 +02:00
co-authored by Claude Opus 5.5
parent 42363229d4
commit 4a05bc2a05
3 changed files with 308 additions and 43 deletions
+83 -19
View File
@@ -763,20 +763,23 @@ def pruefe(rolle: str, basis: str, modell: str, *, bild: bool = False, frist: fl
else:
werte["code"] = messe_code(basis, modell, frist)
protokoll(f" code : {werte['code']['ok']}/{werte['code']['von']} in {werte['code']['sekunden']} s")
werte["bild"] = None
if bild:
try:
werte["bild"] = messe_bild(basis, bild_modell or modell, rolle, frist)
except Zeitende:
raise
except Exception as e:
werte["bild"] = {"direkt": 0.0, "gefunden": [], "agentisch": False, "fehler": str(e)[:160]}
protokoll(f" bild : {werte['bild'].get('direkt')} direkt, im Ablauf "
f"{'ja' if werte['bild'].get('agentisch') else 'nein'}")
werte["bild"] = bild_probe(basis, bild_modell or modell, rolle, frist, protokoll) if bild else None
werte["dauer_s"] = round(time.time() - t0)
return werte
def bild_probe(basis: str, modell: str, rolle: str, frist: float | None = None, protokoll=log) -> dict:
"""Bild-Probe mit Fehlerfang: ein Absturz der Probe ist ein Messwert, kein Programmfehler."""
try:
ergebnis = messe_bild(basis, modell, rolle, frist)
except Zeitende:
raise
except Exception as e:
ergebnis = {"direkt": 0.0, "gefunden": [], "agentisch": False, "fehler": str(e)[:160]}
protokoll(f" bild : {ergebnis.get('direkt')} direkt, im Ablauf {'ja' if ergebnis.get('agentisch') else 'nein'}")
return ergebnis
# --- Kandidaten-Server -------------------------------------------------------------------
class Server:
@@ -908,30 +911,91 @@ def starte_server(gguf: str, *, mmproj: str | None = None, ctx: int = CTX_STANDA
raise
def pruefe_kandidat(rolle: str, gguf: str, *, mmproj: str | None = None, flags=(), ctx: int = CTX_STANDARD,
basis: dict | None = None, frist: float | None = None, protokoll=log) -> dict:
"""Ganzer Durchlauf eines Kandidaten: Server starten → messen stoppen → urteilen.
basis = Werte der Baseline (ohne sie gibt es kein Urteil, ergebnis None).
Rückgabe: {"werte", "ergebnis", "vergleich", "zusammenfassung"}."""
def mit_server(gguf: str, arbeit, *, mmproj: str | None = None, ctx: int = CTX_STANDARD, flags=(),
frist: float | None = None, protokoll=log):
"""Kandidat starten, arbeit() laufen lassen, stoppen. Gibt (Ergebnis, Server) zurück; ein Absturz
während der Arbeit wird zu ServerFehler — ein toter Server darf nicht wie ein schlechtes Modell aussehen."""
server = starte_server(gguf, mmproj=mmproj, ctx=ctx, flags=flags, frist=frist, protokoll=protokoll)
def absturz() -> str:
return f"Während des Tests abgestürzt (Code {server.prozess.returncode}): {log_ende(SERVER_LOG, 300)}"
try:
werte = pruefe(rolle, KANDIDAT_URL, "kandidat", bild=bool(mmproj), frist=frist, protokoll=protokoll)
ergebnis = arbeit()
except Exception as e:
if not isinstance(e, Zeitende) and server.prozess.poll() is not None:
raise ServerFehler(absturz()) from e
raise
else:
# Einzelproben fangen Fehler ab — ein toter Server darf nicht wie ein schlechtes Modell aussehen.
if server.prozess.poll() is not None:
if server.prozess.poll() is not None: # Einzelproben fangen Fehler ab — darum hier nachsehen
raise ServerFehler(absturz())
finally:
stoppe_server(server)
time.sleep(3) # Speicher freigeben lassen, bevor jemand anderes lädt
werte.update(ladezeit_s=server.ladezeit, ctx=ctx, befehl=" ".join(server.befehl))
return ergebnis, server
_SPEC_FLAGS = ("--spec-type", "--spec-draft-model", "--spec-draft-n-max")
def ohne_draft(flags) -> list[str]:
"""Flags ohne spekulatives Dekodieren (jeweils Schalter plus Wert)."""
rest, flags, i = [], list(flags), 0
while i < len(flags):
if flags[i] in _SPEC_FLAGS:
i += 2
continue
rest.append(flags[i])
i += 1
return rest
def waehle_draft(gguf: str, varianten: list[dict], *, frist: float | None = None, protokoll=log) -> dict:
"""Schnellste Art des spekulativen Dekodierens für einen Kandidaten. Jede Variante {"name", "flags", …}
wird gestartet und bei 13k Tiefe gemessen; startet eine nicht (z. B. passt das Vokabular des Drafts
nicht), fällt sie raus. Spekulatives Dekodieren ändert die Antworten nicht, nur das Tempo — darum
entscheidet allein das Tempo. Rückgabe: die gewählte Variante plus "messungen" (Name → t/s bzw. Grund)."""
messungen: dict[str, object] = {}
beste: tuple[dict, float] | None = None
for v in varianten:
try:
tempo, _ = mit_server(gguf, lambda: messe_tempo(KANDIDAT_URL, "kandidat", frist), ctx=32768,
flags=v["flags"], frist=frist, protokoll=protokoll)
except Zeitende:
raise
except Exception as e:
messungen[v["name"]] = f"geht nicht: {str(e)[:120]}"
protokoll(f" Draft-Variante {v['name']}: geht nicht ({str(e)[:120]})")
continue
tiefe = tempo.get("tiefe") or {}
tps = float(tiefe.get("tg_tps") or 0)
messungen[v["name"]] = tps
protokoll(f" Draft-Variante {v['name']}: {tps} t/s bei 13k (Akzeptanz {tiefe.get('acc')})")
if beste is None or tps > beste[1]:
beste = (v, tps)
gewaehlt = beste[0] if beste else next((v for v in varianten if not v["flags"]), varianten[0])
return {**gewaehlt, "messungen": messungen}
def pruefe_kandidat(rolle: str, gguf: str, *, mmproj: str | None = None, flags=(), ctx: int = CTX_STANDARD,
basis: dict | None = None, frist: float | None = None, protokoll=log) -> dict:
"""Ganzer Durchlauf eines Kandidaten: Server starten → messen → stoppen → urteilen.
basis = Werte der Baseline (ohne sie gibt es kein Urteil, ergebnis None).
Mit Draft wie im Betrieb seit 24.09.: Text auf einem Server ohne Projektor, die Bild-Probe auf einem
Zwilling ohne Draft — llama.cpp kann Draft und Bild nicht zusammen (HTTP 500).
Rückgabe: {"werte", "ergebnis", "vergleich", "zusammenfassung"}."""
mit_draft = "--spec-type" in list(flags)
text_mmproj = None if mit_draft else mmproj
werte, server = mit_server(
gguf, lambda: pruefe(rolle, KANDIDAT_URL, "kandidat", bild=bool(text_mmproj), frist=frist, protokoll=protokoll),
mmproj=text_mmproj, ctx=ctx, flags=flags, frist=frist, protokoll=protokoll)
befehle = [" ".join(server.befehl)]
if mit_draft and mmproj:
werte["bild"], zwilling = mit_server(
gguf, lambda: bild_probe(KANDIDAT_URL, "kandidat", rolle, frist, protokoll),
mmproj=mmproj, ctx=min(ctx, 32768), flags=ohne_draft(flags), frist=frist, protokoll=protokoll)
befehle.append(" ".join(zwilling.befehl))
werte.update(ladezeit_s=server.ladezeit, ctx=ctx, befehl=" | ".join(befehle))
if not basis:
return {"werte": werte, "ergebnis": None, "vergleich": {},
"zusammenfassung": "Gemessen, aber ohne Vergleichswerte des heutigen Modells."}