Der Next-Token-Loop: Ein mentales Modell von LLMs, bevor man mit Agenten arbeitet
Erfahren Sie, wie Tokens, Kontextfenster, Sampling und der Generierungszyklus funktionieren, anhand kleiner Offline-Beispiele in Python, die erklären, warum RAG, ReAct und LangGraph existieren.
Ingenieure, die LLMs über ein Framework wie LangChain nutzen, können oft nicht erkennen, wenn etwas schiefgeht, ob das Framework, die darin eingebettete API oder das Modell selbst schuld ist. Die Lösung liegt in einem klaren mentalen Modell: Ein LLM ist eine Funktion zur Vorhersage des nächsten Tokens, die in einem Loop wiederholt aufgerufen wird. Mit diesem Verständnis ist RAG eine Methode, um zu bestimmen, was in die Funktion eingegeben wird, ReAct eine Methode zur Verarbeitung des Ausgangstextes und LangGraph eine Methode, um mehrere Aufrufe zu koordinieren. Die untenstehenden Beispiele laufen lokal mit Python und einer kleinen Bibliothek – es ist keine API-Schlüssel erforderlich.
Generierung bedeutet Vorhersage des nächsten Tokens in einem Loop
Gegeben ist eine Sequenz von Tokens – das Modell gibt für jedes mögliche nächste Token eine Wahrscheinlichkeit an. Ein Token wird ausgewählt, hinzugefügt und die längere Sequenz wird erneut in den Loop eingespeist. Dies wiederholt sich, bis ein Stopptoken oder eine Längenbeschränkung erreicht ist. Die strömende Ausgabe zeigt diesen Loop einfach Token für Token an.
Zwei Konsequenzen ergeben sich daraus:
- Es gibt keinen Plan. Jedes Token hängt nur von den vorhergehenden Tokens ab.
- Es gibt kein Gedächtnis zwischen Aufrufen. Jeder Anfragestart erfolgt genau mit den von Ihnen gesendeten Tokens – deshalb gibt es die retrieval-augmented Generation.
Tokens sind Teilwörter, keine Wörter
Ein Modell erkennt Ganzzahlen, nicht Text. Ein Tokenisierer wandelt Zeichenketten in Token-IDs um und wieder zurück. Häufige Wörter werden in der Regel zu einem Token, während seltene Wörter und Code in mehrere Tokens aufgeteilt werden. Eine praktische Schätzung: Ein Token entspricht etwa drei Vierteln eines Wortes.
Der Auszug lädt o200k_base, die Kodierung der gpt-4o-Familie, über tiktoken herunter, kodiert einen Satz und gibt jede ID zusammen mit ihrem Textfragment aus. Beachten Sie, dass „LangGraph“ zu zwei Tokens wird und dass führende Leerzeichen zum folgenden Token gehören.
import tiktoken
# tokenizer of the gpt-4o model family
enc = tiktoken.get_encoding("o200k_base")
ids = enc.encode("LangGraph orchestrates agents.")
print(ids)
# [30741, 9922, 109873, 1381, 19297, 13]
for i in ids:
print(i, repr(enc.decode([i])))
# 'Lang' 'Graph' ' orchestr' 'ates' ' agents' '.'
Training und Inferenz sind getrennte Phasen
Durch Training werden die Gewichte angepasst, indem über große Mengen an Text die nächsten Token vorhergesagt werden. Bei der Inferenz wird das bereits festgefrorene Modell verwendet. Der Eingabetext ändert die Gewichte niemals, weshalb Informationen, die dem Modell fehlen, zur Zeit der Inferenz im Eingabetext enthalten sein müssen. Genau das macht RAG.
Das Kontextfenster ist die gesamte Welt des Modells
Eine einzelne Aufrufung kann nur eine begrenzte Anzahl an Token verarbeiten, und diese Grenze, das Kontextfenster, umfasst sowohl den Eingabetext als auch die erzeugte Ausgabe zusammen. Alles, was außerhalb dieses Fensters liegt, existiert für diesen Aufruf nicht. Ein Chat wirkt kontinuierlich nur deshalb, weil die Anwendung jedes Mal frühere Nachrichten erneut sendet, und RAG existiert, weil Dokumentensammlungen nicht vollständig passen, weshalb stattdessen nur einige relevante Abschnitte abgerufen werden.
Budgetierung ist einfache Arithmetik. Der Hilfsprogramm zählt die Prompt-Token mithilfe des oben genannten Tokenisierers und subtrahiert sie zusammen mit einer Reserve für die Antwort von einem Fenster von 128.000 Token – dem Wert, der für gpt-4o-mini angegeben ist. Die Limits unterscheiden sich je nach Modell und Version, daher sollten Sie die aktuelle Dokumentation Ihres Anbieters prüfen.
def count_tokens(text: str) -> int:
return len(enc.encode(text))
prompt = "Summarize the attached design doc."
window = 128_000 # e.g. gpt-4o-mini
reserve = 1_000 # room for the answer
used = count_tokens(prompt)
print("left:", window - reserve - used)
Die Temperatur bestimmt, wie das nächste Token ausgewählt wird
Durch Sampling wird ein Token aus der Verteilung des Modells ausgewählt, und die Temperatur verändert diese Verteilung. Bei nahe Null gewinnt fast immer das am häufigsten vorkommende Token. Bei 1,0 und höher erhalten seltener vorkommende Tokens eine echte Chance. Verwenden Sie eine Temperatur von 0 für Extraktion, strukturierte Ausgaben sowie Agenten zur Aufrufung von Tools, und etwa 0,7 bis 1,0 für das Erarbeiten von Entwürfen und Brainstorming. Unser Leitfaden zu Temperatur, Top-K und Top-P behandelt die anderen Einstellungen zum Sampling.
Der Loop in Miniaturform aufbauen
Im nächsten Beispiel wird ein echter Tokenisierer mit einem künstlichen Modell kombiniert, das aus einer Abfrage-Tabelle besteht. Es ist zwar sehr klein, folgt aber dem gleichen Zyklus wie ein echtes LLM: Kodieren, Vorhersage, Hinzufügen, Beenden.
Zunächst wird ein kleines Korpus tokenisiert und jedes benachbarte ID-Paar gezählt. Für jeden Token speichert die Tabelle nur seinen häufigsten Nachfolger, wodurch es sich um einen gierigen Vorhersager handelt – das Äquivalent zu einer Temperatur von null.
CORPUS = (
"the agent calls the model. "
"the model returns a token. "
"the agent calls the tool. "
"the tool returns a result."
)
ids = enc.encode(CORPUS)
counts = {}
for a, b in zip(ids, ids[1:]):
counts.setdefault(a, {})
counts[a][b] = counts[a].get(b, 0) + 1
# greedy "model": token -> likeliest successor
table = {
a: max(s, key=s.get)
for a, s in counts.items()
}
Der Loop kodiert eine Anfrage, sucht nach dem wahrscheinlichsten Nachfolger des letzten Tokens, fügt ihn hinzu und gibt den decodierten Text aus. Fehlt ein Nachfolger, wird None zurückgegeben, was einem Stop-Token entspricht. Der Hinzufügungsschritt ist der entscheidende: Das Ausgabeergebnis wird zum Eingabedaten.
seq = enc.encode("the agent calls")
for _ in range(3):
nxt = table.get(seq[-1])
if nxt is None: # our toy "stop token"
break
seq.append(nxt) # output becomes input
print(enc.decode(seq))
Die Sequenz wächst um ein Token pro Schritt:
the agent calls the
the agent calls the model
the agent calls the model.
Ein Detail ist aufschlussreich. Im Korpus folgt „the“ genauso oft auf „model“ wie auf „tool“, und max behält das, was es zuerst gesehen hat. Reale Modelle stoßen ständig auf solche knappen Entscheidungen, weshalb Einstellungen zur Stichprobenziehung wichtig sind.
Ausführung der Beispiele
Sammeln Sie die Auszüge in einem Skript, zum Beispiel examples/part01_tokens.py, zusammen mit einer requirements.txt-Datei, die tiktoken auflistet. Das Skript gibt die Tokenaufteilungen aus, führt die Budgetberechnung durch und läuft den Testzyklus in etwa einer Sekunde ab – offline, sobald die Tokenisierungsdaten im Cache gespeichert sind. Ändern Sie anschließend das Korpus und beobachten Sie, wie sich die Ausgabe verändert.
pip install -r requirements.txt
python examples/part01_tokens.py
Wichtige Erkenntnisse
- Ein LLM ist eine „eingefrorene“ Funktion, die eine Tokenfolge in eine Verteilung der nächsten Token umwandelt und in einem Feedbackzyklus ausgeführt wird.
Verwandte Artikel
- Verständnis von KI-Agenten: Ziele, Werkzeuge, Speicher und der Agentenzyklus — Eine für Anfänger geeignete Erklärung, wie sich KI-Agenten von Chatbots unterscheiden, mit Informationen zu den Kernkomponenten, dem Entscheidungszyklus, den Autonomiegraden und Anwendungsbeispielen aus der Praxis.
- LangGraph vs Pydantic AI: Warum das Modell und nicht das Framework die Genauigkeit bestimmt — Ein kontrollierter Benchmark mit 160 Ausführungen von LangGraph und Pydantic AI zeigt eine identische Genauigkeit bei der Aufrufung der Tools und verdeutlicht, dass die Wahl des Modells sowie die Gestaltung der Bewertung weitaus wichtiger sind.
- Debugging eines kleinen GPT in PyTorch: Tests, die jeden Fehler isolieren — Ein schrittweiser Arbeitsablauf zum Debuggen eines GPT auf Zeichenebene in PyTorch, von Token-IDs und Zielverschiebungen bis hin zu Gradienten, NaN-Verlusten und Checkpoints.
- Token-Budgets gegen semantische Grenzen: RAG-Chunk-Teilungen richtig durchführen — Erfahren Sie, wie Tokenisierung und Chunking voneinander unterscheiden, wo jede Methode in einem RAG-Pipeline-Workflow ansetzt, und wie Dokumente nach Bedeutung geteilt werden können, während die Größe mit dem echten Tokenisierer gemessen wird. —