Personalize this lesson
Adapt explanations and teaching visuals to your background and preferred voice.
The next-token loop is simple: predict one token, append it, and repeat. Now ask how that loop grew from a research architecture into GPT (Generative Pre-trained Transformer) systems and the modern large language models (LLMs) behind assistants, coding tools, and retrieval systems.
The Transformer was introduced in 2017 for machine translation.[1] Later models specialized its pieces into reading-focused encoders and generation-focused decoders. One concrete thread runs throughout: a developer-assistant prompt moving through a decoder-only model, so each historical step answers a practical question.
Same loop, bigger system: Modern generative LLMs still use the next-token loop from the last chapter. What changed is how models are pretrained, adapted to instructions, routed through parameters, and served within a latency and memory budget.


One generation loop, many tasks
Before the history, start with why the decoder-only path became so useful for general assistants. A decoder-only model reads text left to right and predicts the next token. That sounds simple, but it turns out to be a remarkably flexible interface.
A software-team assistant faces three different problems:
- Classification. A review note says, "This docs page is stale." You need the label
Docs. - Extraction. A build log contains a run ID buried inside a stack trace.
- Generation. A teammate asks, "Why did the tests fail?" You need a concise answer grounded in the CI log.
An encoder-only model like BERT can be fine-tuned for the first task: read a review note, output a label. A decoder-only model can express all three tasks as "continue the text." A few-shot classification prompt looks like this:
1Classify the review note into Bug, Docs, or Security.
2
3Note: The auth test fails on main.
4Label: Bug
5
6Note: This docs page is stale.
7Label: Docs
8
9Note: The API key is printed in logs.
10Label: Security
11
12---
13
14Note: The README still mentions the old CLI flag.
15Label:The model reads the prompt from left to right, including the examples. Then it predicts the continuation at the open label slot. A sufficiently capable instruction-tuned model may produce Docs, but that outcome is something you evaluate, not assume.
This flexibility is why generation became a common product interface. One prompt format can express classification, extraction, summarization, translation, coding, and planning. That universality shaped the field.

The prompt framing itself is simple enough to inspect directly:
1def build_review_label_prompt(note: str) -> str:
2 examples = [
3 ("The auth test fails on main.", "Bug"),
4 ("This docs page is stale.", "Docs"),
5 ("The API key is printed in logs.", "Security"),
6 ]
7 header = "Classify the review note into Bug, Docs, or Security."
8 shots = "\n\n".join(
9 f"Note: {text}\nLabel: {label}" for text, label in examples
10 )
11 return f"{header}\n\n{shots}\n\n---\n\nNote: {note}\nLabel:"
12
13prompt = build_review_label_prompt("The README still mentions the old CLI flag.")
14print(prompt)
15print("expected_next_token=Docs")
16print("why=the prompt pattern ends with a label slot")1Classify the review note into Bug, Docs, or Security.
2
3Note: The auth test fails on main.
4Label: Bug
5
6Note: This docs page is stale.
7Label: Docs
8
9Note: The API key is printed in logs.
10Label: Security
11
12---
13
14Note: The README still mentions the old CLI flag.
15Label:
16expected_next_token=Docs
17why=the prompt pattern ends with a label slotWhy can a decoder-only model classify a review note even though it was trained to generate text?
Answer
The label is just the next token in a prompt pattern. If the prompt shows examples ending with labels such as Bug, Docs, and Security, the model can continue the pattern by generating the most likely label token for the new note.
The fork: reading and writing split apart
The original 2017 Transformer had two parts: an Encoder (to read the input) and a Decoder (to generate the output). Later models specialized these roles.
- The encoder-only path (BERT). Google released BERT in 2018.[2] It used an encoder stack. It conditioned on tokens on both sides of each position and could be fine-tuned for tasks such as classification and extraction. For our developer-assistant example, a BERT-style classifier can be trained to map a review note to a label such as
Docs. But BERT wasn't built to write long answers token by token. - The decoder-only path (GPT). OpenAI released GPT-1 in 2018.[3] It used a decoder-style Transformer stack with masked self-attention, without the original translation decoder's encoder input. It read a prefix left to right and learned to predict the next token. It could write because generation was baked into the training objective. For our developer-assistant example, GPT can draft a CI failure explanation, one token at a time.
Encoder-only models became common for language-understanding tasks. Decoder-only models became the common architecture for general assistants. One practical reason is that generation is a flexible interface. If a model can generate text, you can ask for translation, summarization, coding, planning, or classification in the same format: prompt in, text out.
Why the fork matters for products
The fork wasn't just academic. It shaped how products are built today.
| Product need | Encoder-style fit | Decoder-style fit |
|---|---|---|
| Classify a review note | Strong fit. Read full note, output label. | Works if framed as generation, but not the original strength. |
| Generate a reply | Poor fit without extra decoder machinery. | Strong fit. Predict one token at a time. |
| Search over documents | Strong fit when using a retrieval-specific encoder or reranker. | Useful when paired with generated answers. |
| Write code | Not the natural interface. | Strong fit because code is a token sequence. |
The main lesson isn't "BERT bad, GPT good." Encoder-style models are still useful for classification, extraction, reranking, and retrieval-specific embeddings.[4] GPT-style decoders became the natural assistant interface because a single text-generation loop can express many tasks.
What "decoder-only" means in plain English
A decoder-only model writes with a bookmark:
- It reads everything to the left of the bookmark.
- It writes the next token.
- The bookmark moves one token to the right.
- It repeats the same loop.
- It can't read future tokens because no future tokens exist yet.
That simple constraint is why the same architecture can write a French translation:
1Translate English to French:
2commit -> validation
3test -> essai
4branch ->and produce:
1brancheor write a CI explanation:
Teammate: Why did the tests fail?
Assistant: The auth suite failed because the auth fixture expired...
No separate model was trained for each of these exact prompts. The model treats the examples and the conversation as context, then continues the pattern.
The attention visibility rule is small enough to inspect. In a causal decoder, a position can attend to itself and earlier positions, never future positions. In a bidirectional encoder, every input position is visible while building a representation.
1import numpy as np
2
3tokens = ["<bos>", "tests", "failed", "<eos>"]
4causal = np.tril(np.ones((len(tokens), len(tokens)), dtype=int))
5bidirectional = np.ones_like(causal)
6
7print("tokens:", tokens)
8print("causal row for 'failed': ", causal[2].tolist())
9print("bidirectional row for 'failed':", bidirectional[2].tolist())
10print("future visible in causal row:", int(causal[2, 3]))1tokens: ['<bos>', 'tests', 'failed', '<eos>']
2causal row for 'failed': [1, 1, 1, 0]
3bidirectional row for 'failed': [1, 1, 1, 1]
4future visible in causal row: 0The 0 for the future end token is the constraint that makes autoregressive generation valid. Real attention learns nonuniform weights over visible positions; this matrix only shows which connections are allowed.
What makes a decoder-only model different from an encoder-only model in one sentence?
Answer
A decoder-only model reads left to right and generates the next token, while an encoder-only model reads the whole input at once and produces a representation for tasks like classification, retrieval, or extraction.
The life of one token
To understand why later innovations matter, follow one token through the full decoder-only pipeline using a developer-assistant question: "Why did tests fail?"
The sentence first goes through tokenization, which breaks it into token IDs (for example, a tokenizer might assign one token representing tests an ID such as 1847, while punctuation and spaces may be separate or bundled with nearby text). Those IDs become vectors through an embedding lookup table. Each token ID maps to a learned high-dimensional vector used by the model's layers.
Next, causal self-attention lets each position mix information from allowed positions at or before it. For the representation at "fail," that includes "Why," "did," and "tests." The model computes Query, Key, and Value projections and uses attention weights to build a context-enriched vector for the current position. After attention, the feed-forward network transforms that vector independently, applying nonlinear transformations that turn raw context into useful features.
Finally, after any stack-level final normalization, the refined vector for the last position is projected through a large output matrix to produce scores over the vocabulary. GPT-2, for example, applies a final ln_f before computing logits.[5] Softmax turns those scores into probabilities, and a decoding policy chooses a next token. During generation, the KV cache stores Key and Value tensors from prior tokens so the model doesn't recompute those projections at each new step.
The same loop runs for every single token the model ever produces. Once a service handles real traffic, even small gains in how attention is computed, how many parameters are active per token, how positions are encoded, or how the KV cache is managed translate into large wins on latency, cost, and the maximum context length you can afford to serve.

A small shape trace makes that interface concrete. This isn't a Transformer implementation: random arrays stand in for learned final normalized hidden states and output weights. It exposes the two production objects to remember: last-position logits and cached prior Key/Value tensors.
1import numpy as np
2
3rng = np.random.default_rng(3)
4batch, prompt_tokens, d_model, vocab_size = 1, 5, 8, 6
5final_normalized_hidden = rng.normal(size=(batch, prompt_tokens, d_model))
6output_projection = rng.normal(size=(d_model, vocab_size))
7last_logits = final_normalized_hidden[:, -1, :] @ output_projection
8shifted = last_logits - last_logits.max(axis=-1, keepdims=True)
9probabilities = np.exp(shifted) / np.exp(shifted).sum(axis=-1, keepdims=True)
10
11layers, kv_heads, head_dim = 4, 2, 4
12kv_cache_shape = (layers, 2, batch, kv_heads, prompt_tokens, head_dim)
13print("final normalized states:", final_normalized_hidden.shape)
14print("last-position logits:", last_logits.shape)
15print("next-token probabilities:", probabilities.shape, "sum=", round(float(probabilities.sum()), 3))
16print("cached K/V shape:", kv_cache_shape)1final normalized states: (1, 5, 8)
2last-position logits: (1, 6)
3next-token probabilities: (1, 6) sum= 1.0
4cached K/V shape: (4, 2, 1, 2, 5, 4)Why scaling became a design variable
In 2020, OpenAI published work on scaling laws for neural language models.[6] The core finding: loss improves predictably with three factors.
- Parameter count (how big the model is)
- Dataset size (how much text it reads)
- Compute (how many training FLOPs, floating-point operations, you spend)
Between GPT-1 and GPT-3, OpenAI released GPT-2 in 2019, a 1.5-billion parameter decoder-only model that demonstrated surprisingly coherent open-ended generation and drew public attention to both the power and risks of scaling up autoregressive language models.[7]
OpenAI then trained GPT-3, a 175-billion parameter decoder-only model.[8] It was more than 100 times larger than GPT-2.
GPT-3 demonstrated in-context learning at a surprising scale.[8] You didn't need to retrain the model for every new task. You could show a few examples in the prompt, and the model often inferred the pattern on the fly. This made prompt engineering a practical developer skill.
Scaling laws with concrete numbers
Scaling laws are often summarized as "bigger is better." That's too sloppy. The useful statement is narrower and more balanced.
Consider two hypothetical training runs:
| Model | Parameters | Training tokens | Compute (approx) |
|---|---|---|---|
| Small but balanced | 7 billion | 140 billion | ~1x |
| Large but starved | 175 billion | 300 billion | ~54x |
In this hypothetical table, the 175B model has 25x more parameters, but it was trained on only about 2x more tokens. The Chinchilla paper showed that many large models were undertrained for their size: a smaller model trained on proportionally more data can be far more efficient per parameter.[9]
This toy calculation makes the imbalance visible. parameters x tokens is only a training-compute proxy, not a quality predictor or a substitute for measured evaluation.
1runs = {
2 "small balanced": {"params_b": 7, "tokens_b": 140},
3 "large token-starved": {"params_b": 175, "tokens_b": 300},
4}
5baseline_proxy = runs["small balanced"]["params_b"] * runs["small balanced"]["tokens_b"]
6
7for name, run in runs.items():
8 tokens_per_parameter = run["tokens_b"] / run["params_b"]
9 compute_proxy = run["params_b"] * run["tokens_b"] / baseline_proxy
10 print(
11 f"{name:20s} tokens/parameter={tokens_per_parameter:5.1f} "
12 f"compute proxy={compute_proxy:5.1f}x"
13 )1small balanced tokens/parameter= 20.0 compute proxy= 1.0x
2large token-starved tokens/parameter= 1.7 compute proxy= 53.6xFor engineers, this changes how you read model releases. Check four questions:
- How many parameters does it have?
- How much data was it trained on?
- What was the compute budget?
- How was the data filtered and which workload improved?
A smaller model trained well can beat a larger model trained poorly. That's why modern model announcements talk about tokens, data quality, context windows, tool use, and inference efficiency, not parameter count alone.
From autocomplete to assistant
GPT-3 was capable, but it was still a document completer. If you prompted it with "Explain this test failure", it might continue with another prompt instead of answering directly, because the base objective was "continue the text."
To make models respond to instructions more reliably, InstructGPT used supervised demonstrations followed by human-ranked model outputs, a learned reward model, and reinforcement-learning fine-tuning.[10] The important distinction is behavioral: a base model learns continuation, while an instruction-tuned model is adapted to answer requests.
Base model, instruct model, and chat model
These words get mixed together, so separate them early.
| Model type | What it's trained to do | Example behavior |
|---|---|---|
| Base model | Continue text from pre-training distribution. | May complete a list, article, code file, or prompt transcript. |
| Instruct model | Follow written instructions and output useful answers. | Responds directly when asked to summarize or explain. |
| Chat model | Handle multi-turn role-based conversations. | Tracks user and assistant turns, follows system instructions, and applies safety behavior. |
A base model can be capable but awkward. An instruct model is easier to use. A chat model adds conversation structure and behavior tuning.
The same user request looks different through three lenses:
| Request | Base-model tendency | Chat-model tendency |
|---|---|---|
Explain this test failure. | Might continue with more prompts or examples. | Explains the failure. |
Explain attention. | Might produce documentation-like continuation. | Gives an answer adapted to the user. |
Return JSON only. | Might or might not follow format. | More likely to follow format, especially with structured output controls. |
This difference is why product teams usually start with an instruct or chat checkpoint instead of raw pre-training weights.
A common pairwise preference record has the same prompt paired with a chosen answer and a rejected answer. InstructGPT collected rankings of model outputs; pairwise records are a useful simplified view of the comparison signal.[10] The labels capture desired behavior; this code doesn't train a reward model.
1comparisons = [
2 {
3 "prompt": "CI run RUN-842 failed in auth_fixture_test.py. Explain the failure.",
4 "chosen": "The auth test failed because the auth fixture expired before assertion.",
5 "rejected": "The build failed for an unknown reason.",
6 "required_fact": "auth fixture",
7 },
8 {
9 "prompt": "Review note says the API key is printed in logs. Classify the issue.",
10 "chosen": "Security: redact the API key and rotate the exposed credential.",
11 "rejected": "Docs: update the README wording.",
12 "required_fact": "API key",
13 },
14]
15
16for row in comparisons:
17 keeps_required_fact = row["required_fact"] in row["chosen"]
18 print(f"prompt={row['prompt'][:22]}... chosen_keeps_fact={keeps_required_fact}")
19 print(" preferred:", row["chosen"])1prompt=CI run RUN-842 failed ... chosen_keeps_fact=True
2 preferred: The auth test failed because the auth fixture expired before assertion.
3prompt=Review note says the A... chosen_keeps_fact=True
4 preferred: Security: redact the API key and rotate the exposed credential.Why is a base model awkward as a developer assistant?
Answer
A base model was trained to continue text from its pre-training distribution. It might complete a transcript, list, or document instead of directly answering the user. Instruction tuning and chat tuning teach the model to treat user requests as tasks and produce useful assistant-style responses.
Open weights and local inference
Meta introduced a different distribution model.
In early 2023, Meta introduced LLaMA (Large Language Model Meta AI), a family of foundation models, and released its models to the research community.[11] That research-focused access gave approved users checkpoints they could evaluate and adapt instead of accessing a model only through a hosted interface. Downloadable weights and unrestricted commercial use are separate questions, which is why the license check below matters.
Researchers also didn't need 175 billion parameters for every useful workload. Chinchilla-style scaling showed that many models were undertrained for their size: smaller models trained on more tokens could be far more compute-efficient.[9] Independently, downloadable weights enable techniques such as fine-tuning (adapting a pre-trained model on task-specific examples) and quantization (using lower-precision weight representations to reduce memory cost), subject to model quality and license checks.
Open-weight doesn't mean open-source
This distinction matters in real engineering work.
| Term | Meaning | What to check |
|---|---|---|
| Open weights | The model checkpoint is downloadable. | License, commercial use, redistribution, acceptable use policy. |
| Open-source code | The training or inference code is available under an open-source license. | License, dependencies, reproducibility. |
| Open data | The training dataset is available or documented enough to inspect. | Data license, filtering, privacy, safety. |
Many model families are open-weight but not fully open-source in the strict software-license sense. That doesn't make them useless. It means you need to read the license before building a business around them.
Two current examples show why checkpoint access and deployment cost are separate questions:
| Open-weight checkpoint | Core model | Long-context design | Practical fit |
|---|---|---|---|
| GLM-5.2 | Z.AI labels it 744B total and about 40B active; Hugging Face counts about 753B in released checkpoint[12][13] | 1M-token text context, latent KV compression, sparse attention, IndexShare, and MTP[14][15] | MIT licensed and built for long-horizon coding and tools, but roughly 1.51 TB of raw BF16 weights before runtime overhead makes serving cluster-scale |
| DeepSeek V4 Flash 0731 | 284B core and 13B active; released artifact has an attached DSpark speculative module[16] | 1M-token total text context, CSA and HCA hybrid attention, and a 384K API output cap within that window[17][18] | MIT licensed and tuned for agentic coding and tool use; text-only and weaker than V4-Pro on knowledge-heavy and hardest agent tasks[16][19][20] |
Both models use MoE, so active parameter count helps explain per-token work but not checkpoint residency. Both also expose impressive vendor benchmark tables. Treat those as candidates for a matched internal eval, not proof that one model fits every workload.[13][21][16][20]
For a fuller deployment decision, use Open-Weight vs Closed API LLMs in 2026.
For a startup, open weights can provide:
- Local inference for privacy-sensitive data.
- Fine-tuning on domain examples.
- Quantization for cheaper serving.
- Debugging and evaluation without sending every token to a hosted API.
They also create work:
- You need GPU capacity.
- You own uptime.
- You own safety filters.
- You own model upgrade testing.
- You need a license review.
Modern efficiency and inference choices
Beyond raw scaling, model builders can change how much computation and memory each generated token requires. This matters to engineers because a serving budget depends on the active computation path and stored state, not a headline parameter total alone.
Mixture of Experts (MoE)
Mixture-of-Experts (MoE) models activate only part of the network for each token. Mixtral, for example, routes each token to a small subset of expert feed-forward networks.[22] The model gets more total capacity while keeping active compute lower than a dense model of the same total size.
A compiler pipeline gives a close analogy: a syntax error shouldn't run every optimization pass, and a type error doesn't need the final packaging step. Most passes stay idle for that file. In an MoE model, most expert parameters are inactive for any given token. That lowers active computation relative to a dense model with the same total size, though the full expert pool still consumes memory and routing adds systems tradeoffs.
A toy router shows the mechanism. Its labels are for explanation only: trained experts aren't guaranteed to align neatly with human topics.
1import numpy as np
2
3experts = np.array(["syntax", "types", "tests", "deploy"])
4tokens = ["import", "assertion"]
5router_logits = np.array([
6 [2.4, 0.3, 0.5, -0.2],
7 [0.1, 2.2, 0.7, 0.0],
8])
9
10for token, logits in zip(tokens, router_logits):
11 selected = np.argsort(logits)[-2:][::-1]
12 weights = np.exp(logits[selected] - logits[selected].max())
13 weights /= weights.sum()
14 routes = list(zip(experts[selected].tolist(), np.round(weights, 3).tolist()))
15 print(f"{token:7s} -> {routes}")1import -> [('syntax', 0.87), ('tests', 0.13)]
2assertion -> [('types', 0.818), ('tests', 0.182)]Reasoning models and test-time compute
Standard decoder LLMs generate one token at a time. Reasoning-focused models spend additional inference-time computation before producing a final answer. OpenAI's o1-preview described this approach in product form in 2024.[23] DeepSeek-R1 reported reasoning behavior developed through reinforcement-learning stages in 2025.[24] For applicable OpenAI reasoning API models, official documentation exposes model-dependent controls such as reasoning.effort (and, on some surfaces, reasoning.mode values like standard/pro) that trade speed and token use against additional reasoning work. Effort values, mode options, and whether the Responses API is preferred are model- and product-dependent; check current provider docs for the model you call.[25] Other model families require their own documentation and evaluation.
For our developer-assistant example, a standard model might immediately answer a complex test failure and blame the wrong file. A reasoning model can spend extra inference-time compute on intermediate reasoning before producing the final answer, which can help it compare stack frames, recent diffs, and failing assertions more carefully. Training methods for reasoning and preference tuning get a dedicated chapter later; here you only need the shape of the idea.
The engineering stack that makes it fast
Modern inference relies on several optimizations that sit below the architecture headlines:
- Grouped-query attention (GQA). Instead of every Query head keeping separate Key and Value heads, groups of Query heads share Key and Value heads. GQA aims to retain quality close to multi-head attention while reducing inference cost.[26]
- Rotary positional embeddings (RoPE). Rather than adding fixed position vectors to token embeddings, RoPE rotates pairs of Query and Key features by a position-dependent angle. This gives self-attention an explicit relative-position signal, though length extrapolation still needs careful training and evaluation.[27]
- FlashAttention. Standard attention reads and writes large intermediate matrices in GPU memory, which becomes expensive for long sequences. FlashAttention is an exact attention algorithm that tiles the computation to reduce memory traffic between high-bandwidth memory and on-chip SRAM.[28]
For GQA, the serving consequence can be calculated directly: the KV cache scales with the number of Key/Value heads.
1batch, layers, tokens, head_dim, bytes_per_value = 8, 32, 8192, 128, 2
2query_heads = 32
3
4for label, kv_heads in [("MHA", 32), ("GQA", 8), ("MQA", 1)]:
5 cached_values = batch * layers * tokens * kv_heads * head_dim * 2
6 cache_gib = cached_values * bytes_per_value / (1024 ** 3)
7 relative = kv_heads / query_heads
8 print(f"{label}: kv_heads={kv_heads:2d} cache={cache_gib:5.2f} GiB relative={relative:.3f}")1MHA: kv_heads=32 cache=32.00 GiB relative=1.000
2GQA: kv_heads= 8 cache= 8.00 GiB relative=0.250
3MQA: kv_heads= 1 cache= 1.00 GiB relative=0.031RoPE's rotation also exposes a useful invariant: shifting both token positions by the same amount preserves their relative-position interaction in this two-dimensional example.
1import numpy as np
2
3def rotate(vector: np.ndarray, position: int, theta: float = 0.4) -> np.ndarray:
4 angle = position * theta
5 matrix = np.array([
6 [np.cos(angle), -np.sin(angle)],
7 [np.sin(angle), np.cos(angle)],
8 ])
9 return matrix @ vector
10
11query = np.array([1.0, 0.2])
12key = np.array([0.3, 0.9])
13same_gap_early = rotate(query, 4) @ rotate(key, 2)
14same_gap_late = rotate(query, 14) @ rotate(key, 12)
15different_gap = rotate(query, 14) @ rotate(key, 2)
16print("same gap, early:", round(float(same_gap_early), 6))
17print("same gap, late: ", round(float(same_gap_late), 6))
18print("different gap: ", round(float(different_gap), 6))
19print("same gap equal:", bool(np.isclose(same_gap_early, same_gap_late)))1same gap, early: 0.936998
2same gap, late: 0.936998
3different gap: -0.794779
4same gap equal: TrueGQA, RoPE, and FlashAttention don't change the next-token objective. They change cache cost, position handling, or attention memory traffic, which must be evaluated in the actual serving setup.
How to read a modern model announcement
When a new model appears, avoid one-metric thinking. Read it like an engineer.
| Claim | Question to ask |
|---|---|
| "1T parameters" | Total parameters or active parameters per token? Dense or MoE? |
| "1M context" | What does latency and KV cache memory look like at that length? |
| "Better coding" | Which benchmark, which harness, and how many attempts? |
| "Reasoning model" | Does it use extra test-time compute, visible scratchpad, hidden reasoning, or tool calls? |
| "Open model" | Open weights, open license, open data, or open training recipe? |
| "Cheap API" | Input, output, cache-hit, cache-miss, batch, and long-context prices? |
This habit keeps you from overreacting to headlines.

Evaluate a released model on your workload
History is useful only if it improves a decision. A published benchmark is evidence about a harness and a date, not proof that a model meets your developer-assistant workload's quality, latency, privacy, or cost constraints.
Build an evaluation set of representative cases, such as failing CI logs that require exact error citation and code-review notes that require correct labels. Measure each candidate under the same prompt and tool setup. A scorecard can make tradeoffs explicit; its weights are product decisions, not universal truths.
1candidates = [
2 {"name": "hosted-fast", "quality": 0.86, "p95_ms": 250, "cost": 0.50, "license_ok": True},
3 {"name": "hosted-deep", "quality": 0.94, "p95_ms": 1200, "cost": 1.50, "license_ok": True},
4 {"name": "local-restricted", "quality": 0.90, "p95_ms": 500, "cost": 0.20, "license_ok": False},
5]
6
7def score(model: dict, latency_penalty: float, cost_penalty: float) -> float:
8 if not model["license_ok"]:
9 return float("-inf")
10 return 100 * model["quality"] - latency_penalty * model["p95_ms"] - cost_penalty * model["cost"]
11
12scenarios = {
13 "live code help": (0.03, 8.0),
14 "nightly audit": (0.003, 4.0),
15}
16for scenario, penalties in scenarios.items():
17 ranked = sorted(candidates, key=lambda model: score(model, *penalties), reverse=True)
18 winner = ranked[0]
19 print(f"{scenario:13s} winner={winner['name']} score={score(winner, *penalties):.2f}")1live code help winner=hosted-fast score=74.50
2nightly audit winner=hosted-deep score=84.40A teammate says "we should use whatever benchmark leader is trending this week." What is the engineer's response?
Answer
"Best" is workload- and benchmark-specific. Pick based on your task, held-out evaluations, latency, context needs, price, license, and operational constraints, not on one leaderboard screenshot.
Long context doesn't mean equal attention
A common beginner mistake is assuming that a large advertised context window guarantees equally reliable use of evidence at every position. It doesn't.
Research on how language models use long contexts revealed a recurring pattern called "Lost in the Middle." When a model is given a very long prompt, it often performs best on relevant information at the beginning and the end. Relevant evidence placed in the middle of a long input is more likely to be missed or misused.[29]
For a developer assistant, this has a direct practical implication. If you paste a long incident log into the prompt and bury the failing stack trace in the middle, the model might miss the decisive lines. Keep important instructions and the most relevant context near strong positions such as the beginning or the end of the prompt, or break long documents into chunks and retrieve only the relevant ones.
You can enforce a prompt assembly rule in code. This rule places the governing policy first and the retrieved case evidence next to the question. It doesn't prove the model will answer correctly; evaluate that on held-out cases.
1policy = "Rule: explain CI failures using cited log lines."
2older_notes = [
3 "History: dependency cache restored.",
4 "History: formatter completed.",
5 "History: unit tests started.",
6]
7case = "Evidence: RUN-842 failed at auth_fixture_test.py:41 with expired auth fixture."
8question = "Question: explain the failure and cite the evidence."
9
10prompt_lines = [policy, *older_notes, case, question]
11for position, line in enumerate(prompt_lines, start=1):
12 print(f"{position}: {line}")
13print("evidence_adjacent_to_question:", prompt_lines[-2] == case)11: Rule: explain CI failures using cited log lines.
22: History: dependency cache restored.
33: History: formatter completed.
44: History: unit tests started.
55: Evidence: RUN-842 failed at auth_fixture_test.py:41 with expired auth fixture.
66: Question: explain the failure and cite the evidence.
7evidence_adjacent_to_question: TrueThis behavior is one reason to use retrieval-augmented generation (RAG). Instead of dumping every document into the context window, you retrieve only the most relevant passages and place them strategically.
Match the architecture to the job
| Task | Good default | Why |
|---|---|---|
| Rank review notes by urgency | Encoder-only (BERT-style) | Reads the full text at once and outputs a score or label. |
| Explain a CI failure from logs | Decoder-only (GPT-style) | Generates text token by token in a left-to-right loop. |
| Find the most similar past incident from a database | Bi-encoder, often encoder-style | Encodes incidents separately so their embedding vectors can be indexed and compared with cosine similarity.[4] |
| Generate a release-note summary | Decoder-only (GPT-style) | Treats a structured report as a generated token sequence. |
If you chose decoder-only for the ranking task, generation is flexible, but an encoder can still be the better tool for embeddings and classification.
Common misconceptions
| Misconception | Symptom | Cause | Fix |
|---|---|---|---|
| "GPT means any LLM." | You call every assistant "GPT" in architecture discussions. | GPT is the most famous brand, but it's one decoder-only family. | Use "LLM" or "language model" for the general category. |
| "BERT is obsolete." | You ignore encoder models for retrieval and classification. | Decoder-only assistants get the headlines. | Encoder-style models are still useful for classification, extraction, and retrieval-specific embeddings. |
| "Bigger model wins by default." | You judge models only by parameter count. | Marketing emphasizes large numbers. | Check data quality, compute budget, tuning, and inference cost. A smaller, well-trained model often beats a larger, starved one. |
| "Open-weight means free to use however I want." | You deploy an open-weight model commercially without reading the license. | The word "open" sounds permissive. | The license controls what you can do. Read it before building a business around the model. |
| "Reasoning models are a different species." | You assume a model label proves an unrelated architecture. | Product names obscure the model and inference details. | Read documentation: many reasoning-focused generative models still decode autoregressively while spending extra inference-time computation. |
Mastery check
Key concepts
- The split between Encoder-only (BERT) and Decoder-only (GPT) models
- Scaling laws: why size, data, and compute must be balanced
- The shift from GPT-3 to in-context learning
- Open weights: LLaMA, license checks, local inference, and fine-tuning workflows
- Modern architectural shifts: MoE (Mixture of Experts) and reasoning-time computation
- The life of a token: tokenization, embedding, attention, and KV cache
- Context window behavior: lost in the middle and prompt placement
- Base, instruct, and chat model behavior
- How to read a model announcement without relying on hype
Evaluation rubric
- Foundational: Differentiates between the original Transformer (Encoder-Decoder) and the GPT architecture (Decoder-only)
- Foundational: Explains why decoder-only generation can express classification, extraction, translation, coding, and chat as next-token continuation
- Foundational: Explains the significance of OpenAI's Scaling Laws
- Intermediate: Describes how GPT-3 demonstrated that large models can perform zero-shot and few-shot tasks without fine-tuning
- Intermediate: Identifies how downloadable model weights change evaluation and deployment options, subject to license terms
- Intermediate: Separates base models, instruct models, and chat models by behavior and tuning objective
- Intermediate: Explains why open-weight doesn't automatically mean open-source or unrestricted commercial use
- Intermediate: Describes the lost-in-the-middle effect and why long context windows aren't uniformly useful
- Intermediate: Explains why "best model" is workload-specific and lists the evaluation questions that matter more than a headline score
Follow-up questions
Why did many general-purpose assistant models use decoder-only architectures instead of encoder-only architectures like BERT?
Answer
BERT (encoder-only) is useful for understanding text for tasks such as classification and extraction because it can condition on tokens on both sides. GPT-style decoder-only models are built for autoregressive generation. Generation became a flexible product interface: you can frame many NLP tasks as text generation, from translation to summarization to coding.
What is a Mixture of Experts (MoE) model?
Answer
Instead of one dense feed-forward block where the same parameters are active for every token, an MoE layer has multiple expert feed-forward networks and a router. For each token, the router selects a small subset of experts, often one or two. Mixtral, for example, routes each token through two of eight feed-forward experts and uses about 12.9B active parameters out of 46.7B total.[22]
What practical difference should a beginner remember between a base model and a chat model?
Answer
A base model is trained to continue text. A chat model has extra tuning and message formatting so it treats user turns as requests and assistant turns as helpful responses. Product teams usually start with chat or instruct checkpoints for developer assistants instead of raw pre-training checkpoints.
Common pitfalls
- Symptom: You estimate serving cost from total parameters alone. Cause: You didn't check whether a model routes tokens through experts. Fix: Record active parameters, KV-cache requirements, measured throughput, and latency.
- Symptom: You download weights and plan a deployment before license review. Cause: You treated open-weight as unrestricted open-source. Fix: Check license, acceptable-use terms, redistribution, and commercial-use requirements first.
- Symptom: Your developer assistant misses decisive log evidence in a long prompt. Cause: You assumed context capacity guaranteed uniform evidence use. Fix: Retrieve relevant passages, place them near the question, and evaluate positional robustness.