Zum Hauptinhalt springen

Warum ein 35-KB-System-Prompt auf einem lokalen Modell scheitert

Fortgeschritten
What you'll learn
  • Rechne das Kontextbudget durch, bevor du einen Prompt umziehst, und verstehe, warum ein 35-KB-Prompt, der auf Claude unsichtbar ist, ein Viertel eines kleinen lokalen Fensters belegt
  • Wisse genau, was Ollama tut, wenn der Prompt zu lang ist — es verwirft die Mitte, behält ein paar Präfix-Tokens und meldet das nur im Server-Log
  • Erkenne die fünf Verhaltenssignale der Kontexterschöpfung bei einem lokalen Agenten, bevor er eine Stunde verschwendet
  • Strukturiere einen monolithischen Prompt in Single-Objective-Einheiten um, die hineinpassen, und übergib den Zustand zwischen ihnen auf der Festplatte
  • Stelle die Laufzeit-Regler ein, die zählen: OLLAMA_CONTEXT_LENGTH, KV-Cache-Quantisierung, Flash Attention und den Multiplikator für parallele Anfragen

Ein Beitrag, der am 13.–14. September 2026 auf die Titelseite von Hacker News kam, beschrieb eine Erfahrung, die viele bald machen werden: Ein 35-KB-System-Prompt, der auf Claude Opus problemlos lief, wurde über Ollama auf ein selbst gehostetes 27B-Modell gerichtet, und nach etwa drei Minuten las der Agent Dateien erneut, die er bereits gelesen hatte, wiederholte identische Tool-Aufrufe und schrieb fertige Arbeit neu. Am Modell war nichts „falsch“. Der Prompt plus die Sitzungshistorie hatten schlicht ein 65K-Token-Fenster überschritten, und die Laufzeit hatte begonnen, Teile der Konversation wegzuwerfen.

Diese Seite ist der Leitfaden, den sich der Autor gewünscht hätte. Es geht um Prompt-Größe versus Kontextbudget — ein anderes Problem als die Prompt-Formulierung über Modelle hinweg, behandelt in Prompts zwischen Modellen portieren, und als die Frage, welches lokale Modell man wählt, behandelt in Modelle lokal mit Ollama ausführen.

Die Rechnung, die niemand zuerst macht​

Auf Claude sind 35 KB System-Prompt ungefähr 9.000 Tokens in einem Fenster von 1.000.000 Tokens: unter 1 %. Man denkt nie darüber nach. In einer lokalen Laufzeit ist derselbe Prompt dieselben 9.000 Tokens, aber das Fenster ist das, was du konfiguriert hast, und der Standard ist klein:

Ollama-Standardkontext (aktuelle main-Docs)Maschine
4K Tokensunter 24 GiB VRAM
32K Tokens24–48 GiB VRAM
256K Tokens48 GiB VRAM oder mehr

Bei 4K passt ein System-Prompt mit 9.000 Tokens überhaupt nicht. Bei 32K sind es 28 % des Fensters, bevor der Nutzer ein Wort gesagt hat. Bei den 65K, die der HN-Autor konfiguriert hat, sind es 14 %, was in Ordnung klingt, bis man hinzurechnet, was eine Agentensitzung tatsächlich mitführt: Tool-Definitionen (ein paar hundert bis ein paar tausend Tokens), jede Datei, die der Agent liest, jedes Tool-Ergebnis und die eigene Ausgabe des Modells. Ein Coding-Agent, der drei mittelgroße Dateien liest und zwei Befehle ausführt, ist routinemäßig 20–30K Tokens tief in der Konversation. Mit 9K Fixkosten ist der nutzbare Arbeitsspeicher nach einer Handvoll Runden aufgebraucht.

Die zwei Cloud-Gewohnheiten, die das auf Claude unsichtbar machen, fehlen lokal: Das Fenster ist zwei Größenordnungen kleiner, und es gibt keine eingebaute Kompaktierung. Claude Code fasst die Konversation zusammen, wenn sie sich dem Limit nähert (Kontextverwaltung); ein roher Ollama-Endpunkt tut das nicht. Manche lokalen Agenten-Harnesses kompaktieren selbst, aber die darunterliegende Laufzeit nicht.

Miss den Prompt, bevor du migrierst (jeder OpenAI-kompatible lokale Endpunkt)

# Count what the model will actually see, from Ollama's own tokenizer.
# prompt_eval_count in the response is the tokenised prompt length.
curl -s http://localhost:11434/api/chat -d '{
"model": "qwen3:32b",
"messages": [{"role":"system","content":"'"$(cat system-prompt.md | sed 's/"/\\"/g')"'"},
             {"role":"user","content":"ping"}],
"stream": false,
"options": {"num_predict": 1}
}' | jq '{prompt_tokens: .prompt_eval_count, cached: .prompt_eval_cached_count}'

Vergleiche diese Zahl mit dem num_ctx, mit dem du läufst, und ziehe dann das Tool-Schema und ein realistisches Transkript ab. Wenn der Rest unter etwa der Hälfte des Fensters liegt, ist der Prompt für das Setup zu groß, und keine Formulierung wird das beheben.

Was Ollama tut, wenn der Prompt nicht passt​

Das ist der Mechanismus, der aus „etwas zu lang“ ein „auf rätselhafte Weise dumm“ macht, und er ist nirgends in Prosa dokumentiert; er steht im Quellcode.

Wenn der Prompt einer Anfrage länger als der Kontext ist und Truncation aktiviert ist (was sie für /api/chat und /api/generate standardmäßig ist), tut der llama.cpp-basierte Runner in Ollama in llm/llama_server.go Folgendes:

  1. Behält die ersten num_keep Tokens des Prompts. Der Standard für num_keep ist 4, gesetzt, „um Probleme bei Context-Shifts zu vermeiden“.
  2. Berechnet eine Ziellänge, die ungefähr die Hälfte des verbleibenden Kontexts für die Generierung frei lässt.
  3. Verwirft Tokens aus der Mitte: Das behaltene Präfix bleibt, das Ende bleibt, der Block dazwischen wird entfernt.
  4. Loggt truncating input prompt mit den Größen vorher/nachher auf dem Server. Die API-Antwort trägt kein Flag, und eine Client-Bibliothek sieht eine normale Completion.

Die projekteigene Testtabelle macht das Ausmaß konkret: Mit einem Kontext von 4.096 Tokens und dem Standard-num_keep wird ein zu langer Prompt auf 2.050 Tokens gekürzt. Die Hälfte des Fensters ist absichtlich für die Antwort reserviert. Ein System-Prompt mit 9.000 Tokens auf einem 4K-Fenster kommt also beim Modell als vier Tokens seines Anfangs an, dann ein Loch, dann was immer am Ende der Konversation noch hineinpasst. Die Anweisungen sind weg; die letzte Nutzernachricht überlebt. Das ist genau das Verhalten „Ziele neu formulieren, Tool-Aufrufe wiederholen“, das der HN-Beitrag beschreibt: Das Modell soll auf Basis einer Konversation handeln, deren Regeln stillschweigend gelöscht wurden.

Zwei Auswege aus der stillen Variante:

  • Sende "truncate": false in der Anfrage. Ollama gibt dann HTTP 400 zurück, mit der Meldung, dass der Prompt länger ist als der aktuell verfügbare Kontext, statt zu raten. Für einen Agenten-Harness ist das das richtige Verhalten: laut scheitern, dann kompaktieren oder neu starten.
  • Erhöhe num_keep auf die Länge deines System-Prompts, damit das Präfix einen Shift überlebt. Das bewahrt die Regeln, löscht aber weiterhin Konversation aus der Mitte; es ist ein Pflaster, keine Lösung.

Der Chat-Endpunkt hat darüber eine zweite, sanftere Schicht: Wenn die Nachrichtenliste zu lang für das Limit ist, verwirft Ollamas Prompt-Builder zuerst ältere Nachrichten und behält dabei die System-Nachricht und die letzte Runde (siehe server/prompt.go). Auch so können Zwischen-Tool-Ergebnisse verloren gehen, weshalb ein Agent „vergessen“ kann, dass er eine Datei bereits gelesen hat. Beobachte das Server-Log; mit OLLAMA_DEBUG=1 ist das Trimmen sichtbar.

Nicht nur Ollama

Der Server von llama.cpp hat dasselbe Context-Shift-Verhalten unter einem anderen Flag, vLLM lehnt zu lange Prompts standardmäßig ab, und LM Studio stellt die Kontextlänge als Einstellung pro Modell bereit, die man leicht auf ihrem kleinen Standardwert belässt. Das Muster, das man verinnerlichen sollte: Wisse, ob deine Laufzeit abschneidet oder einen Fehler wirft, und bevorzuge den Fehler.

Die fünf Fehlersignale​

Der Autor des HN-Beitrags führte eine Liste, wie Kontexterschöpfung von außen aussah, und sie deckt sich mit dem, was über jeden Agenten mit kleinem Fenster berichtet wird. Behandle jedes dieser Signale als „das Fenster ist voll“, nicht als „das Modell ist schlecht“:

  1. Identische aufeinanderfolgende Tool-Aufrufe. Das Modell hat das vorherige Ergebnis nicht mehr im Blick, also fragt es erneut.
  2. Erneutes Lesen einer bereits gelesenen Datei. Gleiche Ursache; das Leseergebnis wurde weggetrimmt.
  3. Neuformulierung des Ziels in eigenen Worten mitten in der Aufgabe, manchmal mit Drift. Der System-Prompt wurde abgeschnitten, und das Modell rekonstruiert die Aufgabe aus dem Ende der Konversation.
  4. Fehler beim Parsen von Tool-Aufrufen, die nach einer Reihe sauberer Aufrufe auftreten. Sobald die Beispiele und das Schema im Prompt weg sind, fallen kleine Modelle auf ihre Standardformatierung zurück.
  5. Hohe Rundenzahl im Verhältnis zu tatsächlich geänderten Dateien. Das Verhältnis ist die günstigste Metrik, die man in einem Harness verfolgen kann; wenn sie hochschnellt, anhalten und mit frischem Kontext neu starten.

Wenn du über einen OpenAI-kompatiblen Client arbeitest, logge prompt_eval_count in jeder Runde. Wenn der Wert nicht mehr wächst, während die Konversation weiter wächst, hat die Truncation begonnen.

Umstrukturieren: Single-Objective-Prompts und Zustand auf der Festplatte​

Ein 35-KB-Prompt enthält meist die Regeln eines ganzen Produkts: Personas, Coding-Standards, Tool-Etikette, Ausgabeformate, Sonderfälle und eine lange Liste von „mach niemals X“. Auf einem 1M-Token-Modell funktioniert das mit roher Gewalt. Auf einem 32K-Modell nicht, und die Lösung, bei der der HN-Autor landete, ist dieselbe, für die die stärksten Kommentatoren unabhängig davon plädierten: Teile den Prompt in Single-Objective-Einheiten auf und führe jede in einer eigenen kurzen Sitzung aus.

Guided walkthrough1 of 6
  1. Gehe die 35 KB durch und markiere jeden Absatz mit der Aufgabe, der er dient: Planen, Bearbeiten, Testen, Reviewen, den Commit schreiben. Die meisten Monolithen enthalten fünf bis acht verschiedene Aufgaben plus einen gemeinsamen Kern (Persona, Repo-Fakten, Sicherheitsregeln), der unter 2 KB liegt.

Eine Übergabedatei, die die nächste Single-Objective-Einheit zuerst liest

# HANDOFF — written by unit 2 (implement), read by unit 3 (test)
Objective completed: renamed getUser -> fetchUser across src/
Files changed: src/api/user.ts, src/pages/profile.tsx, src/hooks/useUser.ts
Not done: tests still reference getUser (tests/user.test.ts)
Constraints carried forward: no new dependencies; keep TypeScript strict
Next unit should: update tests, run `npm test`, write result to HANDOFF.md

Das ist auch die ehrliche Antwort an den Kommentator, der sagte, ein 35-KB-Prompt sei „auf jedem LLM verwirrend und unfokussiert“. Frontier-Modelle kaschieren das Aufblähen; lokale Modelle legen es offen. Die Aufteilung nach Zielen verbessert die Ergebnisse meist auch auf Claude, und es ist das Design, das Claude plus lokale Modelle voraussetzt, wenn es günstige Schritte lokal routet.

Die Laufzeit tunen​

Sobald der Prompt die richtige Größe hat, entscheiden vier Regler, wie viel Fenster du dir tatsächlich leisten kannst. Alle stammen aus der Ollama-FAQ und den Docs.

ReglerWas er tutFallstrick
OLLAMA_CONTEXT_LENGTH=65536 ollama serve (oder num_ctx pro Anfrage, oder PARAMETER num_ctx in einem Modelfile)Setzt das FensterGrößerer Kontext kostet Speicher. Die Docs sagen das unverblümt; der KV-Cache wächst linear damit
OLLAMA_KV_CACHE_TYPE=q8_0Quantisiert den KV-Cache; etwa die Hälfte des Speichers des Standards f16. q4_0 ist etwa ein ViertelErfordert Flash Attention; kleine Qualitätskosten, größer mit q4_0
OLLAMA_FLASH_ATTENTION=1Aktiviert Flash Attention; Voraussetzung für KV-Quantisierung und reduziert Speicher bei langen KontextenNicht jedes Backend/Modell unterstützt es; setzen und das Server-Log prüfen
OLLAMA_NUM_PARALLEL (Standard 1)Gleichzeitige Anfragen pro ModellDer benötigte Speicher skaliert mit OLLAMA_NUM_PARALLEL × OLLAMA_CONTEXT_LENGTH. Zwei parallele Slots bei 64K kosten den KV-Speicher von einem bei 128K

Eine sinnvolle Reihenfolge bei festem Speicherbudget: Wähle das größte Modell, das du mit einem kleinen Fenster halten kannst, aktiviere dann Flash Attention und den q8_0-KV-Cache, und erhöhe dann den Kontext, bis das Server-Log zeigt, dass er auf die CPU überläuft. Auf GPU und CPU verteilter Kontext ist der Punkt, an dem die Tokens pro Sekunde einbrechen; mehrere HN-Kommentatoren berichteten von dichten 27B–32B-Modellen in FP8 mit sehr langen Fenstern auf Dual-GPU-Setups, aber auf einer einzelnen Box mit 128 GB Unified Memory lag die praktische Obergrenze bei den 65K, die der Autor verwendete.

Prefix-Caching hilft, aber nur für das Präfix

Ollama meldet prompt_eval_cached_count in jeder Antwort. Ein stabiler System-Prompt ganz am Anfang der Konversation wird über Anfragen hinweg aus dem KV-Cache wiederverwendet, sodass seine Kosten einmal pro Sitzung anfallen, nicht pro Runde. Alles nach der ersten Änderung in der Konversation wird neu berechnet. Setze das feste Material zuerst und das flüchtige Material zuletzt — dieselbe Regel wie bei Claudes Prompt-Caching (Ökonomie des Prompt-Cachings).

Migrations-Checkliste​

  • Tokenisiere den System-Prompt mit dem lokalen Tokenizer und notiere die Zahl.
  • Setze num_ctx explizit. Verlasse dich nie auf den Standard; er hängt vom VRAM der Maschine ab.
  • Entscheide: abschneiden oder Fehler. Für Agenten: sende "truncate": false und behandle den 400.
  • Teile den Prompt nach Zielen auf; gemeinsamer Kern unter 2K Tokens; jede Einheit 2–4K.
  • Übergib zwischen Einheiten über eine Datei; beende die Sitzung nach jeder Einheit.
  • Logge prompt_eval_count pro Runde und alarmiere, wenn der Wert ein Plateau erreicht.
  • Aktiviere Flash Attention und den q8_0-KV-Cache, bevor du das Fenster vergrößerst.
  • Beobachte das Server-Log in der ersten Woche auf truncating input prompt.
Drücke Enter oder die Leertaste, um die Karte umzudrehen. Nutze die Pfeiltasten links und rechts, um zwischen den Karten zu wechseln.Begriff angezeigt.
1 / 6

Teste dich selbst

0/5
  1. Ein System-Prompt mit 9.000 Tokens wird mit num_ctx 4096 und Standardeinstellungen an Ollama gesendet. Was erreicht das Modell?
  2. Welches Signal zeigt Kontexterschöpfung bei einem lokalen Agenten am zuverlässigsten an?
  3. Du willst, dass die Laufzeit laut scheitert, wenn ein Prompt zu lang ist. Was sendest du?
  4. Du setzt OLLAMA_NUM_PARALLEL=4 und OLLAMA_CONTEXT_LENGTH=32768. Wie viel Speicher braucht der KV-Cache im Verhältnis zu einer Anfrage bei 32K?
  5. Welche Änderung liefert die dauerhafteste Lösung für einen 35-KB-Prompt auf einem Modell mit 32K-Fenster?

Quellen & weiterführende Literatur​

Weiter​