Was nach dem Umbau noch Tokens sparte – und welches Experiment ich abgebrochen habe
Domänenwissen nur bei Bedarf laden klang nach dem nächsten großen Hebel. Ein Experiment mit vorher festgelegter Abbruchgrenze sagte Nein. Was stattdessen half und was die Startlast in zwei echten Projekten heute zeigt.
01.11.2026
•11 Min. LesezeitHinweis zum Inhalt
Diese Beitragsreihe beschreibt meine Erkenntnisse beim Einsatz von KI im Softwareentwicklungsprozess. Das sind persönliche Erfahrungen und Erkenntnisse, von denen ich Maßnahmen abgeleitet habe, die für meine Projekte funktionierten. Diese Serie hat nicht den Anspruch, eine umfassende Anleitung oder allgemeingültig zu sein, sondern eine Inspiration für eure eigenen Projekte.
Der Implementierungs-Agent in meinem Flutter-Projekt, der Developer aus Teil 1, lud nach dem Umbau 3.034 Zeilen Adapter vor, bevor er die erste Datei öffnete: Coding-Guidelines, Test-Strategie und Gate-Regeln. Mein Wunsch lag nahe. Die Guidelines sollten nach Domänen aufgeteilt werden, also Backend-SDK, State-Management, Styling und Allgemeines, und der Agent sollte nur laden, was ein Schritt braucht.
Rückblick auf Teil 1 und Teil 2
In Teil 1 habe ich zwei Trennlinien beschrieben: welches Wissen in Kern, Adapter oder Inhalte gehört, und auf welchem Weg es zu welchem Agenten kommt. In Teil 2 hat ein Audit gezeigt, dass mein eigenes Setup alle zwölf Rules in jeden Agenten lud. Elf davon sind seit Version 2.0 Skills, die nur die Rollen vorladen, die sie brauchen, und die Grundlast der Hauptsession sank im Template um 84 %. Im ersten echten Projekt startete der Implementierungs-Agent nur wenig leichter, weil seine Adapter groß sind.
Das Experiment, das „Nein" gesagt hat
Eine frühere Erfahrung machte mich vorsichtig. Eine Text-Regel, zuerst den Code-Index zu nutzen (bei mir CodeGraph), also den abfragbaren Symbol- und Aufrufgraphen des Projekts, hatte im ersten Lauf zu null Index-Abfragen geführt. „Vor der ersten Änderung den Skill laden“ wäre wieder so eine Text-Regel. Deshalb habe ich vor dem Ausrollen gemessen. Eine Abbruchgrenze, die vorher feststeht, macht aus einem Wunsch ein Experiment.
Das Vorhaben hatte drei Stufen: zuerst die Messung reparieren und einen Messplan einfrieren, dann verlustfrei kürzen, und erst danach bei Bedarf laden, aber nur, wenn ein Experiment die Go-Kriterien erfüllt.
Für das Experiment brauchte ich ein Werkzeug, das Transkripte liest. Ein kleines Python-Skript liefert je Subagent-Lauf den Agenten, die Turns, die Token-Summe und die Reihenfolge, in der Skills geladen wurden. Je Domäne zeigt es, ob der passende Skill vor der ersten Änderung in dieser Domäne geladen wurde. Diesen Anteil nenne ich die Lade-Quote.
Was ein Lauf kostet, bevor er startet
Bevor ich das Experiment plante, habe ich 184 alte Subagent-Läufe aus dem Flutter-Projekt ausgewertet, darunter 21 des Implementierungs-Agenten und 30 des Code-Reviewers. Ein Lauf im Experiment besteht aus Implementierung und Code-Review. Das kostete in den alten Läufen im Median etwa 182 Millionen Tokens: 171 Millionen für die Implementierung, die im Median 319 Turns brauchte, und 11 Millionen für den Review. 97 bis 99 % davon waren Cache-Reads, also Kontext, der aus dem Prompt-Cache kommt und deutlich günstiger abgerechnet wird als neu gelesene Tokens.
Die Startlast steckt dabei in jedem Turn wieder im Kontext. Multipliziert man den Startkontext mit der Zahl der Turns, macht er beim Median etwa 36 % der Tokens eines Implementierungs-Laufs aus und etwa 60 % eines Review-Laufs. Jeder Token weniger Startlast wirkt also in jedem Turn.
Das volle Design hätte drei Aufgaben in zwei Varianten mit je drei Wiederholungen gebraucht, also 18 Läufe und hochgerechnet etwa 3,3 Milliarden Tokens. Die Streuung der alten Läufe lag über 30 %. Einen Unterschied von 10 % hätten drei Wiederholungen ohnehin nicht sichtbar gemacht. Ich habe mich für einen Pilot mit sechs Läufen entschieden und wollte danach neu entscheiden.
Der Aufbau
Ein Subagent suchte im Flutter-Projekt drei Vorhaben, die schon umgesetzt waren und einen freigegebenen Plan und einen Start-Commit hatten: eine Datenlogik (drei Quell-, vier Testdateien), eine UI-Komponente (drei Quelldateien plus Katalog, sechs Testdateien) und eine Änderung am Bestand (zwei Quell-, fünf Testdateien).
Dabei fiel ein Störfaktor auf. Die heutigen Guidelines enthielten die
Musterlösungen schon, weil sie nachträglich zurückgeschrieben worden waren. Beide
Varianten bekamen deshalb Adapter auf dem Stand des Start-Commits. Variante A
lädt sie wie bisher vor. Variante B hat denselben Inhalt, aufgeteilt in
Domänen-Skills, die der Agent selbst laden soll. Dass B inhaltsgleich ist, habe
ich per diff der sortierten Zeilen geprüft.
Die Go-Regel stand vorher fest, und alle drei Bedingungen mussten gelten:
- Lade-Quote mindestens 90 %.
- Qualität nicht schlechter: höchstens ein zusätzlicher Major-Befund oder eine zusätzliche Fix-Runde insgesamt.
- Tokens im Mittel höchstens 90 % von Variante A.
Abgebrochen wird nach dem Pilot, wenn die Lade-Quote unter zwei Dritteln liegt oder Variante B in zwei von drei Aufgaben nicht billiger ist.
Die Läufe liefen in Wegwerf-Klonen, nicht in Worktrees. Ein Worktree teilt
Git-Config, Stash-Stapel und origin mit dem echten Repository, und der
Implementierungs-Agent pusht selbst. Je Lauf sah das so aus:
git clone <zielprojekt> runs/R1a-A && cd runs/R1a-A
git remote remove origin # Push schlägt laut fehl, keine CI startet
# .claude/ und scripts/ aus dem Template-Branch kopieren, Adapter-Set einsetzen
git config core.hooksPath scripts/git-hooks
claude # einmal interaktiv: Vertrauensdialog bestätigen
claude -p "$(cat prompt.txt)" --output-format json > run.json
Den Vertrauensdialog braucht jeder neue Ordner einmal. Ohne ihn ignoriert Claude
Code die permissions.allow-Einträge der Projekt-Settings und meldet das auch.
Bei einem Lauf schlief der Rechner ein. Die Subagents liefen zu Ende, aber die
steuernde Session blieb unvollständig. Für lange kopflose Läufe unter macOS
gehört deshalb caffeinate -i davor.
Das Ergebnis
Die Lade-Quote lag bei 0 von 4. Der Nenner sind die Paare aus Lauf und berührter Domäne in Variante B: drei Läufe, von denen einer in zwei Domänen änderte. In keinem Fall hat der Agent den Domänen-Skill vor der ersten Änderung geladen. Geöffnet hat er die Domänen-Skills nur, um am Ende eine Regel hineinzuschreiben.
Variante B startete 11.000 bis 15.000 Tokens kleiner, brauchte aber mehr Turns. Die Gesamttokens lagen um 7, 9 und 22 % über Variante A, bei einem Lauf je Aufgabe. Die Qualität war in beiden Varianten gleich: kein Major-Befund, keine Fix-Runde.
Die Hochrechnung von 182 Millionen Tokens je Lauf traf nicht zu. Die echten Läufe kosteten 2,6 bis 14,5 Millionen Tokens, alle sechs zusammen etwa 35 $. Die Referenzaufgaben waren klein, die alten Läufe waren echte Features.
Die Abbruchregel war erfüllt, also No-Go. Alles bleibt vorgeladen. Warum ich Pflichten lieber einem Mechanismus als einem Satz im Prompt anvertraue, steht im Beitrag über Verbote als Mechanismus.
Nachmachen
- Die Kostenbasis aus alten Transkripten ziehen, bevor du ein Experiment planst.
- Go- und Abbruchregel aufschreiben, bevor der erste Lauf startet.
- In Wegwerf-Klonen ohne Remote laufen lassen und den Vertrauensdialog einmal bestätigen.
- Die Lade-Quote messen, nicht nur Tokens: Wurde das Wissen vor der Änderung gelesen?
Was stattdessen half
Die zweite Stufe des Vorhabens war verlustfreies Kürzen. Sie lief ohnehin, und sie hat gewirkt:
- Die Anleitung für das Agent-Memory schrumpfte von 208 auf 60 Zeilen. Claude Code
spielt sie bei gesetztem
memory:selbst ein. Die Subagent-Doku sagt dazu: „The subagent's system prompt includes instructions for reading and writing to the memory directory.“ - Eine Kopie der PR-Anleitung im Git-Skill und eine doppelte Tabelle der Status-Übergänge fielen weg.
- Die Zeilen „Zuerst: Skill X aufrufen“ entfallen, wenn der Skill ohnehin vorgeladen ist. Das sind die rechnerischen Dubletten aus Teil 2.
Im Template sank der Durchlauf von Version 2.0 zu 2.1 damit von 165.777 auf 121.517 Tokens.
Dazu kamen Größenwarnungen im Prüfskript für den Adapter, das ich im Beitrag zum
Adapter-Vertrag vorstelle. Es warnt, wenn
project-context.md mehr als 200 Zeilen hat oder ein vorgeladener Adapter-Skill
mehr als 500. Die beiden Grenzen entsprechen den Empfehlungen der Doku: unter 200
Zeilen je CLAUDE.md (Memory) und unter
500 Zeilen für den Hauptteil eines Skills
(Skill-Best-Practices).
Es bleibt eine Warnung ohne Exit-Code.
Ein Verhaltenscheck mit zwei weiteren Läufen für zusammen 4,30 $ zeigte, dass die Kürzung nichts gebrochen hat. Gates, Status-Wechsel, Review und Push liefen durch, und kein Agent hat einen vorgeladenen Skill ein zweites Mal geladen. Er zeigte aber auch, dass der Implementierungs-Agent zwei Prozedur-Skills nie selbst aufruft: den für die PR-Übergabe und den für Status-Wechsel an den Dokumenten. Seit Version 2.2 lädt er beide vor, was im Template 7.880 Tokens je Durchlauf kostet, 6,5 % mehr. Ich habe dafür bewusst Tokens gegen Verlässlichkeit getauscht.
Ein Skill kann umgekehrt auch gar nichts kosten: Mein Skill für das
Best-Practice-Audit trägt disable-model-invocation: true, und laut
Skill-Doku liegt dann nicht einmal seine
Beschreibung im Kontext, er lädt erst, wenn ich ihn aufrufe.
Nachmachen
- Doppeltes streichen, das Claude Code ohnehin einspielt (Memory-Anleitung) oder ein anderer Skill schon enthält.
- Größenwarnungen für Rules und vorgeladene Skills ins Prüfskript nehmen, an der Doku ausgerichtet.
- Skills, die ein Agent nachweislich nie selbst aufruft, vorladen statt auf den Aufruf zu hoffen.
Die Zahlen
Der Sprung kam mit 2.0. Danach entscheidet die Größe der Adapter. Die Tabellen zeigen, woran ich das festmache.
Im Template
| Template, ≈ Tokens | 1.5.2 | 2.0.0 | 2.3.0 |
|---|---|---|---|
| Grundlast Hauptsession | 23.162 | 3.821 | 3.961 |
| Startlast je Pipeline-Durchlauf | 311.804 | 165.777 | 129.542 |
| davon Dubletten | 80.527 | 19.809 | 0 |
Alle fünf Release-Stände habe ich mit derselben Fassung von measure-context.py
gemessen. Das Skript zählt bei leeren Adaptern nur, was der Workflow selbst an
Konfiguration mitbringt, schätzt Tokens als Zeichen ÷ 4 und nimmt für einen
Durchlauf feste Aufrufzahlen an. Die Zwischenstände stehen oben im Text: 2.1
senkte die Startlast je Durchlauf auf 121.517 Tokens, 2.2 hob sie mit den beiden
vorgeladenen Prozedur-Skills auf 129.397. Von 1.5.2 bis 2.3.0 sank die Grundlast
um 83 % (nach 2.0 kamen 140 Tokens dazu) und die Startlast je Durchlauf um 58 %.
In zwei echten Projekten
Im Flutter-Projekt (Zielprojekt A) und in einem Nuxt-Projekt (Zielprojekt B) habe ich je ein Feature auf Version 2.3.0 gebaut und die Startlast mit den alten Läufen verglichen. Gemessen ist der Kontext im ersten Turn: die Summe aus Input, Cache-Aufbau und Cache-Reads der ersten Antwort des Modells im Transkript, also der ganze Startkontext laut API. Die Werte sind Mediane, n ist die Zahl der Läufe.
| Startkontext, Tokens (n) | A 1.x | A 2.3.0 | B 1.x | B 2.3.0 |
|---|---|---|---|---|
| Hauptsession | 201k (14) | 64k (2) | 139k (16) | 77k (1) |
| use-case-reviewer | 192k (22) | 53k (2) | 136k (23) | 56k (2) |
| implementierung | 201k (20) | 159k (1) | 143k (43) | 132k (1) |
| code-reviewer | 173k (28) | 122k (1) | 122k (40) | 96k (3) |
Von 1.x zu 2.3.0 sank die Startlast der Hauptsession um 68 % (A) und 45 % (B), die
des Use-Case-Reviewers um 72 % und 59 %. Beim Implementierungs-Agenten waren es nur
21 % und 8 %, beim Code-Reviewer 29 % und 21 %. Die Läufe unter 2.0 bis 2.2 liegen
in beiden Projekten schon auf dem Niveau von 2.3.0, in Zielprojekt A etwa die
Hauptsession bei 65k und die Implementierung bei 156k. Die Versionen 2.1 bis 2.3
haben die Startlast also kaum bewegt. Der
Implementierungs-Agent und der Code-Reviewer bleiben schwer, weil sie die großen
Adapter-Skills vorladen. In Zielprojekt A hatte coding-guidelines zur Zeit der
Feature-Läufe 1.674 Zeilen und testing-strategy 776, in Zielprojekt B 184 und
411. Entsprechend startet die Implementierung in B mit 132k statt 159k. Zielprojekt B hatte schon
unter 1.x kleinere Adapter und startete deshalb niedriger, die Einsparung fällt
dort kleiner aus.
Template und Projekt messen Verschiedenes. Das Template zählt nur die Konfiguration bei leeren Adaptern. Im Projekt steht alles, was die API im ersten Turn als Kontext meldet, also auch Systemprompt, Werkzeuge, befüllte Adapter und Memory. Im Template sank die Grundlast um etwa 19.000 Tokens (−83 %), im Flutter-Projekt um 137.000 (−68 %). Der absolute Gewinn ist im Projekt größer, weil unter 1.x auch die großen Adapter-Dateien als Rules in jeder Session lagen.
Was ein Feature heute kostet
| Feature auf 2.3.0 | Zielprojekt A | Zielprojekt B |
|---|---|---|
| Umfang | Tippen auf einen Tag scrollt die Liste dorthin, der Abschnitt blinkt kurz | Guidelines verwalten: 5 Use Cases, neue Backend-Tabelle, Plan mit 17 Schritten, E2E-Tests je Use Case |
| Tokens gesamt | 48,3 Mio. | 153,4 Mio. |
| davon Cache-Reads | 94 % | 96 % |
implementierung | 13,9 Mio. | 44,9 Mio. |
code-reviewer | 1,2 Mio. (1 Lauf) | 5,2 Mio. (3 Läufe) |
Tokens gesamt ist die Summe der usage aller Antworten im Durchlauf, Hauptsession
und Subagents zusammen. Die beiden Features sind unterschiedlich groß, die Zahlen
lassen sich deshalb nicht gegeneinander lesen und auch nicht gegen die alten
Läufe. In Zielprojekt B entfallen 26,9 Millionen auf das Spiegeln der Doku nach
Confluence, davon erzählt der Beitrag über
Repo, Hub und Spiegel.
Ist es schlechter geworden?
Beide Feature-Läufe kamen ohne Critical- oder Major-Befund durch den Code-Review, und die Gates liefen grün. Beim kleinen Feature in A liefen sie nach zwei der drei Plan-Schritte vollständig, beim großen in B vollständig nach den Schritten 5, 10 und 17, dazwischen in Teilmengen. Auch im Experiment war die Qualität in beiden Varianten gleich.
Belastbar ist das nur begrenzt. Es ist ein Lauf je Projekt, und ein kontrollierter Vergleich gegen 1.x mit demselben Feature fehlt. Vergleichbar ist die Startlast, die Gesamttokens und die Laufzeit sind es nicht.
Was offen ist
Der größte verbleibende Posten der Startlast im Flutter-Projekt ist der
vorgeladene Adapter. Das Prüfskript warnt dort bei project-context.md mit 389
Zeilen, bei coding-guidelines, bei testing-strategy und bei
styling-guidelines mit 1.236 Zeilen. Im Nuxt-Projekt hat project-context.md 277
Zeilen, das Budget liegt bei 200. Gekürzt habe ich dort noch nicht. Die Vorlage für
coding-guidelines enthält außerdem weiter einen Abschnitt mit kanonischen
Mustern, also positiven Direktiven.
Was ich anderen raten würde
- Zuerst messen: Grundlast, Startlast je Agent und Dubletten per Skript, und die Schätzung danach an echten Transkripten gegenprüfen.
- Nur als Rule laden, was jeder Agent braucht, und das Ladeverhalten jedes Mechanismus an der aktuellen Doku prüfen.
- Rollenwissen per
skills:vorladen und darauf achten, dass die vorgeladenen Adapter klein bleiben. - Vor einer Änderung am Verhalten ein kleines Experiment mit vorher festgelegter Abbruchgrenze machen. „Nichts tun“ ist ein gültiges Ergebnis.
Wie messt ihr, was euer Agenten-Setup an Kontext kostet, bevor die erste Zeile Code entsteht?