Lesson 1 of 7
LLM Fundamentals & Prompting
Master the levers behind every LLM call — tokens, context windows, temperature, system prompts, and few-shot examples — to steer output without touching a single weight.
① Watch
Speed
📖 Read this walkthrough — every command, and why
LLM Fundamentals & Prompting — how a model reads, bills, and generates
Mental model: a language model never sees your words. It sees tokens — the model's
unit of text, and the unit you're billed on. Every call the model reads your whole
input as tokens, predicts the next token one at a time, and streams them back until it
signals it's done. Four levers follow from that: tokens (unit + cost), the context
window (how many tokens fit), temperature (how the next token is picked), and the
stop reason (how it knows to stop).
The SDK shape (@anthropic-ai/sdk). Every call is one POST to /v1/messages:
// Node — npm install @anthropic-ai/sdk
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic(); // reads ANTHROPIC_API_KEY
const res = await client.messages.create({
model: "claude-opus-4-8",
max_tokens: 1024, // hard ceiling on OUTPUT tokens
system: "You name fantasy taverns.", // counted as input every call
messages: [{ role: "user", content: "Name one tavern." }],
});
# Python — pip install anthropic
from anthropic import Anthropic
client = Anthropic()
res = client.messages.create(
model="claude-opus-4-8",
max_tokens=1024,
system="You name fantasy taverns.",
messages=[{"role": "user", "content": "Name one tavern."}],
)
1 · Count tokens BEFORE you send (billed before you ever call create)
// countTokens takes the same shape as create; returns input_tokens
const c = await client.messages.countTokens({
model: "claude-opus-4-8",
messages: [{ role: "user", content: "..." }],
});
// c.input_tokens -> 21
Real run: countTokens returned input_tokens = 21. That is the exact number of
tokens the model would read for that prompt — the same unit you pay for.
(Python: client.messages.count_tokens(...).input_tokens — never tiktoken; that's
a different tokenizer and mis-counts Claude.)
2 · Meter a real call (usage tells you what you actually paid)
Real run of messages.create on a "what is a token" prompt:
usage.input_tokens = 30
usage.output_tokens = 41
stop_reason = end_turn
input_tokens (30) > the 21 above because this call carried a system prompt too.
The system prompt + any few-shot examples are re-sent and re-billed on EVERY
call — they are input tokens every single time, not a one-time setup cost.
output_tokens (41) is what the model generated. stop_reason = end_turn means the
model finished on its own (see step 5).
3 · The context window — the fixed token budget
input_tokens + output_tokens must fit inside the model's context window: a fixed
token budget for everything the model attends to at once — your prompt, the
conversation history, and the output it's generating. It is the real constraint
you design around. Fill it and something has to be dropped. This is why the
re-billing in step 2 matters: history and few-shot examples don't just cost money,
they consume the window on every turn.
#1 gotcha: the running cost/space isn't "your latest question" — it's your latest
question PLUS the system prompt PLUS all prior turns, re-counted each call.
4 · Temperature — the determinism lever
Reading that context, the model predicts the single most likely next token.
Temperature tunes how that pick is made: low = focused/repeatable, high = broader
sampling. Same prompt ("give me tavern names"), two temperatures:
temperature 0 -> ["The Crimson Kraken", "The Crimson Kraken"]
(asked twice, identical both times — focused, repeatable)
temperature 1 -> ["The Copper Dragon", "The Wandering Griffin", "The Rusty Griffin"]
(three runs, three different names — wider sampling, varies)
Rule of thumb: temperature 0 for extraction/classification/anything you want
stable; temperature ~1 for brainstorming/variety.
5 · One token at a time, streamed, until it stops
The model generates the answer one token at a time: it appends each predicted
token, feeds the whole thing back in, and predicts the next — repeating until it
emits an end-of-turn signal. That's stop_reason = end_turn (seen in step 2 —
the model decided it was done). Streaming shows those tokens as they're produced
instead of waiting for the whole response:
const stream = client.messages.stream({ model: "claude-opus-4-8",
max_tokens: 1024, messages: [...] });
for await (const e of stream)
if (e.type === "content_block_delta" && e.delta.type === "text_delta")
process.stdout.write(e.delta.text); // tokens appear live
Other stop_reasons you'll meet: max_tokens (hit your output ceiling — the answer
was cut off, raise max_tokens or stream), tool_use (the model is asking you to
run a tool), refusal (declined for safety).
What each part does
token the model's unit of text AND the billed unit (input + output)
countTokens price a prompt before sending — returned input_tokens = 21 here
usage what a real call actually metered — input=30, output=41
system prompt re-sent and re-billed as input tokens on every call
few-shot same — examples count as input tokens every call
context window fixed token budget for prompt + history + output combined
temperature 0 = focused/repeatable, 1 = wider sampling (see the real runs)
stop_reason end_turn = model finished; max_tokens = cut off at the cap
Notes
- temperature 0 is LOW-VARIANCE, not a byte-identical guarantee. It returned
"The Crimson Kraken" twice here, but "0" means greedy-ish sampling, not a
promise the bytes match across every run, model version, or machine. Treat it
as "as stable as it gets," not "deterministic."
- You pay for input AND output. output tokens are typically priced higher than
input — long generations cost more than long prompts of the same length.
- There is no server-side memory between calls. The API is stateless; "history"
exists only because you re-send it (and re-pay for it) each turn.
- max_tokens caps OUTPUT only. It does not limit how big your input can be — that
is the context window's job.
- Prompting, not temperature, is the primary steering tool. Some current models
remove temperature entirely; shape behavior with the system prompt and examples.
Verify
Re-run token counting on any prompt and confirm the unit:
const c = await client.messages.countTokens({
model: "claude-opus-4-8",
messages: [{ role: "user", content: "Name one tavern." }],
});
console.log(c.input_tokens); // the tokens you'll be billed for
Then make one real call and inspect what it actually metered:
const r = await client.messages.create({ model: "claude-opus-4-8",
max_tokens: 1024, system: "You name taverns.",
messages: [{ role: "user", content: "Name one tavern." }] });
console.log(r.usage.input_tokens, r.usage.output_tokens, r.stop_reason);
Add a system prompt or a few-shot example and re-check usage.input_tokens — it
goes UP, proving those tokens are re-billed every call. Run the same prompt twice
at temperature 0, then twice at temperature 1, and compare: temperature 0 repeats,
temperature 1 varies.