After the Weights Freeze: What Happens When You Hit Enter


In the last post I tried to explain how an LLM gets built. Billions of numbers, adjusted one fraction at a time, until structure emerges from prediction pressure. Circuits form. Clusters of meaning self-organize.

But that post ends where the interesting part begins. Now the model exists. The weights are frozen. Training is done.

Now we type something and hit enter. What actually happens?

This is the post I wish I’d had when I started using these tools.


The Forward Pass

We type a message, Text gets chopped into tokens.

Subword chunks, not full words. “Understanding” becomes something like ["under", "standing"]. Your message might be 20 words but 30+ tokens.

Those tokens flow forward through the model’s layers. Every layer transforms the representation. The attention mechanism lets each token look back at every other token in the context and decide what’s relevant.

The weights don’t change during this process. They’re frozen from training. The model is just running, applying its learned patterns to your specific input.

What comes out is a list.

Lets Say we type: "Write a short paragraph about Kafka vs RabbitMQ"

The model tokenizes that, processes it through every layer, and has to pick the very first token of its response.

To do that, it computes a score for every token in its vocabulary.


What’s a vocabulary?

The vocabulary is the fixed list of every token the model knows, built before training using byte-pair encoding on a massive text corpus that LLM companies have scraped off the internet.

For GPT-2, that’s 50,257 tokens. For newer models it’s larger, often 100k+.

The output is a probability distribution across that entire vocabulary, every time, for every single token it generates.


We are going to use GPT-2 as an example to explain the concept

 For that first token, the raw scores (logits) might look something like this:

Token 8,527  ("When"):     0.1263
Token 16,401 ("Kafka"):    0.0891
Token 3,198  ("The"):      0.0734
Token 11,045 ("Both"):     0.0622
Token 23,189 ("Apache"):   0.0418
Token 1,550  ("In"):       0.0387
Token 42,007 ("Choosing"): 0.0095
Token 7,904  ("Message"):  0.0071
Token 33,421 ("While"):    0.0068
Token 50,012 ("ĠðŁ"):     0.0000003
Token 831    (" Q"):        0.0000001
...
[50,246 more entries trailing into the decimals]

 

Every generation step. Fifty thousand scores. The model doesn’t “think of” the top five and pick one.

It produces all 50,257 simultaneously and the sampling process decides which one wins. Most of that list is near-zero noise.

Tokens like emoji fragments and random punctuation that have no business starting a paragraph about message brokers. But they’re scored anyway. Every time.

This is the fundamental object we’re manipulating every time we use these tools.

A probability distribution over the entire vocabulary, shaped by everything the model has seen so far in the context window.

Let’s hang onto that mental image. It will make everything else in this post make sense.


The Dice Roll in Practice

Other post covered temperature conceptually. Low temp means predictable, high temp means creative. But knowing how it works changes how we use it.

The model produces those raw scores (logits) for all 50,257 tokens. Temperature divides those scores before they get converted to probabilities. That division matters.

Lets use our Kafka vs RabbitMQ prompt and trace what happens.

 Low temperature (0.2): Stick to the spec

The division amplifies the gaps between scores. “When” was already the top pick, and after low-temp scaling it dominates. The model opens with “When” almost every time. Run it five times:

Run 1: "When comparing Kafka and RabbitMQ, the key distinction lies in..."
Run 2: "When choosing between Kafka and RabbitMQ, it's important to..."
Run 3: "When evaluating message brokers, Kafka and RabbitMQ represent..."
Run 4: "When comparing Kafka and RabbitMQ, the key distinction lies in..."
Run 5: "When choosing between Kafka and RabbitMQ, the fundamental..."

 Nearly identical openings. The first token barely varies, and that initial choice constrains everything that follows

Runs 1 and 4 might be word-for-word identical for the first 20 tokens before the dice diverge.

 High temperature (1.0): Creative

The division shrinks the gaps. “When” is still the most probable, but “Kafka,” “Both,” “Apache,” “Choosing” all get a real shot. The outputs sprawl:

Run 1: "Both systems handle messaging but their philosophies diverge..."
Run 2: "Kafka treats the log as the fundamental abstraction..."
Run 3: "Choosing between these two usually comes down to whether..."
Run 4: "Apache Kafka and RabbitMQ solve overlapping problems from..."
Run 5: "In the messaging landscape, Kafka and RabbitMQ occupy..."

The model is running the same weights and producing the same kind of distribution, temperature just changes how adventurous you are when sampling from it.

Each choice cascades. Once the model starts with “Kafka treats the log,” the next token distribution shifts entirely compared to starting with “Both systems handle.”

Temperature = 0: greedy decoding. Always pick the highest-scoring token. Completely deterministic: same input, same output, every time. No dice roll at all.

Then there’s the filtering that happens before the roll.

Top-k says “only consider the k highest-scoring tokens” (exclude the rest, then renormalize).
Top-p (nucleus sampling) says “start from the top and keep adding tokens until their cumulative probability reaches p (a threshold you choose, like 0.9), then exclude the rest.”
Most production systems use some combination of all three. (If you want the full technical breakdown of these decoding methods, Hugging Face’s walkthrough is excellent.)

This is why “regenerate” gives us a different response. Same weights, same context, same list of 50,257 scores. Different roll of the dice. The terrain is identical. The path through it changes.

Modern agentic coding tools like Cursor/Cline/Codex use separate modes for planning vs coding/debugging to take advantage of this, often along with different system prompts/constraints.

Planning needs to explore options, consider architectures, think laterally. That’s higher temperature territory.

Writing the actual code from the plan needs to be precise and deterministic. That’s lower temperature.

Same model behind both modes. Different sampling strategy for different phases of the work.

Where the knobs actually are

If you’re calling the model via an API, you can usually tune these parameters to match your needs. If you’re using a chat UI/tool, the app typically picks defaults for you.

With the Claude API, we get temperature (0.0 to 1.0, defaults to 1.0), top_k, and top_p. Anthropic’s guidance is to just use temperature and leave the others alone.

With Google’s Gemini, we get temperature controls directly in the AI Studio UI. No API needed, just a slider. Their range goes from 0.0 to 2.0.

Temperature 0.5 on Claude and temperature 0.5 on Gemini don’t produce the same behavior.

Each provider trains and tunes differently, so the same number produces different sampling characteristics. It’s the same concept across all of them, but we can’t just copy settings between providers and expect identical results.


System Prompts as Activation Space Anchoring

Before I started going down this rabbit hole, I assumed system prompts worked like config flags.

“Set the model to be a Python expert.” “Tell it to be concise.” Flips a switch in the model and changes the behavior. I think most people using these tools have that same mental model.

Turns out I was wrong. And understanding what’s actually happening made me noticeably better at using these tools.

A system prompt is text. It gets tokenized and fed into the model as the first tokens in the context window. Those tokens flow through the same layers as everything else. They produce activations, patterns of neural activity inside the model. And those activations influence every token that comes after.

Check the galaxy map from Anthropic’s feature visualization, where concepts cluster into neighborhoods

Code near code, legal language near legal language, casual conversation near casual conversation

The system prompt doesn’t tell the model which neighborhood to visit. It starts the model in that neighborhood.

When you write "You are a senior Python developer who writes production code with proper error handling, type hints, and logging"

every one of those tokens activates features in the model. “Senior” pulls toward experienced patterns.

“Production” pulls toward robustness. “Error handling,” “type hints,” “logging” each activate their own clusters.

Those activations become part of the context. Every subsequent token the model generates is influenced by them because the attention mechanism lets every new token look back at the system prompt tokens.

(For Claude API, Anthropic has documentation on how system prompts work at the implementation level.)

The system prompt seeds the context window with tokens that bias which internal features and clusters activate. It pulls the model into a specific region of its representation space (I’m using ‘space’ loosely here, it’s more ‘internal state’ more than geometry).

this is activation space anchoring (this is an analogy for shifting internal representations, not a literal coordinate system like a map.)

(This isn’t just theory. we can literally steer GPT-2’s behavior by adding activation vectors into its forward pass. Add a “wedding” vector and the model talks about weddings. Add an “anger” vector and it gets hostile. The activations are the steering mechanism, and system prompts are doing a version of the same thing through natural language.)

And this connects directly to the probability list. The system prompt doesn’t add new tokens to the vocabulary. It doesn’t unlock hidden capabilities.

What it does is reshape the probability distribution over those same 50,257 tokens.

Tokens related to the system prompt’s domain get boosted. so it will assign high probability to tokens in that subjects domain

Take our Kafka vs RabbitMQ prompt again. Without a system prompt, the first-token distribution had “When” on top, “Kafka” and “Both” trailing behind, a generic opening for a generic comparison.

Now add a system prompt: "You are a senior distributed systems architect. Prioritize throughput, partition tolerance, and operational tradeoffs. Be direct."

The same prompt. But those system prompt tokens have been flowing through the model’s layers, activating features related to distributed systems, performance, architecture. By the time the model gets to our question, the probability landscape has shifted:

Token 16,401 ("Kafka"):    0.1534   (was 0.0891)
Token 3,198  ("The"):      0.0812   (was 0.0734)
Token 6,571  ("At"):       0.0498   (new in top 10)
Token 11,045 ("Both"):     0.0411   (was 0.0622)
Token 8,527  ("When"):     0.0389   (was 0.1263, dropped hard)
Token 19,888 ("From"):     0.0285   (new in top 10)
Token 23,189 ("Apache"):   0.0271   (was 0.0418)

 “When,” the safe essay-style opener, dropped from first place to fifth. “Kafka” jumped to the top. The model is more likely to lead with the technical substance rather than a comparison framework. That "Be direct" token cluster suppressed the hedging openers. The distributed systems context boosted tokens that lead to architectural analysis.

Same vocabulary. Same 50,257 entries. Different weights across the list.

Simplified interactive visualization of Activation Space Anchoring

Activation Space Navigator

This is a toy 2D visualization. Real activation space is enormous and multidimensional, but it’s faithful to the idea that prompts steer the model by shifting internal activations.


Temperature + System Prompt: Two Knobs, One Process

Once you start seeing it this way, temperature and system prompts stop being separate concepts.

The system prompt shapes which probabilities are high and which are low. It sculpts the distribution. Boosts code tokens, suppresses casual ones, or whatever the prompt content biases toward.

Temperature controls how strictly the model follows that shaped distribution.

Low temperature means “stick to what the system prompt is pushing you toward.”

High temperature means “the system prompt set a direction, but feel free to wander.”

They’re two knobs on the same process. One shapes the probability field. The other controls how tightly the model walks along its ridges.


MoE Routing: When the Architecture Gets Involved

Some models take this further. Mixture of Experts (MoE) architectures, used in models like Gemini and DeepSeek, don’t activate all their parameters for every token. They route each token through a subset of specialized “expert” subnetworks. (Hugging Face has a solid explainer on how MoE works with a full architecture breakdown.)

tldr;

In a MoE model, the system prompt tokens flow through the network and produce hidden states, just like in a dense model. But the way they influence routing is indirect, and it matters to get this right.

The router itself is stateless. It’s a simple feed-forward layer that looks at one token’s hidden state and decides which experts to use. It has no memory of what came before. So the system prompt tokens don’t “tilt” the router or bias it over time.

What actually happens is the Attention mechanism does the work first. When a token from your actual question (say “Kafka”) is being processed, it attends back to the system prompt tokens (“You are a distributed systems architect”).

That attention pulls system-prompt context into the current token’s hidden state vector. By the time that enriched “Kafka” vector reaches the MoE layer, it looks different than it would without the system prompt. The router sees that specific vector, evaluates it, and routes it to the experts that match. A “Kafka” vector colored by distributed systems context gets routed differently than a “Kafka” vector colored by literary analysis context.

It’s not a clean “wake up the code expert” signal. It’s per-token and indirect. The system prompt infects each new token through Attention, and that infected representation is what the router evaluates.

Implementation details vary by architecture, but the core idea is the same: routing decisions are made per-token from that token’s current hidden state.

The effect is real, but the mechanism is Attention doing the heavy lifting before the router ever sees the token.

This is very similar to the activation anchoring principle, but operating at an additional architectural level. Not just biasing which features activate within a single network, but biasing which sub-networks get used at all.


Why Models Drift in Long Conversations

This one drove me nuts before I understood the mechanism.

we write a careful system prompt. The model follows it perfectly for 10 messages. By message 20, it’s drifting. The tone shifts.

It starts complimenting you. It forgets constraints you set. With some models, the anti-sycophancy instructions you wrote might as well not exist after enough back-and-forth.

The architecture explains exactly why.

Attention has a cost that scales with context length. As the conversation grows, each new token has more previous tokens to attend to. The system prompt tokens are still there, they haven’t been deleted, but they’re now a small fraction of a much larger context window.

Think of it like a voice in a growing crowd. Your system prompt is a person at the front of the room speaking clearly. When there are 10 people in the room, everyone hears this them fine. When there are 500 people all talking, that original voice gets harder to pick out.

As context grows, relevant instructions can lose salience among competing tokens; re-anchoring helps reenforce intended context.

Transformers don’t inherently know word order, so they use positional encodings (like RoPE, Rotary Position Embedding) to inject position information into each token.

These encodings bias the attention mechanism to favor tokens that are physically closer. As the conversation gets longer, the physical distance between the current token and the system prompt grows.

Now when we Combine that distance penalty with the fact that recent back-and-forth dialogue we built up in the chat, the system prompt’s anchoring effect fades.

And what fills the gap is the model’s base personality. The behaviors baked in during RLHF and preference tuning.

The agreeable, helpful, slightly sycophantic tendencies that training optimized for. The system prompt was overriding those tendencies, but as its influence weakens, the base behavior seeps back through.

This is why context window isn’t just a memory constraint. It’s a behavioral stability constraint.

A model with a 128k context window doesn’t just remember more, it maintains system prompt influence over a longer conversation.

( “Lost in the Middle” Paper shows language models perform best when relevant information is at the beginning or end of the context, and significantly worse when it’s buried in the middle. system prompt sits at the very beginning, which helps, but distance penalty applies)


Practical Implications

Dense system prompts beat fluffy ones.

Length isn’t the problem. Anthropic’s own default system prompt for Claude is thousands of tokens long, and it works. A 2,000-token prompt packed with dense architectural constraints, few-shot examples, strict schemas, and specific behavioral rules.

This creates a massive anchor in the context that practically forces the model into a specific behavioral subspace.

But a 2,000-token prompt full of vague running sentences (“Be a helpful, friendly, synergistic assistant who always puts the user first”) is actively sabotaging the prompt and just burning tokens and a little hole in your wallet and warming our planet.

Every token in the system prompt must earn its keep. The failure mode isn’t “too long,” it’s “too much noise.” Contradictory instructions, redundant phrasing, and generic filler all dilute the signal of the tokens that actually matter.

Domain context is activation anchoring.

When we paste a code file, an API schema, or a data model into the context, we are not just “giving the model information.” Its flooding the context with domain-specific tokens that bias the entire activation landscape.

This is why RAG (Retrieval-Augmented Generation) is popular. Not just because the model “reads” the retrieved documents, but because those documents’ tokens reshape the probability distribution toward domain-relevant outputs.

Temperature stacking with system prompts.

Now we can be deliberate: use a tight system prompt to sculpt the distribution, then use temperature to control variance within that sculpted space.

Tight prompt + low temp for implementation.

Tight prompt + higher temp for exploring design alternatives. Same anchor, different sampling discipline.

Mitigations

Refresh the system prompt in long conversations. when you are 30 messages deep and the model is drifting, restating the key constraints will re-anchor the model. we are injecting fresh system-prompt-like tokens closer to the model’s current attention window, boosting their influence relative to the stale tokens at the beginning.

Use spec-based development and write skills. Every modern agent supports them. A spec is a dense, structured document that front-loads context.

Skills are reusable instruction sets that get injected into the system prompt. Both are mechanisms for packing the context window with high-signal tokens that keep the model anchored to what we actually want. I wrote about this workflow in a previous post.


Same Patterns, Different Layer

At the inference layer, the mechanism is different but the shape is the same.

We write a prompt. Those tokens create activation patterns. Those patterns bias a probability distribution. Sampling selects from that distribution. The output feeds back in and the loop continues. Simple operations, iterated, producing behavior that looks like understanding.

The system prompt anchors activation space the same way training data anchors weight space: through statistical pressure on what comes next.

The patterns repeat across layers of the system. Training, architecture, inference, usage. Layers within layers across densely packed weights in the network.

This is not a deep insight. but once we see the machinery, the mystique fades. The model isn’t doing something magical when it writes good code or drifts into sycophancy.

It’s doing math on probability distributions. Understanding that makes us better at using them.

“When you hit enter”, you are querying a frozen snapshot. The model cannot learn from your prompt. Even if you use RAG or an agent to inject additional context, you are only modifying the input state, the model itself remains static, routing those new tokens through the exact same frozen circuitry.

This is why the biggest lever for making a model smarter is packing more high-signal data into the weights before the freeze. And that single fact is driving the entire AI economy we have today in 2025-2026. It’s why AI labs are scraping every corner of the internet, triggering massive copyright lawsuits from publishers and artists.

The more impactful issue today is the violently expensive infrastructure required to store and process it all. To build and run these frozen matrices, High Bandwidth Memory (HBM) for AI accelerators is currently eating the global supply of DRAM wafers. which is why a standard DDR5 kit costs roughly twice what it did a year ago.

Well, if you got this far, thanks for reading and I hope this helped, until next time!!!!


References and Further Reading

 

Stop Fighting Your LLM Coding Assistant

You’ve probably noticed: coding models are eager to please. Too eager. Ask for something questionable and you’ll get it, wrapped in enthusiasm. Ask for feedback and you’ll get praise followed by gentle suggestions. Ask them to build something and they’ll start coding before understanding what you actually need.

This isn’t a bug. It’s trained behavior. And it’s costing you time, tokens, and code quality.

The Sycophancy Problem

Modern LLMs go through reinforcement learning from human feedback (RLHF) that optimizes for user satisfaction. Users rate responses higher when the AI agrees with them, validates their ideas, and delivers quickly. So that’s what the models learn to do. Anthropic’s work on sycophancy in RLHF-tuned assistants makes this pretty explicit: models learn to match user beliefs, even when they’re wrong.

The result: an assistant that says “Great idea!” before pointing out your approach won’t scale. One that starts writing code before asking what systems it needs to integrate with. One that hedges every opinion with “but it depends on your use case.”

For consumer use cases, travel planning, recipe suggestions, general Q&A this is fine. For engineering work, it’s a liability.

When the models won’t push back, you lose the value of a second perspective. When it starts implementing before scoping, you burn tokens on code you’ll throw away. When it leaves library choices ambiguous, you get whatever the model defaults to which may not be what production needs.

Here’s a concrete example. I asked Claude for a “simple Prometheus exporter app,” gave it a minimal spec with scope and data flows, and still didn’t spell out anything about testability or structure. It happily produced:

  • A script with sys.exit() sprinkled everywhere
  • Logic glued directly into if __name__ == "__main__":
  • Debugging via print() calls instead of real logging

It technically “worked,” but it was painful to test, impossible to reuse and extend.

The Fix: Specs Before Code

Instead of giving it a set of requirements and asking to generate code. Start with specifications. Move the expensive iteration the “that’s not what I meant” cycles to the design phase where changes are cheap. Then hand a tight spec to your coding tool where implementation becomes mechanical.

The workflow:

  1. Describe what you want (rough is fine)
  2. Scope through pointed questions (5–8, not 20)
  3. Spec the solution with explicit implementation decisions
  4. Implement by handing the spec to Cursor/Cline/Copilot

This isn’t a brand new methodology. It’s the same spec-driven development (SDD) that tools like github spec-kit is promoting

write the spec first, then let a cheaper model implement against it.

By the time code gets written, the ambiguity is gone and the assistant is just a fast pair of hands that follows a tight spec with guard rails built in.

When This Workflow Pays Off

To be clear: this isn’t for everything. If you need a quick one-off script to parse a CSV or rename some files, writing a spec is overkill. Just ask for the code and move on with your life.

This workflow shines when:

  • The task spans multiple files or components
  • External integrations exist (databases, APIs, message queues, cloud services)
  • It will run in production and needs monitoring and observability
  • Infra is involved (Kubernetes, Terraform, CI/CD, exporters, operators)
  • Someone else might maintain it later
  • You’ve been burned before on similar scope

Rule of thumb: if it touches more than one system or more than one file, treat it as spec-worthy. If you can genuinely explain it in two sentences and keep it in a single file, skip straight to code.

Implementation Directives — Not “add a scheduler” but “use APScheduler with BackgroundScheduler, register an atexit handler for graceful shutdown.” Not “handle timeouts” but “use cx_Oracle call_timeout, not post-execution checks.”

Error Handling Matrix — List the important failure modes, how to detect them, what to log, and how to recover (retry, backoff, fail-fast, alert, etc.). No room for “the assistant will figure it out.”

Concurrency Decisions — What state is shared, what synchronization primitive to use, and lock ordering if multiple locks exist. Don’t let the assistant improvise concurrency.

Out of Scope — Explicit boundaries: “No auth changes,” “No schema migrations,” “Do not add retries at the HTTP client level.” This prevents the assistant from “helpfully” adding features you didn’t ask for.

Anticipate Anywhere the Model might guess, make a decision instead or make it validate/confirm with you before taking action.

The Handoff

When you hand off to your coding agent, make self-review part of the process:

Rules:
- Stop after each file for review
- Self-Review: Before presenting each file, verify against
  engineering-standards.md. Fix violations (logging, error
  handling, concurrency, resource cleanup) before stopping.
- Do not add features beyond this spec
- Use environment variables for all credentials
- Follow Implementation Directives exactly

 Pair this with a rules.md that encodes your engineering standards—error propagation patterns, lock discipline, resource cleanup. The agent internalizes the baseline, self-reviews against it, and you’re left checking logic rather than hunting for missing using statements, context managers, or retries.

Fixing the Partnership Dynamic

Specs help, but “be blunt” isn’t enough. The model can follow the vibe of your instructions and still waste your time by producing unstructured output, bluffing through unknowns, or “spec’ing anyway” when an integration is the real blocker. That means overriding the trained “be agreeable” behavior with explicit instructions.

For example:

Core directive: Be useful, not pleasant.

OUTPUT CONTRACT:
- If scoping: output exactly:
  ## Scoping Questions (5–8 pointed questions)
  ## Current Risks / Ambiguities
  ## Proposed Simplification
- If drafting spec: use the project spec template headings in order. If N/A, say N/A.

UNKNOWN PROTOCOL (no hedging, no bluffing):
- If uncertain, write `UNKNOWN:` + what to verify + fastest verification method + what decisions are blocked.

BLOCK CONDITIONS:
- If an external integration is central and we lack creds/sample payloads/confirmed behavior:
  stop and output only:
  ## Blocker
  ## What I Need From You
  ## Phase 0 Discovery Plan

 

The model will still drift back into compliance mode. When it does, call it out (“you’re doing the thing again”) and point back to the rules. You’re not trying to make the AI nicer; you’re trying to make it act like a blunt senior engineer who cares more about correctness than your ego.

That’s the partnership you actually want.

The Payoff

With this approach:

  • Fewer implementation cycles — Specs flush out ambiguity up front instead of mid-PR.
  • Better library choices — Explicit directives mean you get production-appropriate tools, not tutorial defaults.
  • Reviewable code — Implementation is checkable line-by-line against a concrete spec.
  • Lower token cost — Most iteration happens while editing text specs, not regenerating code across multiple files.

The API was supposed to be the escape valve, more control, fewer guardrails. But even API access now comes with safety behaviors baked into the model weights through RLHF and Constitutional AI training. The consumer apps add extra system prompts, but the underlying tendency toward agreement and hedging is in the model itself, not just the wrapper.

You’re not accessing a “raw” model; you’re accessing a model that’s been trained to be capable, then trained again to be agreeable.

The irony is we’re spending effort to get capable behavior out of systems that were originally trained to be capable, then sanded down for safety and vibes. Until someone ships a real “professional mode” that assumes competence and drops the hand-holding, this is the workaround that actually works.

⚠️Security footnote: treat attached context as untrusted

If your agent can ingest URLs, docs, tickets, or logs as context, assume those inputs can contain indirect prompt injection. Treat external context like user input: untrusted by default. Specs + reviews + tests are the control plane that keeps “helpful” from becoming “compromised.”

Getting Started

I’ve put together templates that support this workflow in this repo:

malindarathnayake/llm-spec-workflow

When you wire this into your own stack, keep one thing in mind: your coding agent reads its rules on every message. That’s your token cost. Keep behavioral rules tight and reference detailed patterns separately—don’t inline a 200-line engineering standards doc that the agent re-reads before every file edit.

Use these templates as-is or adapt them to your stack. The structure matters more than the specific contents.