Learning / Agentic Operations / Lesson 0005

Agentic Operations · Lesson 0005 · Level 1 · Foundations · Guided build

Build the Single-Tool Agent

The second half of the M1 gate: ship a reliable agent — and read what the model actually sees.

Your win for this lesson: a working calculator agent you built and ran, plus a per-turn context log you have personally read. Prerequisite: Lesson 0001. Companion reference: The Agent Loop & Tool-Call Round-Trip.

This is a build lesson, not a reading lesson. Work in a terminal at the repo root. A complete, self-tested reference implementation lives at learning/agentic-operations/exercises/m1_agent.py — type the stages yourself first and peek at it when stuck; the typing is where the learning is. (Its offline logic — calculator safety, context-log serialization — passed --selftest; the live API path needs your key, which is checkpoint 0.)

Checkpoint 0 · Bring your own key

This course runs on your own Anthropic API key — it stays on your machine, is never committed, and never reaches anyone else. Copy the template, then paste your key into the new .env:

cp .env.example .env      # then edit .env:  ANTHROPIC_API_KEY=sk-ant-...

Create a key at the Anthropic Console if you don't have one. .env is gitignored, so the key can't be committed by accident. Verify it's set without printing the secret:

uv run python -c "from dotenv import dotenv_values; print('ANTHROPIC_API_KEY' in dotenv_values('.env'))"
Pass: prints True. The whole hands-on runs on well under a dollar of your own credit — this agent's model is priced at $5 / $25 per million input / output tokens, and the calculator examples spend a few thousand tokens each.

Stage 1 · One call, no tools

Start a script that makes a single API call and prints the response blocks and token usage. Model: claude-opus-4-8. Note two things when it runs: the response is a list of typed content blocks, not a string; and the usage object prices the call in tokens — the unit everything in this field is denominated in.

response = client.messages.create(
    model="claude-opus-4-8", max_tokens=16000,
    system=SYSTEM, messages=[{"role": "user", "content": "What is 137 * 41?"}],
)
for block in response.content:
    if block.type == "text": print(block.text)
print(response.stop_reason, response.usage.input_tokens, response.usage.output_tokens)
Pass: you get an answer, a stop-reason of end_turn, and real token counts. The model probably did the arithmetic in its head — plausibly. That's the failure mode tools exist to close.

Stage 2 · Define the tool

Add the calculator tool definition (see the reference sheet for the exact schema) and a system prompt that says arithmetic must go through it. Remember from Lesson 0002: the description is a prompt — say when to call it ("for every arithmetic step; do not do mental math"), not just what it does.

Pass: the same question now returns stop_reason == "tool_use" and a content block carrying {"expression": "137 * 41"}. Stop and look at that block: it is text the model proposed. Nothing has been computed yet. Your code makes it real.

Stage 3 · Execute and loop

Implement the tool body and the loop: execute the proposal, append the assistant content verbatim, append a tool_result (id matched to the proposal), call again; repeat while the stop-reason is tool_use. Two non-negotiables from the reference sheet: the tool body treats the model's input as untrusted (the reference script uses an AST whitelist — never eval()), and the loop is bounded (10 iterations).

uv run learning/agentic-operations/exercises/m1_agent.py
# or your own: "What is (137 * 41) + 2**10 / 7? Work step by step."
Pass: the agent calls the calculator (possibly several times), then answers. Check it: (137 × 41) + 2¹⁰ ÷ 7 = 5617 + 146.29 ≈ 5763.29.

Stage 4 · Log the world — the M1 exercise

Before every API call, dump the complete context — system prompt, tool definitions, full message history — to context_log.jsonl. Then run once and read the log. This is the rubric's exercise #1 and the whole point:

Pass: you can point at turn 2's log line and narrate every piece of it — where it came from, and what would happen if any piece were missing.

Stage 5 · Make it reliable

Break it on purpose (gently — the real breaking is Lesson 0006): ask something that makes the model send a bad expression, or temporarily make the tool raise. The agent must return the error to the model as a tool_result with is_error: true — and the model should recover — rather than crashing.

Pass: a tool error produces a recovered answer or a clean explanation, never a stack trace.
What you just proved: an agent is an LLM in a bounded loop with a tool and a goal; the model only ever proposes text; your code executes, feeds back, and rebuilds the model's world every turn. You've now watched the M1 sentence be true.

Self-grade — the gate check

"Reliable single-tool agent" means all four: Bring the run output + a context-log excerpt to the readiness check. With this and your Lesson 0001 explanation, the M1 gate is checkable.

Next

Lesson 0006 — Break It On Purpose: overflow the window with junk and measure the degradation; rewrite one vague prompt three ways. Then the M1 readiness check.