Learning / Agentic Operations / Lesson 0007

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

Build the Single-Tool Agent

The second half of the Level 1 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. Prerequisites: Lesson 0001, 0005 and 0006. 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/level1_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 · Key check

Your Anthropic API key should already be set from Lesson 0005 — Tokens & Cost. Verify without printing the secret:

uv run python -c "from dotenv import dotenv_values; print('ANTHROPIC_API_KEY' in dotenv_values('.env'))"
Pass: prints True. If it doesn't, do Lesson 0005's Checkpoint 0 first. This build spends a few thousand tokens per run — pennies on the prices you learned there.

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 you learned to count and price in Lesson 0005.

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 — the same schema you dissected in Lesson 0006 (exact version also on the reference sheet) — 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/level1_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 Level 1 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 0008): 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 Level 1 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 Level 1 gate is checkable.

Next

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