PHASE 01 · FOUNDATIONS
Goal: Make your first API call and develop intuition for how LLMs respond.
An LLM is just an HTTP API. You send a list of messages (a system message describing who the assistant is, plus user/assistant turns) and you get back a generated message. That's the whole thing.
The same model can produce wildly different outputs depending on the system prompt (its instructions), temperature (how random the sampling is), and max tokens (how long the reply can be). Before you build anything fancy, you need a gut feel for how these knobs change behavior.
You'll also learn that different models have very different tradeoffs. A small, cheap model (Claude Haiku, GPT-4o-mini) is fast but less capable. A frontier model (Claude Sonnet/Opus, GPT-4o) is smarter but slower and ~30× more expensive. Picking the right one is a real engineering decision.
.env file — never commit it.pip install anthropic or pip install openai.sys.argv, send it to the API, print the response.--model flag. Run the same prompt against a small model and a frontier model. Note: response quality, latency, tokens used, dollar cost.--system and --temperature flags. See how a sharp system prompt ("You are a terse senior engineer") completely changes the output.llm_chat.py you'll build on for the next 10 phases.llm_chat.py — A CLI that takes a prompt, optional model and system message, and returns a response.
Concepts unlocked: API keys · messages format · model selection · token counting
Forget the hype for a minute. You've already used a tiny language model today — your phone's keyboard. You type a few words and it guesses the next one:
That's the whole trick — just small. Your keyboard learned from the texts you've typed: a few years of messages, maybe a million words if you're chatty. Now scale that up — and "scale" here isn't an adjective, it's a number. Stack the three sizes next to each other:
To predict the next word well across all of that — legal contracts, Python code, love poems, physics forums — the guesser can't get by on habits like your keyboard does. It has to soak up grammar, facts, reasoning patterns, coding style, the shape of an argument. Not because anyone taught it those things — because they're the only way to keep winning the guessing game at that scale.
Push it far enough and something strange happens: "just guess the next word" becomes good enough to write an essay, fix a bug in your code, or explain quantum tunneling — because for each of those, producing the right next word, over and over, is the task.
The one-line intuition. An LLM is autocomplete that read the internet — a next-word guesser scaled up until the guessing turned into something that feels like intelligence. Not a database, not a search engine, not a mind.
1. It's a tool, not a being. It has no memory of you between requests, no goals, no awareness. Each time, it's text in, text out, in isolation — the "conversation" is an illusion you create by re-sending the history every time. That's why it can't "remember" yesterday unless you remind it.
2. It predicts what's likely, not what's true. It completes text the way the internet would, not the way reality is. Usually those line up; when they don't, you get a fluent, confident, wrong answer — a hallucination. Fixing that is the whole point of Phase 2.
Keep "autocomplete that read the internet" in your head. Everything else in this course — prompts, tools, memory, agents — is just clever ways to use that one trick.
We've got the gut feel — now let's be exact about what it is. An LLM is a math function: the f(x) kind from school, where you put something in and get something out, and the same input always gives the same output. Here's the whole thing, start to finish:
Read it left to right. Computers only do math, so your words are turned into numbers first. Those numbers run through the function — and here's the important part: the function is billions of numbers it learned by reading text, not rules a human wrote. Out come more numbers — a score for every possible next word — and the top one is turned back into text.
Notice it never "looks up" Paris in a database. It learned that across everything it read, Paris is overwhelmingly the most likely word after that phrase. To an LLM, knowledge is just very confident prediction — which is also why shaky knowledge comes out as a confident-sounding guess.
The whole thing. An LLM is a math function with billions of learned numbers inside. Words in → numbers → function → numbers → a word out. Everything in this course is built on that one pass.
That trained function has a name: a model. It's the word you'll see all over this course ("which model?", "Claude is a model") — and now you know exactly what it means: a big math function full of numbers learned from text.
One thing to file away: the function part is perfectly repeatable — the same words in give the same scores out, every time. Any randomness you see happens afterward, in a separate step that picks which of the high-scoring words to actually use.
But notice what this picture produces: exactly one word. A chatbot writes whole paragraphs. The gap between "one word out" and "an essay out" is closed by a single, almost embarrassingly simple trick — and that's the next section.
We just watched one prediction — one word out. To get a whole sentence, you do the obvious thing: append that word and ask again. Predict, append, predict, append — a loop, with the model reading its own growing text each time.
In picture form, the whole engine is three boxes — and one arrow that bends back on itself. That bent arrow is the entire idea: the output gets glued onto the input, and the model reads its own last word as part of the next question:
This is autoregressive generation — auto because it feeds on its own output, regressive because each step looks back at everything so far. The model never plans the whole sentence — it just keeps answering "what's next?" one piece at a time. It stops when it predicts a special end-of-text token (or you cap the length).
Word vs token. The loop actually runs on tokens, not words. A token is a chunk of text — often ~4 characters or a word-piece.catis one token;windowsillmight be two. When people talk about cost and context limits, they're counting tokens.
How does it choose among the likely words? Always grab the single most likely one and you get safe, predictable text; allow a little randomness and you get creativity. That dial is called temperature — we'll get to it soon.
We just saw the model only ever continues text. So here's a simple way to think about a prompt: it's just the text you hand the model to continue. You set up the start so the most likely continuation is the answer you want.
Type The capital of France is and the likeliest continuation is Paris. Type Write a haiku about autumn: and the likeliest continuation is a haiku. You're not commanding the model — you're steering its prediction by choosing the setup.
In practice a prompt has two layers. The system prompt sets persona and rules for the whole conversation ("You are a terse senior engineer"); the user message is the specific ask. Both are just text placed in front of the model's prediction. (If you're wondering whether the system prompt "sticks" between messages — good instinct; the next section shows exactly how it travels.)
What "prompting" really is. Prompting isn't magic words. It's arranging the text so your desired answer becomes the most likely continuation. That's the entire skill — and the rest of this course is increasingly clever ways to set up that text (with your data, with tools, with memory).
We keep saying "send the model some text" — but where does it actually go? For the best models, not your laptop. Two reasons: they're enormous (far bigger than a laptop can comfortably handle), and the companies that built them don't hand out the model file, so you can't download Claude or GPT. Instead you reach them through an API: you send your text over the internet and it sends the answer back. Same model — you're just talking to it across the web instead of running it yourself.
Wait — can't I run a model on my own computer?. Yes, actually — just not the giant ones. There are smaller open models (Llama, Mistral, Gemma) you can download and run right on your laptop with a tool like Ollama, fully offline. They're less capable than frontier models like Claude, but they genuinely run on your machine. "It only lives in the cloud" isn't a hard rule — we just use an API here so you're always working with a top-tier model.
The input is a list of messages and the output is the generated message. That's the entire interface you build on for the rest of the course.
Here's what that exchange looks like as real code — and this is the first code in the whole course, so let's be clear: you never have to write this yourself. Your coding assistant writes it for you in the Build section at the end of this phase. Don't parse the syntax; just notice the shape — a list of messages goes in, one message comes back out:
from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY from your environment
msg = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=512,
system="You are a terse senior engineer.",
messages=[{"role": "user", "content": "Explain a hash map in two sentences."}])
print(msg.content[0].text)
Three lines are worth spotting, because they're the three ideas you already know: model picks which model answers, system is the system prompt from last section, and messages is the conversation so far. Everything else — Anthropic(), .content[0].text — is plumbing that unpacks the envelope the reply arrives in. Plumbing is your assistant's job, not yours.
The messages format. Every turn is{"role": ..., "content": ...}with roleuserorassistant. The system prompt rides alongside as its own field. A multi-turn chat is just this list growing — you resend the whole history every time, because the model has no memory: each request starts fresh. The "memory" is you, re-sending the transcript.
So far you've sent the model some text and gotten a reply. But every request carries a few settings too — small dials that change how the model answers, even when your text is exactly the same. Three of them matter most. Here's what each one does.
0 = always the top token (repeatable, good for facts/code); higher = more varied (good for brainstorming).Temperature is the easiest one to see. It reshapes those next-word scores from earlier: a low temperature narrows them to one clear winner; a high temperature flattens them so more words are in play:
Default to low for anything that must be right. If you're extracting data, writing code, or answering factual questions, use a low temperature. Save the high settings for brainstorming. A "creative" temperature on a SQL generator just gives you creative bugs.
One choice rides on every request that's bigger than any of these three dials — and we've been quietly defaulting it this whole time. That's next.
That bigger choice is which model to use — the model= line in every request. They're not all the same, and no model is best at everything. Picture a triangle with a corner for each thing you care about: speed, quality, cost. Every model lives somewhere on that triangle, and moving toward one corner means moving away from another:
You can't have all three corners at once — a model that's the smartest and the fastest and the cheapest doesn't exist. So picking a model is really answering one question: which corner does this task actually need?
That's the whole game. For a simple, high-volume task, a small model is almost as good for a tiny fraction of the cost. For a genuinely hard reasoning task, the frontier model earns its price. The catch is knowing which task you've got — and you only really know by measuring.
You can't pick blind. "Smallest model that still passes" assumes you can measure passes. You can't eyeball that reliably — which is exactly why Phase 3 builds an eval harness. For now, just feel the difference: run the same prompt on a small and a frontier model and compare quality, latency, and cost.
Time to build the deliverable — but you won't type it. You'll describe it to your coding assistant and run what it produces: a tiny command-line tool that takes a prompt (plus optional model, system, and temperature) and prints the reply. Every later phase builds on this file.
Don't just run it — read it. Here's roughly what it'll produce, and what each part is doing:
# llm_chat.py — your first reusable LLM call
import sys, argparse
from anthropic import Anthropic
client = Anthropic() # ANTHROPIC_API_KEY from your .env
def chat(prompt, model="claude-sonnet-4-6", system=None, temperature=1.0):
msg = client.messages.create(
model=model,
max_tokens=1024,
temperature=temperature,
system=system or "You are a helpful assistant.",
messages=[{"role": "user", "content": prompt}])
return msg.content[0].text, msg.usage
if __name__ == "__main__":
ap = argparse.ArgumentParser()
ap.add_argument("prompt")
ap.add_argument("--model", default="claude-sonnet-4-6")
ap.add_argument("--system")
ap.add_argument("--temperature", type=float, default=1.0)
a = ap.parse_args()
text, usage = chat(a.prompt, a.model, a.system, a.temperature)
print(text)
print(f"\n[{usage.input_tokens} in / {usage.output_tokens} out]", file=sys.stderr)
Deliverable — llm_chat.py. A CLI that takes a prompt, optional model and system message, and returns a response. When you're done you can call any LLM from the terminal in under a minute, you've felt the speed/quality/cost triangle in real numbers, and you have the file Phase 2 plugs its retrieval into.