Hidden Bag Vault · Learn

Build 0 of 7

Setup + First Call

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.

Your traveler

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.

OP 10

Before you start

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.

OP 20

Open a terminal and make your workspace

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.

The trap that cost Kyle an hour

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.

OP 30

Install Python

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.

  1. Go to python.org/downloads and download the latest macOS installer. Run it.
  2. When it finishes, a Finder window opens. Double-click Install Certificates.command (it lets Python make secure web connections).
  3. Close Terminal, open a new one, and check:
python3 --version

On a Mac the command is python3, not python.

  1. Go to python.org/downloads/windows and install the Python install manager (python.org's recommended way now; it's also in the Microsoft Store). If it offers to add Python to your PATH, say yes.
  2. Close PowerShell, open a new one, and check:
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".

OP 40

Install your coding agent

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.

Always start it from your workspace

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.

OP 50

Let the agent interview you

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
Planned failure #1: did it add things you never said?

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.

OP 60

Get an API key and keep it secret

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.

  1. Sign in at platform.claude.com (this is the developer console, a separate site from the Claude app).
  2. Go to Settings → Billing and buy $5 of credit. Credits are prepaid and expire after a year.
  3. Go to Settings → API keys, click Create key, name it ai-course. Copy it now; it's shown only once and starts with sk-ant-.
  1. Sign in at platform.openai.com (the developer platform, separate from ChatGPT).
  2. Add a payment method and buy $5 of credit (the minimum). Credits expire after a year.
  3. Go to API keys, create a key named 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.)

The rules (Kyle broke every one of these)
  • Never paste your key into a chat, including your coding agent. Never put it in a screenshot or inside code.
  • If your agent ever asks whether to use an API key it found, say no. Otherwise it bills the API instead of your subscription.
  • If a key leaks, delete it in the console and make a new one. It takes a minute.
OP 70

Your first call: same model, two results

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:

  1. Version 1 reads like generic AI writing. Version 2 should sound more like someone who knows your job and your audience.
  2. Version 2 used more input tokens. Your profile rode along with the request, and you paid for it. That's the trade at the center of every AI system: more context, better answers, higher cost.
  3. The cost: fractions of a cent. You can run this hundreds of times on $5.
What you just learned

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.

OP 80

The memory test

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
What you just learned

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.

OP 90

Planned failure: what day is it?

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
What to look for

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.

What you just learned

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.

OP 100

Explain it back

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:

  1. Same model both times in OP 70. Why did version 2 come out better, and what did that cost?
  2. Why didn't the model know your tool in call 2, and what did call 3 do differently?
  3. Why did it get the date or the news wrong, and what does that tell you about asking an AI anything current?
LANDSCAPE

What you just stepped behind

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.

TERMS

Words you can use now

TermWhat it means
TerminalA window for typing commands to your computer.
Working directoryThe folder your terminal is "in" right now (pwd shows it).
Coding agentAn AI that can read, write and run things in your workspace, with your permission.
APIThe way programs talk to a service directly, without a website or app in between.
API keyThe secret that identifies you to an API and gets billed.
ContextEverything sent to the model with a request. The model knows nothing else.
System promptThe part of the context that sets who the model is working for and how to behave.
TokensThe word-pieces models read and write, and the unit you pay by.
StatelessRemembers nothing between requests. Every model is.
Knowledge cutoffThe point where a model's training text ends.
RATE

How was this build?

ASK

Ask Kyle

Stuck on a step, got an error, or curious why something works? Paste the error message if there is one (never your API key).