Skip to main content

Project: Building a Calculator Agent

16/28
Chapter 4 Tool Use and Function Calling

Project: Building a Calculator Agent

24 min read Lesson 16 / 28

Project: Building a Calculator Agent

Let us build a complete calculator agent that handles complex multi-step math problems by breaking them into primitive operations and chaining the results.

Calculator Tool Suite

import anthropic
import json
import math

client = anthropic.Anthropic()

# ── Tool implementations ──

def add(a: float, b: float) -> float:
    return a + b

def subtract(a: float, b: float) -> float:
    return a - b

def multiply(a: float, b: float) -> float:
    return a * b

def divide(a: float, b: float) -> float:
    if b == 0:
        raise ValueError("Division by zero")
    return a / b

def power(base: float, exponent: float) -> float:
    return math.pow(base, exponent)

def sqrt(value: float) -> float:
    if value < 0:
        raise ValueError("Cannot take square root of negative number")
    return math.sqrt(value)

def logarithm(value: float, base: float = math.e) -> float:
    if value <= 0:
        raise ValueError("Logarithm undefined for non-positive values")
    return math.log(value, base)

TOOL_MAP = {
    "add": add, "subtract": subtract, "multiply": multiply,
    "divide": divide, "power": power, "sqrt": sqrt, "logarithm": logarithm,
}

# ── Tool definitions ──

CALC_TOOLS = [
    {
        "name": "add",
        "description": "Add two numbers: a + b",
        "input_schema": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]},
    },
    {
        "name": "subtract",
        "description": "Subtract b from a: a - b",
        "input_schema": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]},
    },
    {
        "name": "multiply",
        "description": "Multiply two numbers: a * b",
        "input_schema": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]},
    },
    {
        "name": "divide",
        "description": "Divide a by b: a / b. Returns error if b is 0.",
        "input_schema": {"type": "object", "properties": {"a": {"type": "number"}, "b": {"type": "number"}}, "required": ["a", "b"]},
    },
    {
        "name": "power",
        "description": "Raise base to exponent: base ** exponent",
        "input_schema": {"type": "object", "properties": {"base": {"type": "number"}, "exponent": {"type": "number"}}, "required": ["base", "exponent"]},
    },
    {
        "name": "sqrt",
        "description": "Square root of a non-negative number",
        "input_schema": {"type": "object", "properties": {"value": {"type": "number"}}, "required": ["value"]},
    },
]

# ── Agent loop ──

def calculator_agent(problem: str) -> str:
    messages = [{"role": "user", "content": problem}]

    for _ in range(20):
        response = client.messages.create(
            model="claude-sonnet-4-5",
            max_tokens=2048,
            system="You are a precise calculator. Break problems into atomic operations using the available tools. Show your work step by step.",
            tools=CALC_TOOLS,
            messages=messages,
        )

        if response.stop_reason == "end_turn":
            return response.content[0].text

        if response.stop_reason == "tool_use":
            tool_results = []
            for block in response.content:
                if block.type == "tool_use":
                    handler = TOOL_MAP[block.name]
                    try:
                        result = handler(**block.input)
                        content = str(result)
                    except Exception as e:
                        content = json.dumps({"error": str(e)})

                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": content,
                    })

            messages.append({"role": "assistant", "content": response.content})
            messages.append({"role": "user", "content": tool_results})

    return "Could not complete calculation."


# ── Test it ──

problems = [
    "What is (15.5 * 4) + sqrt(144) - 7?",
    "Calculate the compound interest on $10,000 at 5% annual rate for 3 years.",
    "If a triangle has legs of 3 and 4, what is the hypotenuse?",
]

for problem in problems:
    print(f"Problem: {problem}")
    print(f"Answer: {calculator_agent(problem)}\n")

This calculator agent demonstrates the full tool use lifecycle: defining tools, running the agent loop, executing tool calls, and returning results until Claude has a complete answer.