AI Tutorial
Home All Courses Blog Interview Prep ↗ 0%
1
Curious builder 0 XP earned · 300 to level 2
0 days Finish a lesson to begin
Badge collection
0 of 5 unlocked
small wins to finish your path
Next lesson →

Next-token prediction

An LLM generates every answer one token at a time. At each step it scores every token in its vocabulary, turns those scores into probabilities, picks one token, appends it, and runs again. This lesson walks through that loop with runnable code, explains how the same objective is used in training, and shows how it accounts for both the power and the quirks of language models.

Last updated: 01 Nov, 2026

Definitions

Definition · Autoregressive generation

Producing a sequence one element at a time, where each new element is predicted from all the elements before it — including the ones the model just generated. LLMs are autoregressive: every output token becomes part of the input for the next step.

Definition · Logits

The raw, unnormalised scores the model outputs for every token in its vocabulary at a given step. Higher means “more likely next”. Logits can be any real number, positive or negative.

Definition · Softmax

A function that converts a list of logits into probabilities between 0 and 1 that sum to exactly 1: each score is exponentiated and divided by the sum of all exponentiated scores.

Definition · Decoding

The strategy for choosing the next token from the probability distribution — always the most likely one (greedy), or a random draw shaped by settings like temperature and top-p (sampling).

One step: from logits to probabilities

Suppose the prompt is “The capital of France is” and, for simplicity, the vocabulary has only five tokens. The model outputs one logit per token; softmax turns them into a probability distribution.

Python
import math

vocab = [" Paris", " Lyon", " a", " the", " beautiful"]
logits = [6.2, 2.1, 3.0, 2.4, 3.6]


def softmax(scores):
    m = max(scores)                       # subtract the max for numerical stability
    exps = [math.exp(s - m) for s in scores]
    total = sum(exps)
    return [e / total for e in exps]


probs = softmax(logits)
for token, p in sorted(zip(vocab, probs), key=lambda x: -x[1]):
    print(f"{token!r:<13} {p:6.1%}  {'█' * round(p * 40)}")
print("sum:", round(sum(probs), 6))
' Paris' 86.7% ███████████████████████████████████ ' beautiful' 6.4% ███ ' a' 3.5% █ ' the' 1.9% █ ' Lyon' 1.4% █ sum: 1.0

The model is fairly confident in " Paris", but it assigns real probability to alternatives like " beautiful" (as in “…is a beautiful city”). Which one appears depends on the decoding strategy, covered in depth in the Temperature and sampling lesson.

The generation loop

  1. Tokenize the prompt into ids.
  2. Run the model on all ids so far to get logits for the next position.
  3. Convert logits to probabilities and choose one token.
  4. Append the chosen token to the sequence (and stream it to the user).
  5. Stop if a stop condition is met; otherwise go back to step 2.

Here is that loop running on a toy model. The “model” is a lookup table of next-token probabilities, but the loop is structurally identical to what runs inside every LLM server.

Python
TOY_MODEL = {
    "<start>":   {"LLMs": 0.7, "Models": 0.3},
    "LLMs":      {"predict": 0.8, "generate": 0.2},
    "Models":    {"predict": 0.6, "learn": 0.4},
    "predict":   {"the": 0.6, "tokens": 0.4},
    "generate":  {"text": 1.0},
    "learn":     {"patterns": 1.0},
    "the":       {"next": 0.9, "answer": 0.1},
    "next":      {"token": 1.0},
    "token":     {".": 0.7, "again": 0.3},
    "tokens":    {".": 1.0},
    "text":      {".": 1.0},
    "patterns":  {".": 1.0},
    "answer":    {".": 1.0},
    "again":     {".": 1.0},
    ".":         {"<eos>": 1.0},
}


def generate_greedy(max_tokens=10):
    tokens = ["<start>"]
    for step in range(1, max_tokens + 1):
        dist = TOY_MODEL[tokens[-1]]
        next_token = max(dist, key=dist.get)      # greedy: most likely token
        print(f"step {step}: {dist} -> {next_token!r}")
        if next_token == "<eos>":
            return tokens[1:], "stop"
        tokens.append(next_token)
    return tokens[1:], "length"


output, finish_reason = generate_greedy()
print(" ".join(output), "| finish_reason:", finish_reason)
step 1: {'LLMs': 0.7, 'Models': 0.3} -> 'LLMs' step 2: {'predict': 0.8, 'generate': 0.2} -> 'predict' step 3: {'the': 0.6, 'tokens': 0.4} -> 'the' step 4: {'next': 0.9, 'answer': 0.1} -> 'next' step 5: {'token': 1.0} -> 'token' step 6: {'.': 0.7, 'again': 0.3} -> '.' step 7: {'<eos>': 1.0} -> '<eos>' LLMs predict the next token . | finish_reason: stop

A real model differs in three ways: its “lookup” is a neural network that considers the entire sequence, not just the last token; its vocabulary has ~200,000 entries; and it usually samples rather than always taking the top choice.

Stop conditions and finish_reason

Generation ends for one of a few reasons, which APIs report in a field such as finish_reason (OpenAI) or stop_reason (Anthropic):

ReasonWhat happenedWhat to do
stop / end_turnThe model produced its special end-of-sequence token — it decided the answer was complete.Normal; use the answer.
stop (stop sequence)The output contained one of your stop strings, e.g. "###".Normal; the stop string itself is not included.
length / max_tokensThe reply hit the max_tokens limit and was cut off.Raise the limit, ask for shorter output, or continue in a follow-up request. Never parse truncated JSON.
tool_calls / tool_useThe model wants to call a tool you defined.Run the tool and send the result back (covered in the Agents course).
content_filterOutput was blocked by a safety system.Handle gracefully; review the input.

How the same objective trains the model

Definition · Cross-entropy loss

The training signal for language models: the negative logarithm of the probability the model assigned to the actual next token in the training text. If the model gave the right token high probability, the loss is small; if it was surprised, the loss is large. Training adjusts the weights to reduce the average loss over trillions of tokens.

Python
import math

# probability the model assigned to the true next token " Paris"
for p in [0.9, 0.5, 0.1, 0.01]:
    print(f"p(correct) = {p:<5} -> loss = {-math.log(p):.2f}")
p(correct) = 0.9 -> loss = 0.11 p(correct) = 0.5 -> loss = 0.69 p(correct) = 0.1 -> loss = 2.30 p(correct) = 0.01 -> loss = 4.61

To predict the next token of a physics textbook, a legal contract, or a Python file well, the model must internally learn grammar, facts, conventions, and patterns of reasoning. That is why such a simple objective produces such broad abilities.

Prefill and decode: why output is slower than input

Definition · Prefill

The first phase of a request, where the model processes all input tokens in parallel to build its internal state. It is fast per token, and its duration determines the time to first token.

Definition · Decode

The second phase, where output tokens are generated one at a time, each requiring a pass through the model. It is inherently sequential, so output tokens are slower and usually priced several times higher than input tokens.

Definition · KV cache

A memory of the attention “keys” and “values” computed for earlier tokens, so each decode step only processes the newest token instead of re-reading the whole sequence. Providers also reuse it across requests that share a prefix — the basis of prompt caching discounts.

Practical consequence: shortening the answer speeds up a request far more than shortening the prompt by the same number of tokens.

What next-token prediction explains

Observed behaviourExplanation
Small wording changes alter the answerThe prompt is the conditioning text; different context shifts every probability.
Models rarely say “I don't know” unpromptedA confident-sounding continuation is often more probable than an admission of ignorance.
It cannot take back a mistake mid-answerEarlier tokens are fixed; the model can only continue from them.
“Think step by step” improves accuracyWriting intermediate steps gives later tokens more relevant context to condition on. Reasoning models are trained to do this internally.
Examples in the prompt steer format stronglyThe model continues patterns it sees — the basis of few-shot prompting.
Note

Prompt engineering works because you control the text the model continues from. Every instruction, example, and document you include changes the probability of every token it produces.

Try it
  1. Change the logits in the softmax example so " Lyon" becomes the most likely token. How large must its logit be?
  2. Modify generate_greedy to sample with random.choices(list(dist), weights=dist.values()) and run it five times with different seeds.
  3. Set max_tokens=3 and confirm the finish reason becomes length.
Key takeaways
  • LLMs generate autoregressively: score every token, choose one, append it, repeat.
  • Logits are raw scores; softmax turns them into probabilities that sum to 1.
  • Generation stops on an end token, a stop sequence, max_tokens, or a tool call — always check finish_reason.
  • Training minimises cross-entropy: the surprise at the true next token.
  • Prefill processes input in parallel; decode produces output one token at a time, so output is slower and pricier.