So haben wir das Modell Qwen 3.6 für unseren Agent Junie optimiert
Vor einiger Zeit haben wir ein langfristiges Projekt gestartet, um auf unterschiedlichen Hardwarekonfigurationen die vollständig lokale Ausführung von Junie – einschließlich lokaler Inferenz – zu ermöglichen. Nach langer Vorarbeit haben wir vor Kurzem eine erste Version von Junie Local veröffentlicht, die auf MacBooks mit M5-Chip Qwen3.6-27B ausführt.
In diesem Blogartikel möchte ich erklären, wie wir das erreicht haben und warum wir uns für Qwen3.6-27B statt Qwen3.8-27B entschieden haben. Wir haben Optimierungen im gesamten Stack vorgenommen – vom Junie-Agent selbst bis hin zur verwendeten Inferenz-Engine.
Beginnen wir mit Junie, denn hier beginnt die Interaktion mit dem lokalen Modell.
Optimierungen an Junie
Erweiterung des rollierenden Agent-Kontexts
Wie jeder andere Coding-Agent besitzt auch Junie eine zentrale Ausführungsschleife, in der die gesamte Arbeit erledigt wird:
- Im ersten Schritt gibt die Benutzerin oder der Benutzer eine Aufgabe vor.
- Junie übermittelt diese Aufgabe an das LLM.
- Das LLM antwortet mit Toolaufrufen (z. B. Bash-Befehle, Anweisungen zum Lesen/Schreiben von Dateien und weitere Aktionen).
- Schließlich sendet Junie das Ergebnis zurück.
Hier ist ein stark vereinfachtes Ablaufdiagramm der Vorgänge, die im Hintergrund ausgeführt werden:

Wie das Diagramm zeigt, erhält das LLM fortlaufend Anfragen zur Kontexterweiterung, wobei Informationen aus bereits verarbeiteten Anfragen teilweise wiederverwendet werden. Genauer ausgedrückt können wir die Prefill-Daten der vorherigen Anfrage für die nächste wiederverwenden, und diese Daten werden als KV-Cache bezeichnet.
Wenn wir Junie jedoch in derselben Sitzung mit einer zweiten Aufgabe betrauen, übernimmt das System nur relevante Teile aus dem Kontext in das Kontextfenster:

Bei Cloud-Modellen ist dies in der Regel kein Problem, da das Modell den Inhalt von Dateien bei erneutem Bedarf einfach neu anfordert und wieder einliest. Zudem ist das Prefill extrem schnell.
Für lokale Modelle gilt das nicht – dort erfolgt das Prefill langsamer und das „Lesen“ von Dateien kostet beträchtliche Zeit.
Um dieses Problem zu lösen, haben wir die Logik für die lokale Inferenz angepasst. Jede neue Anfrage wird direkt dem rollierenden Kontext hinzugefügt:

Auf diese Weise können wir KV-Caches aus der vorherigen Aufgabe wiederverwenden, d. h. wenn das Modell eine Datei bereits gelesen hat, verbleibt sie im Kontextfenster und muss nicht erneut gelesen werden.
Maximale Wiederverwendung des anfänglichen Präfixes
Eine weitere ähnliche Optimierung betrifft den System-Prompt und den initialen Kontext, den Junie zu Beginn einer neuen Coding-Sitzung übermittelt.
Im vorherigen Abschnitt war der Ablauf etwas vereinfacht dargestellt: Bei der ersten LLM-Anfrage werden tatsächlich deutlich mehr Daten an das LLM gesendet als im Diagramm dargestellt:

Wie Sie sehen, werden noch ganz andere Informationen an das LLM übermittelt. All diese Informationen werden zu Beginn jeder neuen Sitzung übertragen und verarbeitet. Natürlich wäre es super, wenn wir auch diese Daten zwischenspeichern könnten 🙂
Daher haben wir die Reihenfolge geändert, in der wir diese Daten übertragen:

Außerdem haben wir die Inferenz-Engine mit einer speziellen Logik ausgestattet, um den Präfix bis hin zur Benutzeranfrage im Cache zu halten, sodass er bei nachfolgenden Aufgaben (im selben Projekt) einfach wiederverwendet wird. Den Projektkontext nach der Benutzeranfrage haben wir ausgespart, da er recht klein ist, überwiegend aus Dateien der obersten Projektebene besteht und sich daher häufig ändern kann.
Umsetzung von Fortschrittsmeldungen
Bei leistungsfähigeren Cloud-Modellen fordert Junie das LLM auf, Fortschrittsmeldungen für Benutzer*innen in einem XML-ähnlichen Block auszugeben. Leider werden solche Aufforderungen von Qwen 3.6 größtenteils ignoriert. Gleichzeitig beschreibt das Modell seine Aktionen als Klartext innerhalb der LLM-Antwort – d. h. das LLM sendet Toolaufrufe zusammen mit einem erklärenden Begleittext.
Die Lösung ist simpel: Wir nutzen einfach diesen von Qwen 3.6 erzeugten Text als Fortschrittsinformationen für Benutzer*innen. Solche Anpassungen sind modellspezifisch. In unserem Fall hatten wir mit Qwen 3.6 Glück – manche Modelle geben an dieser Stelle überhaupt keinen Text aus, während andere viel zu ausführlich sind.
Vermeidung überflüssiger LLM-Aufrufe
Die nächste Optimierung – das Deaktivieren aller optionalen LLM-Anfragen – mag trivial erscheinen, hat den Agent aber deutlich effizienter gemacht. Konkret bedeutete dies, dass wir jegliche Logik zur Erstellung einer kurzen Aufgabenzusammenfassung deaktiviert haben. Damit ging zwar ein kleiner Teil der Nutzungserfahrung verloren, doch dies fällt nach unserer Einschätzung kaum ins Gewicht. Außerdem schalteten wir den Multi-Agent-Modus vollständig ab, da die sequenzielle Verarbeitung von LLM-Anfragen auf einem M5 am effektivsten ist; mehrere Agents zu nutzen ist zwecklos, da die Inferenz ohnehin den Flaschenhals bildet.
Optimierung der Modellparameter
reasoning_effort: none
Bei internen Tests der Cloud-Version von Qwen3.6-27B stellten wir fest, dass die Aktivierung von Reasoning keinen nennenswerten Qualitätsgewinn brachte. Aus diesem Grund haben wir das Reasoning in der lokalen Version komplett abgeschaltet. Das macht einen großen Unterschied, denn aus Sicht der Inferenz-Engine gibt es keinen Unterschied zwischen den Reasoning-Token und den Token der eigentlichen Antwort. Ohne Reasoning fallen daher 2- bis 3-mal weniger Token an, was die Aufgabenausführung bei vernachlässigbarem Qualitätsverlust etwa doppelt so schnell macht.
Quantisierung
Wir haben uns für die 4-Bit-Version entschieden, da sie in Benchmarks nur minimal hinter der 8-Bit-Variante zurückbleibt – und da die Generierung speicherlimitiert ist, läuft die 4-Bit-Version rund doppelt so schnell wie die 8-Bit-Version. Als wir jedoch die Prefill-Geschwindigkeit der 8-Bit- und 4-Bit-Versionen verglichen, stellten wir fest, dass beide gleich schnell waren. Das kam uns merkwürdig vor, also sind wir der Sache auf den Grund gegangen.
Optimierung der Inferenz-Engine
Der Prefill-Hack
Vielleicht wundern Sie sich, warum wir dem Prefill überhaupt so viel Beachtung schenken. Im Netz dreht sich schließlich fast alles um Benchmarks der Generierungsgeschwindigkeit und Möglichkeiten zu deren Optimierung.
Bei einer dedizierten GPU wie der RTX 5090 mag dieser Einwand tatsächlich berechtigt sein. Auf solcher Hardware läuft das Prefill in der Tat extrem schnell, da es rechenlimitiert ist und dedizierte GPUs meist über relativ viel Rechenleistung verfügen. Damit lassen sich bei Standardkonfigurationen Prefill-Geschwindigkeiten von rund 3.700 Token/s erzielen. Beim M5 lag dieser Wert anfangs bei etwa 650 Token/s. Fragte das Modell bei Analyseaufgaben Dateiinhalte ab, entfiel somit der Großteil der Zeit auf das Prefill und nicht auf die Generierung!
Erschwerend kam hinzu, dass es bei der Prefill-Geschwindigkeit keinerlei Unterschied zwischen 4-Bit-, 8-Bit- und 16-Bit-Quantisierung gab. Aber woran lag das? Nun: Da das Prefill rechenlimitiert ist, stellt die Speichergeschwindigkeit überhaupt keinen Engpass dar. Und wie sich herausstellte, wurde der Großteil der Matrixoperationen beim Prefill mit voller 16-Bit-Präzision ausgeführt. Alle 4-Bit-Gewichte wurden also vor der Berechnung in 16-Bit-Werte konvertiert. Der M5-Prozessor verfügt jedoch über spezielle Befehle für 8-Bit-Zahlen, die wesentlich schneller als 16-Bit-Operationen ablaufen. Nachdem wir mit einem Patch im MLX-VLM-Paket einige* Matrixberechnungen beim Prefill auf 8-Bit umgestellt hatten, stieg die Prefill-Geschwindigkeit um rund 40%!
Dies ist übrigens der Hauptgrund, warum wir uns auf M5-Chips konzentriert haben. Auf M4-Chips fehlen diese 8-Bit-Rechenbefehle, weshalb das Prefill mit 16-Bit-Arithmetik um 20 bis 30% langsamer erfolgt.
*Qwen3.6-27B nutzt sowohl Full-Attention- als auch Self-Attention-Schichten. Wir stellten fest, dass die Full-Attention-Gewichte selbst bei einer 4-Bit-Quantisierung in voller 16-Bit-Präzision vorlagen. Da diese Schichten ohnehin mit voller Präzision arbeiten, haben wir die Optimierung dort nicht angewendet – sie greift nur bei den Self-Attention-Schichten, wo sie den Speicher- und Rechenaufwand tatsächlich reduziert. Hier ist der Link zum MLX-VLM-Patch. Dieselbe Optimierung lässt sich sogar in vLLM vornehmen, indem einfach die Konfigurationsdatei des Modells angepasst wird. Konfigurationsbeispiel
Spekulative Dekodierung mittels MTP und N-Gram-Abgleich
Wir haben folgende Standardoptimierungen angewendet:
- MTP (Multi-Token-Vorhersage) mit separatem Entwurfsmodell: Ein Verfahren zur spekulativen Dekodierung, bei dem ein kleineres Entwurfsmodell die nächsten Token vorschlägt, die vom Hauptmodell nur noch validiert werden.
- Spekulative N-Gram-Dekodierung: Anstelle eines Entwurfsmodells sucht diese Methode nach bereits wiederholten Token-Sequenzen im Kontext und „prognostiziert“ nachfolgende Token durch einen Abgleich mit diesen Mustern.
Wir haben beide Methoden parallel aktiviert. In der Praxis bedeutet dies, dass während der Generierung gelegentlich nicht nur die rund 3 über MTP vorgeschlagenen Token übernommen werden, sondern zusätzlich bis zu 8 weitere Token aus der N-Gram-Methode. Die nachstehende Abbildung zeigt die generierten Raw-Token, farblich gekennzeichnet nach der Herkunft des jeweiligen Tokens (Entwurfsmodell bzw. N-Gram).

Durch diese Kombination kann die Generierung bis zu doppelt so schnell erfolgen.
Qwen3.8-27b
Weshalb fiel unsere Wahl trotz alledem nicht auf 3.8 anstelle von 3.6?
Leider liefert Qwen 3.8 nur bei aktiviertem Reasoning gute Ergebnisse. Ohne Reasoning sinkt die Ausgabequalität erheblich – bei typischen Aufgaben kann das Modell sogar komplett versagen und in einer Endlosschleife denselben Toolaufruf wiederholen. Die Aktivierung des Reasoning erhöht jedoch die Anzahl der generierten Token erheblich: Bei „Medium“-Reasoning werden etwa 5-mal mehr Token produziert. Da die Prefill-Zeit annähernd gleich bleibt, wird der gesamte Vorgang eher um den Faktor 4 als um den vollen Faktor 5 langsamer. Dieser Performanceverlust ist immer noch gravierend, sodass Qwen3.6-27B auf Mac-Hardware vorerst die bessere Wahl bleibt.
Fazit
Dieser Erfahrungsbericht zeigt hoffentlich, dass es bei typischen agentischen Programmieraufgaben nicht ausreicht, bloß auf die Geschwindigkeit der Tokengenerierung zu achten.
Vielmehr müssen alle Ebenen des Stacks gleichermaßen optimiert werden:
- Generierung und Prefill: Der schrittweise Token-Dekodierungsprozess und der erste Kontextverarbeitungslauf.
- Modellparameter und Quantisierung: Die Präzision der Gewichte sowie die Konfiguration des Modells.
- Agent-Harness: Die übergeordnete Orchestrierungsebene (Toolaufrufe, Ablaufsteuerung, Prompt-Logik), die das Modell steuert.
In diese Richtung möchten wir in Zukunft weiterarbeiten. Die M5-Unterstützung war erst der Anfang: Wir haben bereits Prototypen für DGX Spark und RTX 5090 (und evaluieren sogar 24-GB-Grafikkarten). Bleiben Sie gespannt!
Autor des ursprünglichen Blogposts