Tutorials

How to build an AI agent with Claude in Python

Build an AI agent with Claude in Python: two tools and an agent loop on the official Anthropic SDK, with a SayGM key that bills Claude typically below list.

6 min read · Python · Verified on Sep 26, 2026

The project

You want Claude to do more than chat: look something up, work something out, then answer. That is an agent, a model that can call your functions and decide what to do next.

In this tutorial we build one from scratch with the official Anthropic SDK, in one short Python file. It has two tools, a weather lookup and a Celsius to Fahrenheit converter, and a loop that keeps going until Claude has what it needs.

At the end you ask "What is the weather in Paris, in Fahrenheit? Do I need an umbrella?" and Claude calls both tools in turn before it answers. Swap in your own functions and the same loop runs them.

What you'll learn

  • How to describe a tool so Claude knows when and how to call it.
  • How the agent loop works: send the conversation, run the tools Claude asks for, send back the results, repeat.
  • How to point the Anthropic SDK at SayGM with one base URL.

How it works

Claude decides which tool to call, and your code runs it. The loop carries the results back until Claude has an answer.

The agent loopYour question goes to the agent loop in run_agent. The loop sends the conversation and the two tool definitions through the SayGM gateway to Claude, claude-haiku-4-5, on the Messages API. When Claude asks for a tool, the loop runs get_weather or convert_temperature in your code and sends the result back. When Claude stops asking, its text is the answer.Youquestion and answerAgent looprun_agent()Your toolsget_weather,convert_temperatureSayGM gatewayMessages APIClaudeclaude-haiku-4-5
Your codeSayGM gatewayModel

What you need

  • Python 3.13.
  • A SayGM key in the SAYGM_API_KEY environment variable.

Setup

Install the Anthropic SDK.

commands.sh
pip install anthropic==1.8.0

Create a client with the base URL https://api.saygm.com. The SDK adds /v1/messages itself. The agent runs on claude-haiku-4-5.

main.py
import json
import os

import anthropic
from anthropic.types import MessageParam, ToolParam, ToolResultBlockParam

client = anthropic.Anthropic(
    base_url="https://api.saygm.com",
    api_key=os.environ["SAYGM_API_KEY"],
)
model = "claude-haiku-4-5"

Steps

Describe the tools

Each tool has a name, a description and a JSON schema for its input. run_tool does the work when Claude asks for one. The weather here is fixed sample data; replace run_tool with calls to a real weather API or your own systems.

Claude reads the description to decide when a tool fits, so write it the way you would explain the function to a colleague.

main.py
tools: list[ToolParam] = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a city.",
        "input_schema": {
            "type": "object",
            "properties": {"city": {"type": "string", "description": "City name, like Paris"}},
            "required": ["city"],
        },
    },
    {
        "name": "convert_temperature",
        "description": "Convert a temperature from Celsius to Fahrenheit.",
        "input_schema": {
            "type": "object",
            "properties": {"celsius": {"type": "number"}},
            "required": ["celsius"],
        },
    },
]


def run_tool(name: str, tool_input: dict) -> str:
    if name == "get_weather":
        return json.dumps({"city": tool_input["city"], "celsius": 18, "conditions": "light rain"})
    if name == "convert_temperature":
        return json.dumps({"fahrenheit": tool_input["celsius"] * 9 / 5 + 32})
    return f"Unknown tool: {name}"

Run the agent loop

Send the conversation to Claude. When it stops to use a tool, run every tool it asked for, add the results, and go again. When it stops for any other reason, its text is the answer. max_turns stops a loop that never ends.

Claude can ask for more than one tool in a single turn, which is why the loop runs every tool_use block before it calls Claude again.

main.py
def run_agent(question: str, max_turns: int = 6) -> tuple[str, list[MessageParam]]:
    messages: list[MessageParam] = [{"role": "user", "content": question}]
    for _ in range(max_turns):
        response = client.messages.create(
            model=model, max_tokens=1024, tools=tools, messages=messages
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            answer = "".join(block.text for block in response.content if block.type == "text")
            return answer, messages
        results: list[ToolResultBlockParam] = [
            {
                "type": "tool_result",
                "tool_use_id": block.id,
                "content": run_tool(block.name, block.input),
            }
            for block in response.content
            if block.type == "tool_use"
        ]
        messages.append({"role": "user", "content": results})
    msg = f"The agent did not finish within {max_turns} turns."
    raise RuntimeError(msg)


answer, transcript = run_agent(
    "What is the weather in Paris, in Fahrenheit? Do I need an umbrella?"
)
print(answer)

Run it

Put the code in main.py in the order above, export your key, and run it. You see Claude's answer, with the temperature in Fahrenheit.

commands.sh
python main.py

What you'll see

Claude called get_weather, then convert_temperature, and answered with both numbers and a verdict on the umbrella.

Terminal
$ python main.py
**Weather in Paris:**
- **Temperature:** 64.4°F (18°C)
- **Conditions:** Light rain

**Yes, you should bring an umbrella!** There's light rain in Paris right now, so an umbrella would definitely be helpful. The temperature is mild/cool, so you might also want to bring a light jacket along with your umbrella.
From a run of the example on Sep 27, 2026. Long answers are trimmed, and replies change a little from run to run.

Where this stops

The weather is fixed sample data, so Paris is always 18°C with light rain. A real agent needs real tools, and each tool is code you own: check its inputs, give it a timeout, and return a clear error message when it fails so Claude can tell the user.

For any tool that changes something, such as sending an email or placing an order, have your code ask a person to confirm before it runs.

Take it further

  • Replace get_weather with a call to a real weather API.
  • Add a third tool, such as a calendar lookup, and ask a question that needs all three.
  • Stream the final answer with client.messages.stream, so long replies appear as they are written.
  • Keep messages between questions to turn the script into a chat.

What it costs

You pay per token, from a prepaid balance. SayGM rates are live and typically below list. USD per 1M tokens, uncached input / output. SayGM’s side is the best rate a provider offered, as of 46 minutes ago.

ModelSayGMList priceYou save
Claude Haiku 4.5Anthropic$0.89 / $4.47$1.00 / $5.0010%

The last verified run of this example used 2,451 tokens, which costs less than one centat today’s SayGM rates.

Swap the model

Change the model id in the code to any model that serves the same API, such as a larger model for harder prompts or a confidential model for private data. This tutorial uses:

Every model, its API and its live rate is on the models page.

Next steps

Questions

What makes this an agent rather than a chatbot?
The loop. Claude asks for a tool, your code runs it and sends the result back, and this repeats until Claude has an answer. The agent decides which tools to call and in what order.
Do I need a special SDK to use SayGM?
No. Use the official anthropic package. Set base_url to https://api.saygm.com and use your SayGM key. The rest of the code is standard Anthropic SDK code.
Can I use Claude through the OpenAI SDK instead?
Use the Anthropic SDK for Claude, with the same SayGM key. Claude models serve the Messages API, which the Anthropic SDK speaks natively.
Where do my requests go?
Claude requests pass through the SayGM gateway to Anthropic. Your tools run in your own code, so their data stays with you unless you send it to the model.
How much does the agent cost to run?
Per token, from a prepaid SayGM balance. The table on this page shows live rates beside the list price, and what the last verified run of this example cost.

Sources

Run it with your own key.

One SayGM key works for every model in this tutorial. Top up once and pay per token.