Max Nardit
← Artikel
9 mindev

Claude Code Background-Subagents und dein Nutzungslimit: Anthropics 99-Prozent-Angabe zur Statuszeile und die Cache-Writes, die du messen kannst

Isolation ist der Sinn eines Subagents, und in meinen Transkripten bestand der Großteil seiner Rechnung aus fünfminütigen Cache-Writes. Gemessen auf einem Max-Plan: Background-Subagents etwa 11,5 % der bepreisten Nutzung, 64 % davon Cache-Writes. Ein Python-Skript zum Nachrechnen und vier Hebel, jeweils mit dem Teil der Rechnung, auf den sie wirken.

Inhalt

Am 4. November schrieb Anthropics Entwickler-Account, Background-Subagents in Claude Code würden jetzt weniger von deinem Nutzungslimit verbrauchen, weil jedes Status-Update „etwa 99 % weniger“ koste. Am selben Tag kam Claude Code 2.1.318 mit drei weiteren Änderungen für Background-Subagents. Ich wollte wissen, was das mit meiner eigenen Rechnung macht, und bin zwei Wochen meiner Transkripte durchgegangen. Den Request, der laut Anthropic billiger geworden ist, habe ich darin nirgends gefunden. Was sie zeigen, ist die eigentliche Arbeit der Subagents, und fast zwei Drittel dieser Kosten sind fünfminütige Cache-Writes. Die Änderung an der Statuszeile betrifft einen anderen Request, und der Cache-Fix in 2.1.318 zielt auf eine Quelle dieser Writes, die nach einem groben Proxy in meinen Logs klein aussah.

Die Zahlen unten stammen aus einem Max-Abo auf der First-Party-API, 22. Oktober bis 4. November. Das Zeitfenster endet an dem Tag, an dem 2.1.318 erschien. Es ist also eine Baseline, kein Vorher-nachher-Vergleich.

Was haben Claude Code 2.1.316 bis 2.1.318 für Background-Subagents geändert?

Einen Fix für einen Hinweis, einen Cache-Fix und ein neues Effort-Setting, verteilt auf drei Releases. Die Release Notes zu 2.1.318 beheben, dass der Hinweis „Now using usage credits“ „etwa alle 30 Sekunden, während ein Background-Subagent läuft“, erneut auftauchte. Sie schieben den Shell-Befehl eines Background-Subagents nach 3 Minuten in den Hintergrund, „bei allen Providern, damit der Prompt-Cache nicht abläuft, während er läuft“. Und auf Bedrock, Vertex, Foundry oder mit abgeschalteter Telemetrie „kann“ die Fortschrittszusammenfassung eines Subagents bei kaltem Prompt-Cache „jetzt bis zu 5 Minuten auf die ausstehende Antwort des Subagents warten“.

2.1.316 brachte CLAUDE_CODE_SUBAGENT_EFFORT, das Effort-Level, mit dem Subagents laufen, „wenn nichts anderes eines vorgibt, statt das der Session zu erben“. Mit 2.1.317 nutzen Subagents, die auf einem anderen Modell als die Hauptunterhaltung laufen, „das gespeicherte Effort-Level dieses Modells oder seinen Default“, außerdem bekam die Admin-API des Claude-Apps-Gateways einen Kostenbericht (GET /v1/organizations/analytics/cost), der bisherige Ausgaben pro Entwickler, pro Tag oder pro Gruppe aufschlüsselt.

Keine der drei Release Notes erwähnt die Änderung an der Statuszeile. Es gibt sie nur in Anthropics Post, der sie v2.1.316 und neuer zuordnet.

Was behauptet Anthropic über die Status-Updates von Claude-Code-Subagents?

Dass die Statuszeile früher alle 30 Sekunden die ganze Subagent-Unterhaltung neu gelesen hat und das jetzt nicht mehr tut. Der erste Post:

Background subagents in Claude Code now use less of your usage limit.
A running subagent updates its status line every 30 seconds, and each
update used to re-read its whole conversation. Now Haiku 5.5 writes it
from short notes instead, so each update costs about 99% less.

Der Folgepost liefert die Zahl auf Session-Ebene: „Sessions mit Background-Subagents haben etwa 5 % weniger vom Limit verbraucht, mit demselben Limit bekommst du also etwa 6 % mehr Prompts.“ Dazu heißt es, die Änderung sei „für die meisten Pro-, Max- und Ultra-Nutzer und für alle in Team und Enterprise live“.

Alle drei Zahlen stammen von Anthropic selbst, gemessen über die eigenen Nutzer, ohne veröffentlichte Methode. „Für die meisten live“ heißt Rollout, ein einzelner Account hat es also vielleicht, vielleicht auch nicht. Die Zeile in 2.1.318 zu Bedrock, Vertex, Foundry und Setups ohne Telemetrie legt nahe, dass die Fortschrittszusammenfassung dort weiterhin auf den eigenen Cache des Subagents wartet. Meine Lesart, nicht etwas, das in den Notes steht: Der billige Weg ist ein First-Party-Feature.

In meinen eigenen Logs konnte ich es auch nicht prüfen. Das Transkript eines Subagents unter ~/.claude/projects/<project>/<session-id>/subagents/ protokolliert dessen Requests mit Modell, Effort und Token-Verbrauch. In meinem Zeitfenster liefen ein paar hundert Background-Subagent-Requests auf 2.1.316 oder neuer, und keine der Subagent-Dateien enthält einen Haiku-Request. Die einzigen Haiku-Requests in den Session-Dateien stammen aus Sessions, die ich selbst auf Haiku gefahren habe. Entweder hatte der Rollout diesen Account noch nicht erreicht, oder der Statuszeilen-Request wird nicht dort geschrieben, wo ich nachgesehen habe. Welches von beiden, kann ich nicht sagen. Dieser Audit kann diese Requests also weder identifizieren noch die 5 % bestätigen oder widerlegen.

Warum bekommen Claude-Code-Subagents einen fünfminütigen Prompt-Cache?

Weil Claude Code den einstündigen Cache per Default nur der Hauptunterhaltung und ein paar Hilfs-Requests gibt. Die Doku zum Prompt Caching sortiert jeden Request in einen von zwei Töpfen. In einem Abo innerhalb des Plankontingents bekommt die Hauptunterhaltung eine Stunde. Alles andere, also Subagents, Workflows, Teammates, Forks und Compaction, bekommt fünf Minuten, abgesehen von serverseitig gesteuerten Hilfs-Requests, die Anthropic auf eine Stunde setzt. Subagents „bekommen selbst im Abo fünf Minuten, bis du dich für eine längere entscheidest“.

Das ist der Grund für die 3-Minuten-Regel in 2.1.318. Ein Subagent, der mehr als fünf Minuten auf einen Build oder eine Testsuite wartet, kommt zu einem abgelaufenen Cache zurück, und sein nächster Request schreibt seinen Kontext erneut zum Cache-Write-Preis. Den Befehl in Minute drei in den Hintergrund zu schieben, soll genau das verhindern.

Die Doku ergänzt, dass ein Subagent eine eigene Unterhaltung mit eigenen Anweisungen und eigenem Tool-Set startet und sein erster Request „den Cache des Parents nicht liest, weil sich die beiden Präfixe unterscheiden“. Ein Fork ist die Ausnahme: Er erbt das Präfix des Parents und liest bei seinem ersten Request dessen Cache.

Auch die Hauptunterhaltung ist betroffen. Sobald ein Abo über sein Planlimit hinausgeht und Usage Credits verbraucht, setzt Claude Code auch die Hauptunterhaltung auf die fünfminütige TTL, außer du hast promptCacheTtl festgelegt. Genau diesen Moment markiert der Hinweis „Now using usage credits“. Fünfminütige Writes sind pro Token billiger, aber eine Hauptunterhaltung, die länger als fünf Minuten ruht, baut ihren Cache dann beim nächsten Prompt neu auf, und mit Credits wird dieser Neuaufbau abgerechnet. Es ist derselbe Wechsel, der die Idle-Compaction abschaltet, sobald du auf Credits bist.

Wie misst du, was dich Background-Subagents in Claude Code kosten?

Fang mit /usage an und lies dann die Transkripte. In einem Pro-, Max-, Ultra-, Team- oder Enterprise-Plan ordnet die /usage-Aufschlüsselung die jüngste Nutzung Skills, Subagents, Plugins und MCP-Servern zu, als Prozentwerte über die letzten 24 Stunden oder 7 Tage, berechnet aus der lokalen Historie. Das liefert dir den Anteil, nicht die Ursache. Die Zeile Prompt cache (main) in derselben Ansicht „deckt nur die Hauptunterhaltung ab, keine Subagents“, für das Cache-Verhalten brauchst du also die Dateien.

Jede Assistant-Zeile in einem Transkript trägt ein usage-Objekt (Cache-Writes nach TTL aufgeteilt, Cache-Reads, Output), das model, den effort und die Claude-Code-version. Neben jeder Subagent-Datei liegt eine .meta.json, die sagt, ob er im Hintergrund lief und ob er ein Fork war. Das Skript unten summiert einen Datumsbereich in Töpfe und gewichtet sie mit den Opus-5.5-Listenpreisen aus der Preistabelle fürs Caching: 4 $ Input, 5 $ fünfminütiger Write, 8 $ einstündiger Write, 0,20 $ Cache-Read, 20 $ Output pro Million Tokens. Im Abo ist das eine Gewichtung, keine Rechnung. Anthropic veröffentlicht nicht, wie Tokens in Plan-Nutzung umgerechnet werden.

python
# subagent_cost.py: python3 -I subagent_cost.py 2026-10-22 2026-11-05
import collections, glob, json, os, sys
from datetime import datetime, timezone
 
PRICE = dict(inp=4, w5=5, w1h=8, read=0.20, out=20)  # Opus 5.5 list, $ per MTok
start, end = (datetime.fromisoformat(d).replace(tzinfo=timezone.utc) for d in sys.argv[1:3])
seen, tok, info = set(), collections.defaultdict(collections.Counter), collections.Counter()
 
def bucket_of(path):
    if "/subagents/" not in path:
        return "main"
    try:
        meta = json.load(open(path[:-6] + ".meta.json"))
    except (OSError, ValueError):
        meta = {}
    if meta.get("agentType") == "fork":
        return "fork"
    return "sub-" + meta.get("requestShape", "unknown")  # sub-background / sub-foreground
 
def parts(u):
    # A request with an advisor call lists each model call under "iterations";
    # the top-level TTL split then covers only the first one.
    its = u.get("iterations") or [dict(u, type="message")]
    for it in its:
        cc = it.get("cache_creation") or {}
        yield it.get("type"), dict(
            inp=it.get("input_tokens") or 0, read=it.get("cache_read_input_tokens") or 0,
            w5=cc.get("ephemeral_5m_input_tokens") or 0, w1h=cc.get("ephemeral_1h_input_tokens") or 0,
            out=it.get("output_tokens") or 0, writes=it.get("cache_creation_input_tokens") or 0)
 
paths = sorted(glob.glob(os.path.expanduser("~/.claude/projects/**/*.jsonl"), recursive=True))
for path in paths:
    b, first, prev = bucket_of(path), True, None
    for line in open(path, errors="ignore"):
        try:
            rec = json.loads(line)
            msg, u = rec["message"], rec["message"]["usage"]
            ts = datetime.fromisoformat(rec["timestamp"].replace("Z", "+00:00"))
        except (ValueError, KeyError, TypeError):
            continue
        is_first, gap = first, prev is not None and (ts - prev).total_seconds() > 300
        first, prev = False, ts  # copied history still sets the order
        if not msg.get("id"):
            info[(b, "skipped: no message id")] += 1
            continue
        if msg["id"] in seen:  # streamed chunks and resumed sessions repeat a message
            continue
        seen.add(msg["id"])
        if not start <= ts < end:
            continue
        for kind, p in parts(u):
            t = tok[b if kind == "message" else "advisor (other model, not priced)"]
            t.update(p)
            t["w_unsplit"] += p["writes"] - p["w5"] - p["w1h"]
            t["w_first"] += p["writes"] if is_first else 0
            t["w_after_gap"] += p["writes"] if gap else 0
        info[(b, rec.get("version"), msg.get("model"), rec.get("effort"))] += 1
 
priced = {b: sum(t[k] * PRICE[k] / 1e6 for k in PRICE) for b, t in tok.items() if "advisor" not in b}
grand = sum(priced.values()) or 1
for b, t in sorted(tok.items()):
    w = max(t["writes"], 1)
    print(f"\n{b}: " + (f"{100 * priced[b] / grand:.1f}% of priced usage" if b in priced else f"{t['inp'] + t['out']} tokens"))
    if b in priced and priced[b]:
        print("  split", {k: f"{100 * t[k] * PRICE[k] / 1e6 / priced[b]:.0f}%" for k in PRICE if t[k]})
    print(f"  cache-read tokens per cache-write token: {t['read'] / w:.1f}")
    print(f"  writes on a file's first request: {100 * t['w_first'] / w:.0f}%")
    print(f"  writes after a >5 min gap: {100 * t['w_after_gap'] / w:.2f}%")
    print(f"  writes with no TTL split: {t['w_unsplit']}")
for key, n in sorted(info.items(), key=str):
    print(*key, n)

Zwei Dinge musst du richtig machen. Dedupliziere nach Message-ID über alle Dateien hinweg, nicht pro Datei: Eine gestreamte Antwort schreibt dasselbe usage in mehrere Zeilen, und eine fortgesetzte Session kopiert frühere Nachrichten in eine neue Datei. Mein erster Durchlauf hat beides falsch gemacht und die Hauptunterhaltung kräftig überzählt. Filtere nach dem timestamp jedes Eintrags, nicht nach dem Änderungsdatum der Datei, sonst zieht eine lang laufende Session Wochen alter Requests ins Zeitfenster. Manche Requests enthalten außerdem einen Advisor-Aufruf an ein zweites Modell unter usage.iterations. Das Skript nimmt die TTL-Aufteilung aus jeder Iteration und weist die Advisor-Tokens in einer eigenen Zeile aus, ohne Preis, weil sie zu den Preisen eines anderen Modells abgerechnet werden. Wenn du hauptsächlich ein anderes Modell nutzt, setz dessen Zeile aus der Preistabelle ein.

Was haben zwei Wochen Transkripte von Claude-Code-Subagents gezeigt?

Dass Background-Subagents etwa ein Neuntel meiner bepreisten Nutzung ausmachten und fast zwei Drittel ihrer Kosten Cache-Writes waren.

22. Okt. bis 4. Nov., bepreist zu Opus-5.5-ListenpreisenHauptunterhaltungBackground-SubagentsForks
Anteil an der bepreisten Nutzung87,4 %11,5 %1,0 %
Cache-Writes, Anteil am Topf52 % (einstündig)64 % (fünfminütig)63 % (fünfminütig)
Cache-Reads, Anteil am Topf39 %24 %29 %
Output, Anteil am Topf9 %12 %8 %
Cache-Read-Tokens pro Cache-Write-Token29,69,411,7

Foreground-Subagents lagen bei 0,1 %, zu wenig, um etwas herauszulesen, und Advisor-Aufrufe an ein zweites Modell sind aus allen Anteilen herausgerechnet. Background-Subagents protokollierten etwa 9 Cache-Read-Tokens pro Cache-Write-Token, die Hauptunterhaltung etwa 30. Auf Opus 5.5 kostet ein Cache-Read das 0,05-Fache des Basis-Inputs und ein fünfminütiger Write das 1,25-Fache, ein Faktor von 25. Solange Reads die Writes um weniger als 25 zu 1 übertreffen, sind die Writes also der größere Posten.

Die meisten davon entstehen nicht am Anfang. Erste Requests machen zusammen 10 % der Cache-Writes der Background-Subagents aus. Auch der Output erklärt nicht viel: Bei 12 % der Kosten, bepreist mit 20 $, deckt er selbst dann nur etwa 3 der 64 Punkte ab, wenn jedes Output-Token einmal zu 5 $ zurückgeschrieben würde. Die übrigen rund 85 % sind alles andere, was in späteren Turns in den Cache kam: Tool-Ergebnisse, Anweisungen, Neuaufbauten nach einer Invalidierung. Was davon, sagen die Transkripte nicht, also kann ich dir nicht sagen, wie viel davon Tool-Output ist. Genau diesen Teil lohnt es sich zuerst zu testen, weil du ihn steuerst.

Lange Pausen waren selten. Requests, die mehr als fünf Minuten nach dem vorherigen Request desselben Subagents kamen, trugen 0,73 % der Cache-Writes der Background-Subagents. Das ist ein Proxy für den Ablauf, auf den die 3-Minuten-Regel zielt, keine Messung davon: Eine Lücke beweist nicht, dass der Cache abgelaufen ist, und diese Writes enthalten auch neue Inhalte. Es zeigt aber, dass meine Subagents selten lange genug untätig waren, dass sich die Frage überhaupt stellt. Ein Workload mit langen Builds in Subagents sähe anders aus, und dieselbe Zeile des Skripts zeigt dir, welchen du hast.

Das effort-Feld passte nicht zu meinen Settings. In meiner settings.json steht effortLevel: xhigh, und jeder Background-Subagent-Request im Zeitfenster lief auf medium. Dafür gibt es zwei Erklärungen. Meine Sessions starten mit --effort medium auf der Kommandozeile, und Subagents auf dem Modell der Session erben deren Level. Und laut Doku zur Modellkonfiguration „zählt“ ein effortLevel auf oberster Ebene in den User-Settings ohnehin „nicht für Opus 5.5“. Es ist die ältere Form, die weiterhin für Opus 5, Fable 5.1 und ältere Modelle gilt. Das effort-Feld im Transkript zeigt, was tatsächlich lief.

Welche Claude-Code-Settings ändern, was Background-Subagents verbrauchen?

Vier Hebel, jeweils mit dem Teil der Rechnung, auf den sie wirken. Keiner davon ist hier gemessen. Hier würde ich zuerst testen.

  • Was ein Subagent einliest (Teil des Write-Anteils von 64 %). Ein komplettes Testlog, eine ganze Datei, wo ein Zeilenbereich reichen würde, eine ungefilterte Suche: Neuer Inhalt kommt als Write zum 1,25-Fachen des Inputs in den Cache, bevor ein späterer Turn ihn billig lesen kann. Fordere Zusammenfassungen von Tools an, die das unterstützen, begrenze den Output in den Befehlen, die du einem Subagent gibst, und bündle zusammengehörige Arbeit in einem Subagent, damit er dieselben Dateien nicht zweimal lädt. Der erste Request eines fortgesetzten Subagents kann den Cache lesen, den der ursprüngliche Lauf aufgewärmt hat, aber nur, solange dieser Cache noch lebt, per Default also innerhalb von fünf Minuten.
  • Modell (bepreist jede Spalte neu). model: haiku im Frontmatter eines Subagents für Suchen und Zusammenfassungen. Claude Code bestimmt das Modell eines Subagents zuerst aus dem Parameter des jeweiligen Aufrufs, dann aus dem Frontmatter, dann aus CLAUDE_CODE_SUBAGENT_MODEL. Die Variable allein ist also nur ein Fallback. Der Post zu Haiku 5.5 als Subagent behandelt dessen Effort-Level und die 100K-Preisgrenze, die auf diesem Modell mehr zählen als irgendwo sonst.
  • Effort (Output, 12 %, plus das Thinking, das im Kontext mitläuft). Setz effort: im Frontmatter der Agents, die du selbst definierst, oder CLAUDE_CODE_SUBAGENT_EFFORT für den Rest. Stand heute steht die Variable im Changelog zu 2.1.316, aber nicht auf der Seite zu den Umgebungsvariablen, prüf nach dem Setzen also das effort-Feld in einem Subagent-Transkript. CLAUDE_CODE_EFFORT_LEVEL schlägt jedes Subagent-Setting.
  • Cache-TTL für Subagents (nur der Fall, dass der Cache abläuft). subagentPromptCacheTtl: "1h" oder CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL=1h setzt die Stunde für den ganzen Topf außerhalb der Hauptunterhaltung, und FORCE_PROMPT_CACHING_5M=1 überschreibt das. Ein einstündiger Write kostet 8 $ statt 5 $. Bepreise ich meine protokollierten fünfminütigen Writes zum Stundensatz neu, steigen die 64 % des Topfs um 60 %, die Zeile der Background-Subagents also um grob 38 %, bevor vermiedene Writes eingerechnet sind. Das lohnt sich nur, wenn Subagents oft länger als fünf Minuten untätig sind, und meine waren das selten.

Claude Code Background-Subagents: Sind die 99 Prozent die Zahl, auf die du achten solltest?

Nicht für deine eigene Rechnung. Anthropics 5 % sind Plan-Nutzung über alle seine Nutzer, und aus einem einzelnen Account kann ich sie nicht reproduzieren. Die Zahl, die du beobachten kannst, steht in deinen eigenen Transkripten. In meinen waren Background-Subagents 11,5 % der bepreisten Nutzung und ihre Cache-Writes etwa 7 % des Gesamtwerts. Die Write-Spalte ist also die erste Stelle, an der du nachsehen solltest.

Diese Writes sind zugleich der Preis für das, wofür ein Subagent da ist. Ein Subagent hält ein Testlog oder einen Crawl aus der Hauptunterhaltung heraus, wo dieser Inhalt bei jedem späteren Turn bis zur nächsten Compaction erneut gelesen würde. Der Subagent zahlt dafür, ihn zu cachen, und wirft den Kontext dann weg. Ob sich dieser Tausch lohnt, hängt vom Workload ab, und ich habe denselben Job nicht in beiden Varianten laufen lassen, um es zu messen. Bevor du mehrere Background-Subagents auf einen Pro-, Max- oder Ultra-Plan setzt, lass das Skript einmal laufen, schau dir diese Spalte an und kürze, was jeder Subagent einliest, bevor du kürzt, wie viele du laufen lässt.

01 / Diskussion

Hier gibt es keine Kommentarspalte. Diskussionen laufen auf X.

Max Nardit@mnardit

02 / Weitere Artikel