model.complete(text) -> text

An LLM is a function from text to text. Everything else, you build.

Build the harness behind an AI coding agent, one problem at a time. Sixteen lessons. Real Python in your browser. No API key. A question before every answer.

Start lesson 1 See the syllabus

Free. No account, nothing to install. Lesson 1 takes about 35 minutes and ends with code you wrote.

Call 1 tells the model a name. Call 2, one line later, asks for it. What does call 2 answer?

model.complete(SYSTEM, [
    lab.user("My name is Ada.")])
reply = model.complete(SYSTEM, [
    lab.user("What is my name?")])
print(lab.show([reply]))
"Ada"
The belief behind this one: the model, or the API in front of it, carries the conversation from one call to the next.
"I don't know"
The belief behind this one: every call starts from nothing, and call 2 was handed one sentence.
An error
The belief behind this one: the API tracks a session, and complains when a question arrives without one.

Three beliefs about where a conversation lives. This page will not tell you which one holds; a program will. Lesson 1 runs these two calls as real Python in your browser, and a prediction locked in here is waiting there, beside the Run button. Then you build the part that remembers.

Start lesson 1: run it and see

Locking in also starts the Python download in the background (under 10 MB, once), so that Run is ready when you get there.

the path

Sixteen problems, in the order you would hit them. Each lesson starts where the code from the last one breaks.

  1. The model can only talk
    1. 01 The function that forgets
    2. 02 Words are not deeds
    3. 03 Turn the crank
  2. Things go wrong
    1. 04 Tell the model what went wrong
    2. 05 Knowing when to stop
    3. 06 The firehose
  3. Watching and steering a run
    1. 07 Show your work
    2. 08 Who holds the list?
    3. 09 But I had something to say
    4. 10 The poisoned transcript
  4. Memory that outlives the process
    1. 11 Pull the plug
    2. 12 The wall
  5. From a brain to a coding agent
    1. 13 The briefing
    2. 14 The veto
  6. Meeting the real world
    1. 15 The stop button that does not stop
    2. 16 Capstone: out of the browser

16 lessons in 6 parts · 4 checkpoints · 2 optional side quests · nothing is ever locked

what you will build

One file, harness.py, that grows from a chat that remembers into a coding agent you can stop, resume and trust. Every lesson adds one thing it can do.

  1. part a · 01 to 03

    The model can only talk

    You end with an agent: a model call in a while loop, over a transcript you hold.

    • 01 Hold a conversation: it knows what was said, and who said it.
    • 02 Run a tool the model asks for and show it the result.
    • 03 Keep going, call after call, until the model has nothing left to ask for.
  2. part b · 04 to 06

    Things go wrong

    A loop that survives bad tool calls, runaway models, provider failures and huge outputs.

    • 04 Turn a failed tool call into a message the model can act on.
    • 05 Stop a runaway model, and leave a transcript you can continue.
    • 06 Cut a huge output to a budget, and tell the model what was cut.
  3. part c · 07 to 10

    Watching and steering a run

    Events, a harness object, input in the middle of a run, and a transcript that cannot be bricked.

    • 07 Report every step as an event, to any frontend.
    • 08 Own the one transcript, and refuse a second writer.
    • 09 Take a message from you mid-run, at a point where it is safe.
    • 10 Repair an interrupted transcript for the model, without rewriting the record.
  4. part d · 11 to 12

    Memory that outlives the process

    An append-only log, resume by replay, and compaction as a rule for reading the log.

    • 11 Lose the process and pick up where it stopped.
    • 12 Hit the context limit and carry on, with the full history kept.
  5. part e · 13 to 14

    From a brain to a coding agent

    A system prompt that is computed, skills that load on demand, and a gate that fails closed.

    • 13 Brief the model from its own tools, project files and skills.
    • 14 Refuse a dangerous call, whatever a file tells the model to do.
  6. part f · 15 to 16

    Meeting the real world

    Async, cancellation, a real provider adapter, real tools, a real model.

    • 15 Stop when you press Stop, even inside a slow tool.
    • 16 Speak a real provider's streaming API, and run on your own machine.

what it comes down to

By the end of lesson 3 you have written this loop. The other thirteen lessons are what it takes to trust it.

The crank: one tool-using run of run_agent run_agent appends the prompt to the caller's list, messages, then repeats: send the whole list to model.complete, append the reply, and look for tool calls in it. If there are some, run each one, append its toolResult, and go again with the longer list. If there are none, return the reply. Here the run is user, assistant with toolCall c1, toolResult c1, assistant: two model calls. messagesthe caller's list 0 userWhat is in config.py? 1 assistantread(path="config.py")c1 2 toolResult · c1DEBUG = True 3 assistantIt sets DEBUG to True. model.complete()ScriptedModel run_tool()tools: read run_agentgo again append-only: it grows downward whole list, every call 1 message3 messages append reply reply tool_calls(reply)? none:return reply some:run each append result none: return reply tool_calls(reply)? whole list,every call 1 message3 messages appendreply appendresult

The crank Send the whole list to the model. Append the reply. If it asks for tools, run them, append each result, and go again with the longer list. If it asks for nothing, you are done. The model steers; the loop only turns.

how a lesson works

You are asked before you are told. Then you build the answer, and then you check it against a real harness.

1 Commit

Every idea starts as a problem and a question. Your answer is locked in, then the page answers. A wrong prediction is the method working; nobody keeps score.

2 Build it

You write the code, in an editor on the page. Hidden tests read as sentences, hints come one at a time, and the reference solution unlocks when you need it.

3 Compare

Then the same idea in a real coding-agent harness, cited by file and line, with the honest gap between your version and theirs.

Questions first
No lesson opens with a definition. It opens with something breaking, and the name for the fix arrives after you have built it.
Real Python in your browser
The labs run CPython 3.12, compiled to WebAssembly. The first Run downloads it, under 10 MB, once. No install, no terminal, no notebook server.
No API key
The model in the labs is a scripted stand-in whose rules are printed on the page. It fails on cue, which a real model will not do for you, and it costs nothing.
Nothing uploaded
There is no account and no server behind the labs. Your code, your answers and your progress stay in this browser, and you can export them to a file.

who it is for

For you, if

  • You write Python comfortably: functions, dicts, lists, classes, exceptions.
  • You have used a coding agent, or called a model API, and want to know what is actually inside one.
  • You would rather derive a design from the failure that forces it than be handed the design.

Not for you, if

  • You are learning Python from zero. The labs assume you can read a traceback.
  • You want prompt-writing tips, or a tour of an agent framework. There is no framework here: you write every line.
  • You want to know how models work inside. Here the model stays a function you call.
  • You need an agent running by tonight. Install one. Come back when you want to know why it is built that way.

What you need

A current browser, on a desktop or a phone; a keyboard makes the labs easier. No machine-learning background. Generators and asyncio are not assumed: lessons 7 and 15 open with a short warm-up you can skip.

How long it takes

35 to 70 minutes a lesson, about 13 to 15 hours for the core path. That is an estimate from the lesson structure, not yet a measurement. The capstone is two sessions in the browser, then work on your own machine; its last step is optional and uses a real model with your own key, which never touches this site.

Every lesson marks a good place to stop. Nothing is locked: if you already know a part, go straight to its lab.

the reference implementation

Every lesson compares what you built with how Tau solves the same problem.

Tau is an open-source coding-agent harness written in Python, under the MIT licence. Each comparison cites a file and line range pinned to one commit, and the build checks every citation against that commit, so an excerpt cannot drift from the source.

The comparison is honest in both directions: where your version is simpler, and where Tau does something the course does not. This site is an independent project. It is not affiliated with or endorsed by the Tau project or by any model provider. Licences and attribution.

next

lesson 01 · the function that forgets · 35 min

You call it twice. What does it remember?

You tell a model your name. One line later, you ask for it back. Predict, run it, then build the fix.

Start lesson 1 or read the whole syllabus first