Vizuara Harness Engineering
Mentor Handbook · 02 Teaching the Loop

Teaching the first bare harness (live build)

By the end of this chapter you can stand at the front of the room and build a real coding agent live, from an empty file, in under two hours — a model client, a message array, and a loop — and have the whole cohort watching a program plan, act on a real machine, and decide again on its own. This is the morning where the workshop stops being slides and becomes an agent. You must own the build so completely that you can type it while talking, recover from a typo without panic, and know exactly which line makes the room gasp.

This chapter is a lecture plan, not a concept explainer. The concepts (the loop, the client, the message array) were taught the day before. Today you perform the build. So most of what follows is choreography: what to type, when to pause, what to let them try, and the one demo that lands the whole idea.

The one promise you make at 7:00 AM

Open by making a promise, out loud, and writing it on the board: "In the next two hours, from an empty file, we will build a program that reads your code, runs commands on your machine, and finishes a job you give it — with no human in the loop. About forty lines. No framework." Then say the honest part: "Everything after today is making this forty-line thing safe, durable, and scalable. But the beating heart is what we build this morning."

🎤 Say this at the board
"By nine o'clock this file will be an agent. Not a chatbot — an agent. The difference is one word: it acts. It doesn't just answer you, it reaches out and touches the machine, sees what happened, and decides what to do next. Watch me build the smallest possible version of that, and then you'll build your own before we break."
The morning's promise, drawn: an empty file grows hands and becomes something that acts on the world.figure rendering · The morning's promise, drawn: an empty file grows hands and becomes so
The morning's promise, drawn: an empty file grows hands and becomes something that acts on the world.

The shape of the two hours

Here is your timing spine for the 7:00–9:00 AM session. Keep it on a sticky note beside your keyboard. The rule of the morning is: you type, they watch, then they type. Never explain a piece for more than a few minutes before it becomes running code.

The two-hour ribbon: six blocks, with the loop as the starred centerpiece.figure rendering · The two-hour ribbon: six blocks, with the loop as the starred centerpi
The two-hour ribbon: six blocks, with the loop as the starred centerpiece.
  • 7:00–7:10 — The promise + the empty file. Set the stakes. Draw the three-piece diagram. Open one editor, one terminal, side by side, both projected big.
  • 7:10–7:30 — The model client. One function. Prove it talks.
  • 7:30–7:50 — Two tiny tools. A schema half and a function half.
  • 7:50–8:20 — The loop. The heart. Slowest, most careful block.
  • 8:20–8:40 — The demo that lands it. A multi-lap task, live, with prints showing every step.
  • 8:40–9:00 — They build + the honest gaps. Cohort types their own; you show what it's dangerously missing.

Block 1 (7:00–7:10) — Draw the three pieces before you type a thing

Before a single line of code, draw the map. If they hold this triangle in their heads, every line you type lands in a slot they already have.

A bare harness is exactly three things. A model client — one function that sends the conversation and gets back the model's next message. A message array — the running list of everything said so far, which literally is the agent's memory. And a loop that ties them together with a set of tools the model is allowed to call.

🧠 Metaphor
The agent is a short-order cook taking orders on a notepad. The message array is the notepad — every line ever said, in order, top to bottom. The model client is the cook's brain deciding the next move. The tools are the cook's hands: knife, stove, fridge. The loop is the cook's rhythm: read the notepad, do one thing, write down what happened, read the notepad again. The cook never forgets, because everything is on the notepad. And the cook stops only when the notepad says the dish is done.
Draw this first, before any code: the three pieces and the loop that spins them.figure rendering · Draw this first, before any code: the three pieces and the loop that s
Draw this first, before any code: the three pieces and the loop that spins them.
🎓 Teaching note
Draw the triangle slowly and leave it up on a side board for the whole session. Every time you finish a code block, walk back to the triangle and physically tap the piece you just built: "that was the client — one down." The cohort should watch three boxes fill in over two hours. This spatial anchor is what keeps a live build from feeling like a wall of code.

Block 2 (7:10–7:30) — The model client: prove it talks

Now the first code. Keep it brutally small. The whole "AI" of the agent is one function; everything else you write today is the harness around it.

import anthropic
client = anthropic.Anthropic()  # reads ANTHROPIC_API_KEY from the environment

def call_model(messages, tools):
    return client.messages.create(
        model="claude-sonnet-4-6",
        max_tokens=4096,
        system="You are a coding agent. Use tools to finish the job.",
        messages=messages,
        tools=tools,
    )

Type it live. Then — before anything else — prove it talks. This is the first dopamine hit of the morning and you must not skip it.

▶️ Live demo
Right after typing the client, run it with one throwaway line at the bottom: print(call_model([{"role":"user","content":"say hi in three words"}], [])). Run it in the terminal. When the reply object prints, point at the .content and say: "there — the model answered. That's the entire brain. Now we build the body around it." Then delete the throwaway line. The cohort has seen the API respond in ninety seconds; the mystery is gone.
The click
The line that reframes everything: "Notice messages and tools are inputs we control. The model only ever sees what our harness decides to hand it. That means the harness — not the model — is in charge of what the agent knows. Hold that thought; it's the seed of everything we build in the next four days." Say this while pointing at the two arguments.

Block 3 (7:30–7:50) — Two tiny tools: a contract and a function

A tool has two halves, and you teach them as two halves, side by side. The half the model sees is a contract: a name, a description, and a JSON schema of the arguments — that's how the model knows the tool exists and how to call it. The half we run is an ordinary function.

🧠 Metaphor
A tool is like a vending machine button. The label on the button (name + description + the slot it expects a coin in) is the contract — that's all the customer, the model, can see. Behind the panel is the machinery that actually drops the snack — that's our function. The model reads the label and presses; we make the snack come out. If the label lies about what the button does, the model presses the wrong one.
import subprocess, pathlib

TOOLS = [
    {"name": "read_file",
     "description": "Read a text file and return its contents.",
     "input_schema": {"type": "object",
        "properties": {"path": {"type": "string"}}, "required": ["path"]}},
    {"name": "run_bash",
     "description": "Run a shell command, return stdout+stderr.",
     "input_schema": {"type": "object",
        "properties": {"cmd": {"type": "string"}}, "required": ["cmd"]}},
]

def run_tool(name, args):
    if name == "read_file":
        return pathlib.Path(args["path"]).read_text()
    if name == "run_bash":
        r = subprocess.run(args["cmd"], shell=True, capture_output=True, text=True)
        return (r.stdout + r.stderr)[:4000]
    return f"unknown tool: {name}"
A tool has two halves: the vending-machine label the model reads, and the machinery behind the panel that we run.figure rendering · A tool has two halves: the vending-machine label the model reads, and
A tool has two halves: the vending-machine label the model reads, and the machinery behind the panel that we run.
⚠️ Where students trip
Someone will ask "where does the model learn what these tools do?" The answer, said clearly: "from the description and the schema — nothing else. The model never sees your Python function. It only reads the label. That's why a vague description is a broken tool." Plant this now; tomorrow's whole chapter is tool schemas as contracts.

Say plainly what you are leaving out: there is no permission gate before run_bash and no sandbox. You are omitting them on purpose so the loop is naked and obvious — and so that when the agent runs a command they didn't expect, they feel why Layer 2 exists.

Block 4 (7:50–8:20) — The loop: the heart, taught line by line

This is the block that matters. Slow down. It is fifteen lines and it is the whole of agency. Type it one line at a time, narrating each.

def run_agent(user_request):
    messages = [{"role": "user", "content": user_request}]
    while True:
        reply = call_model(messages, TOOLS)                    # 1. ask the model
        messages.append({"role": "assistant", "content": reply.content})

        if reply.stop_reason != "tool_use":                    # 2. no tool? done
            return text_of(reply)

        tool_results = []
        for block in reply.content:                            # 3. run each tool asked for
            if block.type == "tool_use":
                out = run_tool(block.name, block.input)
                tool_results.append({"type": "tool_result",
                    "tool_use_id": block.id, "content": str(out)})
        messages.append({"role": "user", "content": tool_results})  # 4. feed results back
        # 5. loop: the model now sees the results and decides the next move

Trace one lap out loud, walking your finger down the code. Put the request on the notepad, call the model. The model replies — maybe text, maybe a request to use a tool. Append the reply to the array (the agent remembering what it just decided). If it asked for no tool, it's done. If it did, run each tool, wrap the outputs as tool_result messages, append them to the array, and loop. Next lap, the model sees its own request and the results, and picks the next move.

One lap: call, check for a tool request, run it, append, repeat. It ends only when the model stops asking for tools.figure rendering · One lap: call, check for a tool request, run it, append, repeat. It en
One lap: call, check for a tool request, run it, append, repeat. It ends only when the model stops asking for tools.

Now spend your last few minutes here on the single most important line: if reply.stop_reason != "tool_use": return.

The click
Stare at the stop condition with them. "The loop keeps going as long as the model keeps asking for tools, and stops the instant it gives a plain answer instead. Read that again: the agent decides when it's finished. Not us. We wrote a while True with no counter — and yet it stops, because the model chooses to stop." Then the punchline: "That inversion — the program handing the when-are-we-done decision to the model — is exactly what makes this an agent and not a script. That is Layer 1. That is the whole workshop's foundation."

1 A real harness never ships a naked while True. It adds a max-turns guard and an interrupt path, because a confused model can loop forever politely re-reading the same file. We leave those out this morning so the mechanism is bare; they get their own treatment under stop conditions.

Block 5 (8:20–8:40) — The demo that lands it

Everything so far has been construction. This block is the payoff, and it is the moment they'll remember. Before you run it, add prints so every lap is visible — this turns an invisible API loop into a play they can watch.

▶️ Live demo
Add one line inside the loop after the model replies: print("LAP:", [b.type for b in reply.content]). Then run the agent with a task that needs three laps: run_agent("Read pyproject.toml, tell me the Python version, then list the test files."). The room watches it print ['tool_use'] (it reads the file), then ['tool_use'] again (it runs ls), then ['text'] (it answers) — three laps, no human between them. Say nothing while it runs. Let them watch the machine think.
The demo as a comic: three laps of the cook, each one a tool call, ending in a plain answer.figure rendering · The demo as a comic: three laps of the cook, each one a tool call, end
The demo as a comic: three laps of the cook, each one a tool call, ending in a plain answer.
⚠️ Where students trip
The most common confusion after the demo: "did the model run the command?" No. The model only ever asks — it emits a tool_use block. Our harness runs the command and hands back the result. Draw the line clearly: the model is a brain in a jar; it can only speak. Every real action in the world is our code responding to what it said. This distinction is load-bearing for the safety chapter tomorrow.

Block 6 (8:40–9:00) — Let them build, then show the gaps

Now they type. Give them the exact task and get out of the way.

🎓 Teaching note
Have the cohort start from an empty file and rebuild the client, one tool, and the loop from memory — screens off, or from the triangle diagram only, not by copying your code. Ten minutes, pairs allowed. Walk the room. The two errors you'll see most: forgetting to append the assistant reply before running tools (the model loses track of its own request), and forgetting to wrap tool outputs as tool_result with the matching tool_use_id (the API rejects it). Both are teachable-moment gold — let them hit the error, then fix it together on the projector.

Close by being honest about what this forty-line agent is missing — this is the trailhead for the rest of the week.

🏭 In production today
Say it straight: "This is not a toy. This is the identical skeleton inside Claude Code and pi — a client, a message array, a tool loop. The difference between what's on your screen and what ships to millions is not the heart. It's the survival gear around the heart." Then name the gaps, each mapping to a day ahead: it will run a dangerous command with no permission (Layer 2), it chokes when the conversation outgrows the context window (Layer 3), it loses all its work if the process dies (Layer 4), and it can't split a job too big for one context (Layer 5).
The honest ending: a bare agent that genuinely acts, surrounded by the four pieces of armor the rest of the workshop bolfigure rendering · The honest ending: a bare agent that genuinely acts, surrounded by the
The honest ending: a bare agent that genuinely acts, surrounded by the four pieces of armor the rest of the workshop bolts on.

Checkpoint questions (drop these between blocks)

  • After the client: "How much of the intelligence did we write?" (None — it's one API call. We write the harness.)
  • After the tools: "What does the model actually see about a tool?" (Only the name, description, and schema — never our function.)
  • After the loop: "Who decides when the agent is finished?" (The model, by not asking for a tool. Not us.)
  • After the demo: "Did the model run the ls?" (No — it asked; our harness ran it.)

You can now teach

  • The two-hour build plan block by block — the promise, the client, the tools, the loop, the demo, and the hands-on close — with concrete timings for a 7:00–9:00 AM session.
  • The model client as one function, and the ninety-second demo that proves the API talks before you build anything around it.
  • A tool as two halves — the contract the model reads and the function you run — using the vending-machine picture, and why a vague description is a broken tool.
  • The loop line by line, ending on the stop condition, so the cohort feels the inversion of control that turns a script into an agent.
  • The three-lap demo with visible prints — the moment the room watches a program plan, act, and finish on its own.
  • The honest gaps — permission, context, durability, orchestration — each mapped to the day of the workshop that fills it.