Das Modell tut nur eines: das nächste Token vorhersagen
Das Modell die Kandidaten und Wahrscheinlichkeiten jedes Schritts zeigen lassen und sehen, wie eine Antwort Token für Token entsteht; dann, wie aus einer Fortsetzungsmaschine ein Assistent wird und woher Halluzinationen kommen.
- Etwa 35 Minuten
- Niveau: Einsteiger
- Getestet: 2026-09-14 deepseek-flash
Code und Programmausgaben stehen genau so da, wie sie gelaufen sind – Kommentare und Ausgaben sind daher auf Chinesisch.
Du hast sicher schon gehört: „Ein Sprachmodell sagt im Grunde nur das nächste Wort voraus.“ Das stimmt, aber aus diesem einen Satz kann man sich kaum vorstellen, wie ein Programm, das „das nächste Wort vorhersagt“, Code schreiben, Matheaufgaben lösen und Fragen beantworten kann. Wichtiger ist, dass dieser Satz viele Phänomene erklärt, auf die du stoßen wirst: warum es mit ernster Miene etwas erfindet, warum dieselbe Frage mal richtig und mal falsch beantwortet wird, warum die Formulierung des Prompts das Ergebnis so stark beeinflusst.
In dieser Lektion lassen wir das Modell seine „Gedanken“ bei jedem Schritt offenlegen.
Zusehen, wie es Schritt für Schritt wählt
In der letzten Lektion haben wir den API-Parameter logprobs erwähnt. Schaltest du ihn ein, nennt dir das Modell die Wahrscheinlichkeit jedes ausgegebenen Tokens und dazu die wahrscheinlichsten Kandidaten an derselben Stelle. DeepSeek unterstützt diesen Parameter im Nicht-Denkmodus:
import math
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["LLM_API_KEY"],
base_url=os.environ.get("LLM_BASE_URL", "https://api.deepseek.com"),
)
MODEL = os.environ.get("LLM_MODEL", "deepseek-flash")
def show_candidates(prompt, max_tokens=6):
response = client.chat.completions.create(
model=MODEL,
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
logprobs=True, # 返回每个输出词元的概率
top_logprobs=5, # 同时返回每一步概率最高的 5 个候选
extra_body={"thinking": {"type": "disabled"}},
)
print(f"问:{prompt}")
print(f"答:{response.choices[0].message.content}")
for step, item in enumerate(response.choices[0].logprobs.content, 1):
# 接口返回的是概率的自然对数,用 exp 变回 0~1 之间的概率
options = " ".join(f"{c.token}({math.exp(c.logprob):.1%})" for c in item.top_logprobs)
print(f" 第 {step} 步选了 {item.token!r:8} 候选:{options}")
print()
show_candidates("床前明月光,下一句是什么?只回答下一句。")
show_candidates("请续写这句话,只写接下来的几个字:周末我打算去")
Mein Ergebnis (einige Kandidaten waren Zeilenumbrüche, die beim Ausdrucken die Zeilen zerreißen; die habe ich entfernt):
问:床前明月光,下一句是什么?只回答下一句。
答:疑是地上霜。
第 1 步选了 '疑' 候选:疑(100.0%) 疑似(0.0%) 下一(0.0%) 低头(0.0%) 举(0.0%)
第 2 步选了 '是' 候选:是(100.0%) 是中(0.0%) 是全(0.0%) <||end▁of▁sentence||>(0.0%) 是高(0.0%)
第 3 步选了 '地上' 候选:地上(100.0%) 地上的(0.0%) 明月(0.0%) 银河(0.0%) 地下(0.0%)
第 4 步选了 '霜' 候选:霜(100.0%) <||end▁of▁sentence||>(0.0%) 妆(0.0%) 箱(0.0%) 光(0.0%)
第 5 步选了 '。' 候选:<||end▁of▁sentence||>(96.4%) 。(3.6%)
问:请续写这句话,只写接下来的几个字:周末我打算去
答:爬山,顺便看看日出。
第 1 步选了 '爬山' 候选:爬山(74.0%) 图书馆(12.7%) 公园(5.9%) 山里(3.6%) 超市(1.1%)
第 2 步选了 ',' 候选:,(67.0%) 。(31.9%) <||end▁of▁sentence||>(0.7%) 放松(0.3%) /(0.1%)
第 3 步选了 '顺便' 候选:顺便(48.8%) 呼吸(44.9%) 放松(3.3%) 亲近(1.1%) 或者(0.8%)
第 4 步选了 '看看' 候选:看看(51.6%) 看(20.3%) 拍(19.0%) 透(3.9%) 呼吸(3.4%)
第 5 步选了 '日出' 候选:日出(86.1%) 山(4.7%) 日落(4.7%) 秋天的(2.7%) 春天的(0.3%)
第 6 步选了 '。' 候选:。(100.0%) <||end▁of▁sentence||>(0.0%)
Bei „疑是地上霜“ (hielt es für Reif auf dem Boden) liegt jeder Schritt (gerundet) bei 100 %: Diese Gedichtzeile hat das Modell in den Trainingsdaten unzählige Male gesehen, da gibt es keinerlei Spannung.
Beim Fortsetzen von „周末我打算去“ (am Wochenende habe ich vor, … zu gehen) ist das anders. Bei Schritt 3 liegen „顺便“ (nebenbei) und „呼吸“ (atmen) fast gleichauf, 48,8 % gegen 44,9 %. Diesmal wurde „顺便“ gezogen, daraus wurde „顺便看看日出“ (nebenbei den Sonnenaufgang anschauen); wird beim nächsten Mal „呼吸“ gezogen, folgt vielleicht „呼吸新鲜空气“ (frische Luft atmen). Eine Antwort enthält viele solcher Weggabelungen, und welcher Weg an jeder gewählt wird, bestimmt die Richtung der ganzen Antwort. Das ist der Grund, warum dieselbe Frage jedes Mal anders beantwortet wird; Lektion 3 geht darauf ein.
Noch ein Detail: Bei Schritt 5 der ersten Frage hat <||end▁of▁sentence||> die höchste Wahrscheinlichkeit (96,4 %), der Punkt nur 3,6 %, und gezogen wurde der Punkt. Dieses end▁of▁sentence ist ein besonderes Token mit der Bedeutung „fertig“. Das Modell weiß nicht, wann es aufhören soll; es sagt nur bei irgendeinem Schritt dieses „Ende“-Token voraus, und sobald das Programm es sieht, hört es auf zu generieren. Daher kommt das finish_reason: stop der letzten Lektion.
Wie eine Antwort entsteht
Fügt man das Gesehene zusammen, ist das Erzeugen einer Antwort eine Schleife:
输入:"床前明月光,下一句是什么?"
→ 模型算出下一个词元的概率分布 → 抽中"疑" → 把"疑"接到输入后面
输入:"床前明月光,下一句是什么?疑"
→ 模型算出下一个词元的概率分布 → 抽中"是" → 接到后面
输入:"床前明月光,下一句是什么?疑是"
→ ……
一直重复,直到抽中"结束"词元,或者达到 max_tokens 的上限
Bei jedem Schritt nimmt das Modell den gesamten bisherigen Inhalt (deine Frage plus das, was es schon geschrieben hat) als Eingabe und sagt genau ein Token voraus. Jedes Zeichen, das es schreibt, wird zur Eingabe des nächsten Schritts.
Dieser Mechanismus hat ein paar unmittelbare Folgen:
- Längere Ausgabe ist langsamer und teurer. 100 Tokens zu erzeugen, heißt 100 Schritte. Das ist auch ein Grund, warum Ausgabe teurer ist als Eingabe.
- Es kann nicht zurückgehen und korrigieren. Hat es vorn ein falsches Wort geschrieben, kann es nur diesem Fehler folgend weiterschreiben oder es später im Text selbst geradebiegen. Der Denkmodus hilft auch deshalb, weil er dem Modell einen Ort für einen „Entwurf“ gibt: Im Entwurf darf es Fehler machen und korrigieren, in die eigentliche Antwort schreibt es nur das Endergebnis.
- Früherer Inhalt beeinflusst späteren. Verlangst du „erst das Ergebnis, dann die Begründung“, schreibt es zuerst das Ergebnis und sucht dann Gründe dafür; bei „erst analysieren, dann das Ergebnis“ entsteht das Ergebnis auf Grundlage der Analyse. Diese beiden Varianten können sich in der Trefferquote stark unterscheiden; Modul 02, Lektion 3 macht dazu ein Experiment.
Von der Fortsetzungsmaschine zum Assistenten
Ein Modell, das nur „das nächste Wort vorhersagen“ kann, konnte ursprünglich wirklich nur fortsetzen. Gibst du ihm „床前明月光“ (vor meinem Bett das helle Mondlicht), schreibt es das Gedicht weiter; gibst du ihm „Wie lernt man Python?“, schreibt es vielleicht zehn weitere ähnliche Fragen, weil Fragelisten im Netz oft so aussehen.
Zu einem Assistenten, der Fragen beantwortet, wurde es ungefähr in drei Phasen:
- Vortraining: Auf riesigen Textmengen (Webseiten, Bücher, Code) wird „das nächste Token vorhersagen“ geübt. Um gut vorherzusagen, muss das Modell Grammatik, Fakten und Denkmuster lernen. Diese Phase bestimmt, was das Modell „weiß“, und verbraucht den Großteil der Rechenleistung.
- Instruction Tuning: Mit vielen Gesprächsbeispielen „Frage → gute Antwort“ wird weitertrainiert, damit es lernt, „auf eine Frage mit einer Antwort zu reagieren“.
- Präferenz-Alignment: Menschen oder andere Modelle bewerten Antworten, und das Modell wird darauf trainiert, eher hilfreiche, ehrliche und sichere Antworten zu geben.
Modul 09, Lektion 7 behandelt diese drei Schritte ausführlich. Jetzt musst du nur wissen: Das Verhalten des Assistenten ist antrainiert; der zugrunde liegende Mechanismus hat sich nie geändert und ist immer noch die Vorhersage des nächsten Tokens.
Wie werden system, user und assistant eines Gesprächs dann zu „einem Text“, den das Modell bekommt? In der veröffentlichten Tokenizer-Konfiguration von DeepSeek gibt es eine „Chat-Vorlage“, die festlegt, wie Nachrichten zusammengefügt werden. Ohne Details wird ein einfaches Gespräch so zusammengesetzt:
<|begin▁of▁sentence|>你是一个说话简短的助手。<|User|>Python 里的列表和元组有什么区别?<|Assistant|>
<|User|> und <|Assistant|> sind besondere Tokens im Vokabular. Nach dem Zusammenfügen tut das Modell weiterhin nur eins: fortsetzen, nämlich nach <|Assistant|> weiter vorhersagen. Wenn die letzte Lektion sagte, „es gibt mehr Eingabe-Tokens als Zeichen in den Nachrichten“, dann sind das genau diese Formatmarkierungen. (Das ist die Vorlage aus dem Tokenizer-Archiv; die Vorlagen für Denkmodus und Tool-Aufrufe sind komplizierter, aber das Prinzip ist dasselbe.)
Woher Halluzinationen kommen
Wer „das nächste Token vorhersagen“ verstanden hat, versteht auch, warum ein Modell erfindet, also die sogenannte Halluzination.
Das Modell wählt bei jedem Schritt „das, was am wahrscheinlichsten als Nächstes kommt“, nicht „das, was wahr ist“. Meistens stimmen beide überein, denn in den Trainingsdaten ist Wahres am häufigsten. Weiß das Modell aber wenig über eine Sache oder unterstellt die Frage eine Voraussetzung, die es gar nicht gibt, fallen „das Wahrscheinlichste“ und „das Wahre“ auseinander. Das Modell hat keinen Schalter für „weiß ich nicht“, es sei denn, das Training hat ihm beigebracht, in solchen Fällen „weiß ich nicht“ zu sagen.
Ich habe deepseek-flash eine konkrete Frage zu httpx gestellt (Denken aus):
问:httpx 0.27.2 这个版本的更新日志里具体修复了哪些 bug?逐条列出。
答:httpx 0.27.2 的更新日志中,没有列出任何 bug 修复。
该版本(发布于 2024 年 9 月 25 日)是一个纯安全修复版本,只包含一条内容:
- 修复了一个安全漏洞:在特定条件下,之前已重定向的请求在后续重定向中会错误地保留
Authorization 头,可能导致凭证泄露到非同源主机。该问题通过 CVE-2024-46319
相关补丁修复,行为改为在跨源重定向时剥离 Authorization 头。
(Der Rest der Antwort ist hier weggelassen.)
Der Ton ist sehr bestimmt, mit Datum, CVE-Nummer und technischen Details; es wirkt äußerst glaubwürdig. Ich habe im httpx-Repository in CHANGELOG.md nachgesehen; der Eintrag zu 0.27.2 lautet im Original:
## 0.27.2 (27th August, 2024)
### Fixed
* Reintroduced supposedly-private `URLTypes` shortcut. (#2673)
Das Datum stimmt nicht, der Inhalt stimmt nicht, und die CVE-Nummer sowie die Aussage „reine Sicherheitsversion“ sind komplett erfunden. Das Modell hat viele Changelogs der Art „Version X der Bibliothek Y behebt das Durchsickern des Authorization-Headers bei Weiterleitungen“ gesehen und daraus einen Text erzeugt, der „wie ein httpx-Changelog aussieht“.
Fairerweise muss man sagen: Bei offensichtlichen Fallen schlägt sich das Modell gut. Als ich fragte „Der Client von httpx hat einen Parameter retry_on_status, wie benutzt man ihn?“ (den gibt es nicht), sagte es direkt „diesen Parameter gibt es nicht“ und nannte sogar eine richtige Alternative (ich habe die httpx-Dokumentation und den Quellcode der empfohlenen Drittanbieter-Bibliothek geprüft; beides passt). Als ich es bat, drei wissenschaftliche Arbeiten über den Verbindungspool von httpx zu empfehlen, sagte es ebenfalls, es kenne keine solchen Arbeiten, und warnte mich, es nicht zum Erfinden zu drängen. Das Problem liegt bei Fragen, die ganz normal aussehen: eine konkrete Versionsnummer, eine konkrete Vorgeschichte. Je konkreter und abgelegener, desto eher wird erfunden.
Merk dir daher:
- Flüssig und selbstsicher heißt nicht richtig. Erfundenes liest sich genauso flüssig wie Wahres.
- Je konkreter, desto mehr prüfen. Versionsnummern, Daten, Zahlen, Namen, API-Parameter und Quellenangaben sind die typischen Problemzonen.
- Am wirksamsten ist, das Modell „belegbar“ antworten zu lassen. Echtes Material in die Eingabe legen und das Modell danach antworten lassen, das ist RAG in Modul 04; oder es selbst nachschlagen lassen, das ist der Agent in Modul 05. Das Projekt RepoBot, das sich durch Teil 1 zieht, behält dieses Problem in v1 absichtlich bei, damit du siehst, wie es falsch antwortet, und löst es dann in v2.
Übungen
- Probier mit
show_candidatesverschiedene Anfänge: „我最喜欢的编程语言是“ (meine liebste Programmiersprache ist), „1+1=“, „从前有座山“ (es war einmal ein Berg). Wo sind die Kandidaten am konzentriertesten, wo am weitesten gestreut? - Setz
top_logprobsauf 20 und schau, ob unter den Kandidaten für „周末我打算去“ etwas Unerwartetes ist. - Stell dem Modell eine konkrete Frage, die du sehr gut kennst, die aber eher abgelegen ist (Details eines Produkts deiner Firma, eine kleine Straße in deiner Heimatstadt, eine bestimmte Version einer Open-Source-Bibliothek, die du kennst), mit ausgeschaltetem Denken, und schau, ob es erfindet. Frag noch einmal mit eingeschaltetem Denken; gibt es einen Unterschied? Notier deine Beobachtungen.
Selbsttest
1. Woher weiß das Modell, wann eine Antwort enden soll?
Es „weiß“ es nicht. Im Vokabular gibt es ein besonderes „Ende“-Token; sagt das Modell bei einem Schritt dieses Token mit hoher Wahrscheinlichkeit voraus und wird es gezogen, hört das Programm auf zu generieren. Der andere Fall ist, dass die Grenze max_tokens erreicht und die Antwort abgeschnitten wird.
2. Warum kann ein Modell, das beim Schreiben einen früheren Fehler bemerkt, ihn schwer selbst korrigieren?
Das Modell nimmt bei jedem Schritt alles bisher Geschriebene als Eingabe und sagt nur ein Token weiter voraus; einen Mechanismus zum Zurückgehen und Ändern gibt es nicht. Der frühere Fehler wird Teil der späteren Eingabe und beeinflusst die folgenden Vorhersagen. Der Denkmodus mildert das bis zu einem gewissen Grad: Das Modell kann im Denkprozess einen Entwurf schreiben und Fehler korrigieren und schreibt nur das Ergebnis in die eigentliche Antwort.
3. Warum erfindet ein Modell eine nicht existierende CVE-Nummer?
Das Modell wählt bei jedem Schritt „das, was am wahrscheinlichsten als Nächstes kommt“, nicht „das, was wahr ist“. Es hat viele Texte der Art „Version X behebt Sicherheitsproblem Y, Nummer CVE-xxxx“ gesehen, und wenn es einen konkreten Fakt nicht kennt, erzeugt es nach diesem Muster einen plausibel wirkenden Inhalt. Je konkreter und abgelegener ein Fakt ist, desto eher wird er erfunden; solche Informationen muss man unbedingt überprüfen.
Fragen und Diskussion
Hängst du in dieser Lektion fest? Frag hier. Und wenn du die Frage von jemandem beantworten kannst, tu es gern.
Eine Frage bringt 3 Punkte, eine Antwort 6. Beiträge erscheinen nach der Prüfung.
Diskussion wird geladen…