Build 0 of 7
By the end of this build you'll have a working setup on your own computer and a small program that talks to an AI model directly, with no chat app in between. Along the way you'll see three things most people who use AI every day never figure out.
Time: about 2–3 hours. Do it across two or three sittings; your progress saves. Cost: a few cents of the $5 API credit you'll buy in OP 60.
Pick your side once. Every command on this page has a tab for Mac or Windows, and every AI step has a tab for Claude or ChatGPT. Your choice sticks across the whole course.
The terminal is a window where you type instructions to your computer instead of clicking. It looks old-fashioned, and that's fine: every AI coding tool lives here, and once you've used it for a week it stops feeling strange.
Press Cmd + Space, type Terminal, press Enter. Then run these one at a time (paste, press Enter, wait):
mkdir -p ~/ai-course cd ~/ai-course pwd
Press the Windows key, type PowerShell, open Windows PowerShell (not "Admin"). Then run these one at a time:
mkdir $HOME\ai-course cd $HOME\ai-course pwd
What just happened: mkdir made a folder, cd moved you into it, and pwd printed where you are. You should see a path ending in ai-course. That folder is your workspace for the whole course.
Every new terminal window starts in your home folder, not where you left off. If a command later says "No such file or directory", run pwd. Nine times out of ten you're in the wrong folder, and cd ~/ai-course (Mac) or cd $HOME\ai-course (Windows) fixes it.
Python is the language your small programs will be written in. You won't need to write it from scratch; you need to be able to run it and read it.
python3 --version
On a Mac the command is python3, not python.
python --version
Then install the two libraries this build uses (run it in your ai-course folder):
cd ~/ai-course python3 -m pip install anthropic openai python-dotenv
cd $HOME\ai-course python -m pip install anthropic openai python-dotenv
Check: the version command prints a number like Python 3.14.x, and the install ends with "Successfully installed".
A coding agent is an AI that works inside your terminal: it can read files, create them, and run commands in your workspace, asking your permission as it goes. It's the difference between an AI that talks about your work and one that does it.
curl -fsSL https://claude.ai/install.sh | bash
irm https://claude.ai/install.ps1 | iex
Close the terminal, open a new one, then:
cd ~/ai-course claude --version claude
cd $HOME\ai-course claude --version claude
A browser window opens the first time. Sign in with your Claude account. Inside Claude Code, type /status to confirm it's using your subscription. Type /exit to leave.
curl -fsSL https://chatgpt.com/codex/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://chatgpt.com/codex/install.ps1 | iex"
Close the terminal, open a new one, then:
cd ~/ai-course codex
cd $HOME\ai-course codex
Choose Sign in with ChatGPT and use your ChatGPT account. Press Ctrl + C to leave.
The agent can only see the folder you start it in. Starting it from the wrong folder is the second most common mistake. cd into ai-course first, every time.
Every build after this one is customized to you, and it starts with this file. Your first real job for the agent: interview you, then write what it learned to a file in your workspace. This file becomes the core of the AI memory you build in Build 2.
Start your agent in ai-course and paste this:
Interview me so you can write a profile of me that future AI tools will use. Ask ONE question at a time and wait for my answer. About 10 questions, covering: - my job and what a normal day looks like - the tasks that eat most of my week - what I write most (emails, reports, posts...) and who reads it - the tools, apps and accounts I use every day - documents I keep going back to - repetitive work I'd hand off if I could - one thing outside work I'd love to have running on its own - how I like things written (tone, length, pet peeves) Use only what I actually tell you. Do not add or assume anything. When we're done, create the file me/profile.md with my answers under clear headings, then show me the file.
When it's done, open the file yourself and read every line:
open -e me/profile.md
notepad me\profile.md
Even when told not to, AI models fill gaps with things that sound right: a job duty you don't have, a tool you never mentioned, a "passion for innovation" you never claimed. That's not lying; it's how these models work (you'll see why in OP 90). Find anything you didn't say and delete it. This file will steer every build after this, so it has to be true.
Your chat subscription is for the apps. Your own programs talk to the model through the API, which is billed separately by usage and unlocked with an API key. Treat the key like a credit card number: anyone who has it can spend your money.
ai-course. Copy it now; it's shown only once and starts with sk-ant-.ai-course and copy it.Now store it in a file called .env in your workspace. Open it in a plain text editor:
cd ~/ai-course touch .env open -e .env
cd $HOME\ai-course notepad .env
If Notepad asks to create the file, say yes.
Type one line, paste your key after the =, save and close:
ANTHROPIC_API_KEY=paste-your-key-here
OPENAI_API_KEY=paste-your-key-here
Then create a .gitignore file, which tells Git (in a later build) never to upload your .env:
printf ".env\n" > .gitignore ls -a
Set-Content .gitignore ".env" Get-ChildItem -Force
Check: the list shows .env, .gitignore and your me folder. (Files starting with a dot are hidden in Finder and Explorer. That's normal.)
Here's the core idea of this whole course. You'll send the same request to the same model twice: once cold, once with your profile attached. Then you'll see what that context cost.
First, put a real piece of your writing in sample.txt: an email you sent, a product or program description, a post. A paragraph or two.
touch sample.txt open -e sample.txt
notepad sample.txt
Now have your agent create the program. Start it in ai-course and paste this whole block, including the code:
Create a file called first_call.py containing exactly this code. Don't change anything, then stop.
from pathlib import Path
from dotenv import load_dotenv
import anthropic
load_dotenv() # reads your key from .env
client = anthropic.Anthropic()
MODEL = "claude-haiku-4-5-20251001" # the cheapest current Claude model
PRICE_IN, PRICE_OUT = 1.00, 5.00 # dollars per million tokens
task = Path("sample.txt").read_text()
profile = Path("me/profile.md").read_text()
def ask(prompt, system=None):
args = dict(model=MODEL, max_tokens=800,
messages=[{"role": "user", "content": prompt}])
if system:
args["system"] = system
reply = client.messages.create(**args)
print("".join(b.text for b in reply.content if b.type == "text"))
used = reply.usage
cost = used.input_tokens / 1e6 * PRICE_IN + used.output_tokens / 1e6 * PRICE_OUT
print(f"\n[{used.input_tokens} tokens in, {used.output_tokens} out, about ${cost:.5f}]\n")
print("========== 1. NO CONTEXT ==========")
ask("Improve this:\n\n" + task)
print("========== 2. WITH YOUR PROFILE ==========")
ask("Improve this:\n\n" + task,
system="You are helping the person described below. Write for their audience, "
"in their voice.\n\n" + profile)
Create a file called first_call.py containing exactly this code. Don't change anything, then stop.
from pathlib import Path
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv() # reads your key from .env
client = OpenAI()
MODEL = "gpt-6-luna" # the cheapest current OpenAI model
PRICE_IN, PRICE_OUT = 0.10, 0.50 # dollars per million tokens
task = Path("sample.txt").read_text()
profile = Path("me/profile.md").read_text()
def ask(prompt, system=None):
args = dict(model=MODEL, input=prompt)
if system:
args["instructions"] = system
reply = client.responses.create(**args)
print(reply.output_text)
used = reply.usage
cost = used.input_tokens / 1e6 * PRICE_IN + used.output_tokens / 1e6 * PRICE_OUT
print(f"\n[{used.input_tokens} tokens in, {used.output_tokens} out, about ${cost:.5f}]\n")
print("========== 1. NO CONTEXT ==========")
ask("Improve this:\n\n" + task)
print("========== 2. WITH YOUR PROFILE ==========")
ask("Improve this:\n\n" + task,
system="You are helping the person described below. Write for their audience, "
"in their voice.\n\n" + profile)
Exit the agent and run it yourself:
python3 first_call.py
python first_call.py
Look at three things:
The model was identical both times. The only difference was what it could see. That bundle of information sent along with a request is called context, and the part that tells the model who it's working for and how to behave is the system prompt. Most "this AI is bad" moments are context problems.
Most people assume the AI remembers them. It took Kyle four separate surprises to believe it doesn't. You'll see it in one run.
Have your agent create memory_test.py:
Create a file called memory_test.py containing exactly this code. Don't change anything, then stop.
from dotenv import load_dotenv
import anthropic
load_dotenv()
client = anthropic.Anthropic()
MODEL = "claude-haiku-4-5-20251001"
def ask(messages):
reply = client.messages.create(model=MODEL, max_tokens=200, messages=messages)
return "".join(b.text for b in reply.content if b.type == "text")
tool = input("Name a tool or app you use at work: ")
first = {"role": "user", "content": f"The tool I rely on most is {tool}. Just reply OK."}
answer1 = ask([first])
print("\nCALL 1:", answer1)
question = {"role": "user", "content": "Which tool do I rely on most?"}
print("\nCALL 2 (a brand-new request):", ask([question]))
history = [first, {"role": "assistant", "content": answer1}, question]
print("\nCALL 3 (the same question, with the earlier messages sent along):", ask(history))
Create a file called memory_test.py containing exactly this code. Don't change anything, then stop.
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
MODEL = "gpt-6-luna"
def ask(messages):
return client.responses.create(model=MODEL, input=messages).output_text
tool = input("Name a tool or app you use at work: ")
first = {"role": "user", "content": f"The tool I rely on most is {tool}. Just reply OK."}
answer1 = ask([first])
print("\nCALL 1:", answer1)
question = {"role": "user", "content": "Which tool do I rely on most?"}
print("\nCALL 2 (a brand-new request):", ask([question]))
history = [first, {"role": "assistant", "content": answer1}, question]
print("\nCALL 3 (the same question, with the earlier messages sent along):", ask(history))
python3 memory_test.py
python memory_test.py
Call 2 has no idea. Call 3 knows, but only because your program sent the earlier messages along again. The model itself remembers nothing between requests; it's stateless. When a chat app "remembers" you, the app is quietly resending the conversation, or saved notes about you, every single time. That's also why long chats get slow, expensive, and eventually forgetful.
Kyle's assistant once searched the web for last night's baseball scores and came back confident and wrong, because it thought it was a month later than it was. Here's the root of that bug.
Have your agent create date_test.py:
Create a file called date_test.py containing exactly this code. Don't change anything, then stop.
from datetime import date
from dotenv import load_dotenv
import anthropic
load_dotenv()
client = anthropic.Anthropic()
MODEL = "claude-haiku-4-5-20251001"
def ask(prompt, system=None):
args = dict(model=MODEL, max_tokens=200, messages=[{"role": "user", "content": prompt}])
if system:
args["system"] = system
reply = client.messages.create(**args)
return "".join(b.text for b in reply.content if b.type == "text")
q = "What is today's date, and what's the biggest news story this week? Be brief."
print("WITHOUT THE DATE:\n", ask(q))
today = date.today().strftime("%A, %B %d, %Y")
print("\nWITH THE DATE IN THE SYSTEM PROMPT:\n", ask(q, system=f"Today's date is {today}."))
Create a file called date_test.py containing exactly this code. Don't change anything, then stop.
from datetime import date
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
MODEL = "gpt-6-luna"
def ask(prompt, system=None):
args = dict(model=MODEL, input=prompt)
if system:
args["instructions"] = system
return client.responses.create(**args).output_text
q = "What is today's date, and what's the biggest news story this week? Be brief."
print("WITHOUT THE DATE:\n", ask(q))
today = date.today().strftime("%A, %B %d, %Y")
print("\nWITH THE DATE IN THE SYSTEM PROMPT:\n", ask(q, system=f"Today's date is {today}."))
python3 date_test.py
python date_test.py
Without the date, the model either guesses (often months or years off) or says it can't know. With the date, it gets the day right, but watch the news part: it still can't know this week's news, and it may make some up anyway.
A model learned from text up to a certain point, its knowledge cutoff, and has no clock. It knows only what it was trained on plus what's in its context. Giving it the date fixes the date. It doesn't fix the news; for that it needs a tool like web search, which you'll wire up in a later build. And when it doesn't know, it can still produce something that sounds right. That's the same thing that put made-up details in your profile in OP 50.
If you can explain it, you own it. This sign-off needs real answers.
First, start your agent and paste:
Explain first_call.py to me line by line, in plain English. Then ask me three questions to check I understood it, one at a time, and tell me honestly where I'm wrong.
Then answer these in the box, in your own words:
The chat apps you've used are products built around models like the one you just called. The app adds things on top: a system prompt of its own, memory of past chats, your uploaded files, tools like web search. Today you called the model with none of that, and saw exactly what each missing piece costs you. The rest of this course is about adding those pieces back yourself, on purpose, so you know how every one of them works.
| Term | What it means |
|---|---|
| Terminal | A window for typing commands to your computer. |
| Working directory | The folder your terminal is "in" right now (pwd shows it). |
| Coding agent | An AI that can read, write and run things in your workspace, with your permission. |
| API | The way programs talk to a service directly, without a website or app in between. |
| API key | The secret that identifies you to an API and gets billed. |
| Context | Everything sent to the model with a request. The model knows nothing else. |
| System prompt | The part of the context that sets who the model is working for and how to behave. |
| Tokens | The word-pieces models read and write, and the unit you pay by. |
| Stateless | Remembers nothing between requests. Every model is. |
| Knowledge cutoff | The point where a model's training text ends. |
Stuck on a step, got an error, or curious why something works? Paste the error message if there is one (never your API key).