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. Lesezeit

Hinweis 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

  1. Die Kostenbasis aus alten Transkripten ziehen, bevor du ein Experiment planst.
  2. Go- und Abbruchregel aufschreiben, bevor der erste Lauf startet.
  3. In Wegwerf-Klonen ohne Remote laufen lassen und den Vertrauensdialog einmal bestätigen.
  4. 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

  1. Doppeltes streichen, das Claude Code ohnehin einspielt (Memory-Anleitung) oder ein anderer Skill schon enthält.
  2. Größenwarnungen für Rules und vorgeladene Skills ins Prüfskript nehmen, an der Doku ausgerichtet.
  3. 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, ≈ Tokens1.5.22.0.02.3.0
Grundlast Hauptsession23.1623.8213.961
Startlast je Pipeline-Durchlauf311.804165.777129.542
davon Dubletten80.52719.8090

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.xA 2.3.0B 1.xB 2.3.0
Hauptsession201k (14)64k (2)139k (16)77k (1)
use-case-reviewer192k (22)53k (2)136k (23)56k (2)
implementierung201k (20)159k (1)143k (43)132k (1)
code-reviewer173k (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.0Zielprojekt AZielprojekt B
UmfangTippen auf einen Tag scrollt die Liste dorthin, der Abschnitt blinkt kurzGuidelines verwalten: 5 Use Cases, neue Backend-Tabelle, Plan mit 17 Schritten, E2E-Tests je Use Case
Tokens gesamt48,3 Mio.153,4 Mio.
davon Cache-Reads94 %96 %
implementierung13,9 Mio.44,9 Mio.
code-reviewer1,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?