Skip to content

A real agent

Read · 1 of 2

The swap

This is the loop you wrote in module 4. Three things changed: anthropic.Anthropic() replaces the stand-in, the tools are passed as definitions (module 3) in tools=, and the stop check is != "tool_use", so it also stops cleanly if the model hits max_tokens.

Run it with python3 agent.py and watch a real model plan the calculation, call your tool, read the result and answer.

1import anthropic
2
3client = anthropic.Anthropic()
4
5TOOLS = [{
6 "name": "calculator",
7 "description": "Add or multiply two numbers. Use it for every calculation instead of doing maths yourself.",
8 "input_schema": {
9 "type": "object",
10 "properties": {
11 "op": {"type": "string", "enum": ["add", "mul"]},
12 "a": {"type": "number"},
13 "b": {"type": "number"},
14 },
15 "required": ["op", "a", "b"],
16 },
17}]
18
19def calculator(op, a, b):
20 return a * b if op == "mul" else a + b
21
22messages = [{"role": "user", "content": "What is 17 + 23 * 41? Use the calculator."}]
23for _ in range(10): # a turn limit, as in module 4
24 reply = client.messages.create(model="claude-sonnet-5-5", max_tokens=1000, tools=TOOLS, messages=messages)
25 messages.append({"role": "assistant", "content": reply.content})
26 if reply.stop_reason != "tool_use":
27 break
28 outputs = []
29 for block in reply.content:
30 if block.type == "tool_use":
31 try:
32 content = str(calculator(**block.input))
33 except Exception as e:
34 content = "ERROR " + str(e)
35 print("tool:", block.name, block.input, "->", content)
36 outputs.append({"type": "tool_result", "tool_use_id": block.id, "content": content})
37 messages.append({"role": "user", "content": outputs})
38print(reply.content[-1].text)
$python3 agent.py
Read this lesson as text

Your module 4 loop with a real model and a real tool definition.

The swap

This is the loop you wrote in module 4. Three things changed: anthropic.Anthropic() replaces the stand-in, the tools are passed as definitions (module 3) in tools=, and the stop check is != "tool_use", so it also stops cleanly if the model hits max_tokens.

Run it with python3 agent.py and watch a real model plan the calculation, call your tool, read the result and answer.

import anthropic

client = anthropic.Anthropic()

TOOLS = [{
    "name": "calculator",
    "description": "Add or multiply two numbers. Use it for every calculation instead of doing maths yourself.",
    "input_schema": {
        "type": "object",
        "properties": {
            "op": {"type": "string", "enum": ["add", "mul"]},
            "a": {"type": "number"},
            "b": {"type": "number"},
        },
        "required": ["op", "a", "b"],
    },
}]

def calculator(op, a, b):
    return a * b if op == "mul" else a + b

messages = [{"role": "user", "content": "What is 17 + 23 * 41? Use the calculator."}]
for _ in range(10):  # a turn limit, as in module 4
    reply = client.messages.create(model="claude-sonnet-5-5", max_tokens=1000, tools=TOOLS, messages=messages)
    messages.append({"role": "assistant", "content": reply.content})
    if reply.stop_reason != "tool_use":
        break
    outputs = []
    for block in reply.content:
        if block.type == "tool_use":
            try:
                content = str(calculator(**block.input))
            except Exception as e:
                content = "ERROR " + str(e)
            print("tool:", block.name, block.input, "->", content)
            outputs.append({"type": "tool_result", "tool_use_id": block.id, "content": content})
    messages.append({"role": "user", "content": outputs})
print(reply.content[-1].text)
python3 agent.py

Before you ship anything

Log every call: the messages, the tool calls and the token usage. When an agent does something odd, the log is how you find out why.

Run your evals (module 10) against the real model, several times each (pass@k): real models vary between runs in a way the stand-in never did.

Keep the guards: turn limits, budgets, input checks and approval gates (modules 4, 8 and 9). A real model will eventually do something you didn't expect.