Back to course overview
Module 6Building the conversational agent 15 min

The tool-use loop

Build the core agent loop: call Claude, run any tools it requests, feed results back, repeat until it answers.

An agent is a loop. You send the conversation to Claude with the available tools. If it wants a tool, it says so; you run the tool and send the result back; it may want another; eventually it produces a final answer. This is the manual tool-use loop, and it's about 30 lines of Python.

simplified from agent/agent.pypython
import anthropic
from tools import TOOLS, run_tool

client = anthropic.Anthropic()      # reads ANTHROPIC_API_KEY from the environment
MODEL = "claude-opus-4-8"

def answer(messages):
    while True:
        response = client.messages.create(
            model=MODEL,
            max_tokens=4096,
            thinking={"type": "adaptive"},
            system=SYSTEM_PROMPT,
            tools=TOOLS,
            messages=messages,
        )
        # Keep the full assistant turn (text + tool_use blocks) in history.
        messages.append({"role": "assistant", "content": response.content})

        if response.stop_reason != "tool_use":
            return "".join(b.text for b in response.content if b.type == "text")

        # Run every tool the model asked for; return all results together.
        results = []
        for block in response.content:
            if block.type == "tool_use":
                output = run_tool(block.name, block.input)
                results.append({
                    "type": "tool_result",
                    "tool_use_id": block.id,   # must match the tool_use block
                    "content": output,
                })
        messages.append({"role": "user", "content": results})

The shipped file wraps this same loop in a class: AnalyticsAgent.answer(user_message, history) returns a dict with the reply text, the updated messages history, and the tool_calls it made — and it caps the loop at max_turns=8 instead of while True, so a confused model can't spin forever. That class is what the eval suite (Module 7) and the web app (Module 8) import.

The three rules that make it work

  • Check `stop_reason`. "tool_use" means run tools and continue; "end_turn" means the model is done — return its text.
  • Append the full `response.content`. It contains the tool_use blocks (and thinking); the next request needs them for context.
  • Match `tool_use_id`. Each tool_result must carry the id of the tool_use it answers, so the model knows which result is which.
Adaptive thinking

thinking: {type: "adaptive"} lets the model reason before deciding which metrics to pull — useful for multi-step questions ("compare CAC and AOV by channel and tell me which channels are efficient"). The loop preserves those thinking blocks automatically because it appends the whole content.

Tip

A typical question triggers one or two tool calls: maybe list_metrics to check names, then query_metrics for the numbers. The loop handles any number of calls without you writing branch logic — that's the elegance of the pattern.