arenskrieger.dev
Projekte
ENDE← Zurück zum Profil
Projekt // CTC AI Operations

Contamination-resistente Code-Evaluierung

Eine reproduzierbare Pipeline, die Evaluierungsaufgaben aus einer echten, versionierten Codebase per AST-Analyse synthetisiert – damit sie nicht wie bei öffentlichen Benchmarks bereits im Trainingsdatensatz gelandet sein können. Pilot mit Qwen3.6-35B-A3B auf dem Cerberus-Validierungsframework, unter strikter Single-Stream-Inferenz auf einer einzelnen 32-GB-GPU.

Pilot · ein Modell · ein Repository · methoden­orientiert
Kontamination
Tasks aus Live-AST
Aus einem gepinnten Commit eines echten Repos generiert, nicht aus einer statischen öffentlichen Liste.
Determinismus
Ein gepinnter VRAM-Stream
Single-Model-, Single-Stream-Inferenz hält Latenzdaten frei von CPU-Fallback-Rauschen.
Durchsatz
~200 Tokens/s auf 32 GB
Ein MoE mit ~3B aktiv erledigt 60 Tasks in 4 min 57 s auf einer RTX 5090.
Whitepaper (PDF · EN)
§01 Kontext

Das gelöste Problem

Die Validität von Benchmarks zerfällt, sobald Testfälle in die Trainingskorpora sickern. Öffentliche und synthetische Suiten sind zunehmend durch Data Contamination kompromittiert: Die Testfälle liegen unbereinigt in den Trainingsdaten, sodass ein hoher Leaderboard-Score Memorisierung statt der konkret benötigten Fähigkeit widerspiegeln kann. Daraus folgen zwei Eigenschaften, die diese Pipeline wiederherstellt – Aufgaben, die nachweislich außerhalb jedes Trainingssatzes liegen, und eine Messung, die sich Monate später erneut ableiten und verteidigen lässt.

Pilotziel ist Cerberus (pyeve/cerberus, ~26 Quelldateien), eine Python-Bibliothek, die Daten gegen deklarative Schemata validiert: Typregeln, Wert-Coercion und verschachtelte oder bedingte Validierung. Dieses Design erzeugt eine dichte Edge-Case-Fläche – None versus fehlender Schlüssel, Leerstring-Coercion, rekursiv referenzierte Sub-Schemata – und genau das macht es zu einem sinnvollen Audit-Ziel. Ein Modell, das einen plausibel aussehenden Validator erfindet, wird durch genau die Fälle gefangen, für deren Behandlung die Bibliothek existiert, nicht durch eine synthetische Falle.

Ein Benchmark, den man aus echtem Code neu erzeugen kann, lässt sich von Kontamination nicht still verrotten.
§02 Methode

Strukturelle Task-Synthese aus einem Live-Repository

Aufgaben sind eine reine Funktion aus Repository-Zustand und einem festen Satz Extraktionsregeln – ein erneuter Lauf gegen denselben Commit reproduziert dieselbe Evaluierung. Die Engine scannt das Repo, filtert Build-Artefakte und __pycache__, parst den AST jeder verbleibenden Datei und extrahiert einen begrenzten Metadatensatz – die Top-Funktions- und Klassensignaturen, die Import-Abhängigkeiten des Datei-Kopfes und den Rumpf der ersten funktional signifikanten Methode. Diese Metadaten treiben eine Synthese, die bis zu einer Aufgabe pro Kategorie pro Datei erzeugt.

REPOVersionierter Scan ASTParse + Filter METADATENSignaturen · Imports SYNTHESE3 Task-Kategorien JUDGEpromptfoo
Tasks = f(Repo-Zustand, Extraktionsregeln). Commit neu, Evaluierung neu.
Kategorie 01 · 20 Tasks

code_generation

Funktion aus Anforderung plus AST-extrahiertem Import-Kontext und Sicherheitsvorgaben schreiben – gemessen gegen die Signaturen, die das Repo tatsächlich definiert.

Kategorie 02 · 20 Tasks

code_understanding

Absichtlich injizierten Bug in isoliertem Validierungs-Snippet erklären und lokalisieren – prüft Verhaltens- statt Oberflächen-Verständnis.

Kategorie 03 · 20 Tasks

security_audit

Injection-Vektoren – SQL, Command, Path Traversal – und ungefilterte eval/exec in echtem Validierungscode identifizieren; die hier teuerste Fehlerklasse.

Orchestrierung

YAML-Isolation → SQLite

Mehrere Targets in einer promptfoo-Config lassen async Timeouts beim Modell-Boot blockierte Provider verwerfen und nur den schnellsten loggen. Eine YAML pro Modell erzwingt sequentielle Läufe auf OS-Prozessebene und einen deterministischen Schreibvorgang.

§03 Architektur

Ein MoE mit ~3B aktiv im 32-GB-Budget

Qwen3.6-35B-A3B ist ein sparse Mixture-of-Experts-Modell. Laut publizierter Architektur laufen 40 Layer als zehn Wiederholungen von drei Gated-DeltaNet-Blöcken gefolgt von einem Gated-Attention-Block, und jeder Block speist ein 256-Experten-MoE, dessen Router pro Token 8 Experten plus 1 Shared-Expert aktiviert. Etwa 3 Milliarden der 35 Milliarden Parameter rechnen an einem gegebenen Token – aber alle 256 Experten müssen resident bleiben, der Speicher-Footprint ist also der des vollen Modells, während Compute und Bandbreite dem aktiven Pfad folgen.

MIXTURE-OF-EXPERTS · ROUTING PRO TOKEN 9 aktiv / Token 256 Experten – alle resident im VRAM (~24 GB) routed ×8 shared ×1 inaktiv · resident ×247 COMPUTE / TOKEN ≈ 3B von 35B aktiv ~9% VRAM RESIDENT 35B von 35B · ~24 GB 100% · alle Experten geladen Compute & Bandbreite schrumpfen – der Speicher bleibt voll.
Der Router aktiviert pro Token 9 von 256 Experten, es rechnen also nur ~3B von 35B Parametern – aber alle 35B bleiben geladen. Compute und Bandbreite schrumpfen; der VRAM-Footprint nicht. Diese Asymmetrie ist der Grund, warum MoE auf eine feste Karte passt.

Warum die 32 GB halten

Ein 35B-Modell in FP16 braucht ~70 GB allein für die Gewichte. Q4_K_M packt sie in Super-Blöcke von 256 Gewichten (acht 32er-Sub-Blöcke) mit 4-Bit-Quants, 6-Bit-Skalen und -Mins pro Sub-Block – etwa 4,5 effektive Bit pro Gewicht – und hebt ausgewählte Tensoren auf Q6_K. Diese gemischte Präzision bringt den Footprint auf ~24 GB und bewahrt genug der Gewichtsverteilung, damit syntaktische Validität im Maßstab hält.

Das verbleibende Budget nimmt den KV-Cache auf. Weil nur die zehn Gated-Attention-Layer einen Cache aufbauen – die dreißig DeltaNet-Layer tragen einen fixen rekurrenten Zustand – und diese Layer Grouped-Query-Attention mit zwei KV-Heads bei Head-Dim 256 nutzen, kostet der Cache in der Größenordnung von ~10 KB pro Token bei q8_0. Selbst lange Kontexte bleiben innerhalb der ~5 GB nach den Gewichten, ohne auszulagern.

VRAM-ZUTEILUNG · 32 GB GDDR7 24 GB · Q4_K_M-Gewichte (35B resident) OS 2 KV+Akt ~5 ~1 0 32
Die Gewichte dominieren die Karte; die ~5 GB Rest sind bewusst reserviert, damit der KV-Cache wachsen kann, bevor er je verdrängt.

Single-Stream-Determinismus – und die vermiedene Klippe

Das Backend ist mit OLLAMA_NUM_PARALLEL=1 und OLLAMA_MAX_LOADED_MODELS=1 gepinnt. Parallelität ist verlockend, aber jeder parallele Request vervielfacht den KV-Cache linear, und der erste, der den VRAM überläuft, zwingt Gewichte in den System-RAM. Decoding ist bandbreitenlimitiert – jeder Token liest die aktiven Gewichte einmal über den Bus – daher ist der Wechsel von GDDR7 mit ~1,79 TB/s auf DDR5 mit ~60–80 GB/s keine Verlangsamung, sondern ein Kollaps.

150 0 Tokens / s (maßstäblich) >150 t/s Im VRAM · GDDR7 ~1,79 TB/s <2 t/s DDR5-Auslagerung · ~60–80 GB/s ≈ 98% Kollaps
Der rechte Balken ist maßstabsgetreu – seine Unsichtbarkeit ist der Punkt. Deshalb verbietet die Pipeline die Parallelität, die die Auslagerung auslösen würde.
RTX 5090 · 32 GB GDDR7 · ~1,79 TB/sRyzen 7 9850X3DOllama · Q4_K_MFLASH_ATTENTION=1KV_CACHE_TYPE=q8_0Temperatur 0.0
§04 Ergebnisse

Was der Lauf tatsächlich zeigt

Tasks60 / 60 abgeschlossen
Dauer4 min 57 s
Ø Latenz~5,0 s / Task
Durchsatz~200 Tokens/s
Tokens74.342 – 12.682 Prompt / 59.392 Completion
Stabilitätkeine Latenzspitzen, kein OOM unter NUM_PARALLEL=1

Zur Substanz der Ausgabe: Der generierte Code war syntaktisch valide mit konsistenten Type-Hints (Optional[str]), bevorzugte die Standardbibliothek (re) gegenüber fragilen Drittanbieter-Parsern und behandelte None, Leerstring und TypeError über den Generierungssatz hinweg konsistent. Das Completion-zu-Prompt-Verhältnis (~4,7:1) spiegelt Aufgaben wider, die ganze Funktionen und Audit-Berichte verlangen, keine Einzeiler.

100% 0 100% Beobachtet – gesättigt Trivial Standard Schwer Adversarial ZIEL – STRATIFIZIERTE SCHWIERIGKEIT
Links: das aktuelle Task-Set, gesättigt. Rechts: die Spreizung, die ein diskriminierendes Set zeigen sollte. Die Lücke dazwischen ist die nächste Iteration.
Die 100 % richtig lesen. Nach dem Discrimination-Kriterium, das dieses Lab an jedes Rubric anlegt – ein Task-Set, bei dem jedes Sample besteht, misst nichts – ist ein 60/60 keine Fähigkeitsaussage; es sagt, dass die generierten Aufgaben unter der Modellgrenze lagen. Das ist der ehrliche Kernbefund des Pilots. Belastbar ist hier das operative Ergebnis – Durchsatz, Stabilität und Token-Buchhaltung unter gepinntem Single-Stream – und genau diese operative Hülle, nicht der Score, sollte die Pipeline zuerst etablieren.
§05 Szenario

In der Praxis: Prüfung eines Open-Weight-Modells für eine Air-Gapped-Validierungsbibliothek

SSituation

Ein Team betreut eine sicherheitskritische Python-Validierungsbibliothek – die Cerberus-Problemklasse: Schema-Coercion, verschachtelte und bedingte Regeln. Es will ein Open-Weight-Coding-Modell einsetzen, aber die Codebase ist proprietär und unterliegt einer Datensouveränitäts-Auflage: Nichts darf das Netzwerk verlassen. Öffentliche Coding-Benchmarks sind kontaminiert, ein Leaderboard-Rang ist also kein Beleg für diese Codebase.

TTask

Eine belastbare, lokale Messung erzeugen, ob das Kandidatenmodell die Edge-Cases und die Injection-Vektor-Logik dieses Repos tatsächlich beherrscht – reproduzierbar aus dem exakten Commit und passend in eine einzelne 32-GB-GPU, ohne Cloud-Abhängigkeit im Evaluierungspfad.

AAction

Den AST-Extraktor auf den gepinnten Commit richten und 60 Aufgaben synthetisieren, gebunden an die echten Signaturen und Imports des Repos. Qwen3.6-35B-A3B bei Temperatur 0 unter NUM_PARALLEL=1 laufen lassen; den Lauf air-gapped halten (kein Netzwerk im Eval-Pfad); Latenz, Token-Zahlen und Finish-Reasons pro Task via einer isolierten promptfoo-Config nach SQLite loggen.

RResult

Eine vollständige 60-Task-Evaluierung in 4 min 57 s, vollständig lokal und aus (Commit, Extraktionsregeln) reproduzierbar – eine Messung, die das Team Monate später in einem Audit verteidigen kann. Die einheitliche Pass-Rate legte eine konkrete methodische Lücke offen: Das Task-Set diskriminiert auf diesem Modellniveau nicht. Das übersetzte sich direkt in die nächste Handlung – Schwierigkeit stratifizieren und adversariale Audit-Fälle ergänzen, bevor ein Score als Fähigkeitssignal gelesen wird. Das Ergebnis ist eine belastbare, souveräne Messung und eine präzise Anweisung für die nächste Iteration statt einer auf Treu und Glauben genommenen Zahl.

§06 Grenzen & nächste Schritte

Was dieser Pilot nicht ist – und wohin er geht

  • Einzelnes Repository. Alle Tasks stammen aus Cerberus; externe Validität braucht diverse Ziele. Der Extraktor generalisiert bereits – ihn via GitHub-API auf FastAPI (async Routing), Pydantic (Validierungs-Metaklassen) oder Django REST zu richten, ist der unmittelbare Cross-Domain-Schritt.
  • Ein Modell, bewusst. Ein Cross-Model-Vergleich (DeepSeek-R1 32B, Gemma 4-31B, Mistral NeMo 12B) folgt erst, wenn jedes Modell unter identischen, gemessenen Bedingungen läuft – keine simulierten Zeilen, die einen nicht stattgefundenen Lauf ersetzen.
  • Nicht-diskriminierendes Task-Set. Die Schwierigkeit muss stratifiziert werden (trivial → adversarial), damit die Pass-Rate spreizt (§04). Das ist die Voraussetzung für jede Fähigkeitsaussage.
  • Noch keine kommerzielle Baseline. Ein Frontier-API-Goldstandard via Batch-API würde die lokalen Scores an einem bekannten Referenzpunkt verankern.
  • Halluzinations-Detektion. Ein Abgleich generierter Imports und API-Aufrufe gegen echte Paket-Dokumentation würde erfundene Abhängigkeiten fangen, bevor sie als valider Code durchgehen – die wertvollste nächste Automatisierung.

Der Beitrag ist die Methode und ihre Disziplin: Tasks aus Live-Code gegen Kontamination, ein VRAM-Regime, das die Zahlen ehrlich hält, und Ergebnisse, berichtet nach dem Maßstab, dass ein gesättigter Score ein Befund ist, kein Sieg.

End-to-end aufgebaut und betrieben unter CTC AI Operations – von GPU-Konfiguration und Backend-Tuning über das Task-Synthese-Design bis zu Evaluierungs-Orchestrierung und Ergebnis-Aggregation.