Skip to main content
This example shows how to build a math agent with multiple tools. The agent can perform various calculations and reason about which operations to use.

Complete Code

import asyncio
from opper_agents import Agent, tool, hook
from pydantic import BaseModel, Field


# Define tools
@tool
def add(a: float, b: float) -> float:
    """Add two numbers together."""
    return a + b


@tool
def subtract(a: float, b: float) -> float:
    """Subtract b from a."""
    return a - b


@tool
def multiply(a: float, b: float) -> float:
    """Multiply two numbers together."""
    return a * b


@tool
def divide(a: float, b: float) -> float:
    """Divide a by b."""
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b


@tool
def power(base: float, exponent: float) -> float:
    """Raise base to the power of exponent."""
    return base ** exponent


@tool
def sqrt(n: float) -> float:
    """Calculate the square root of a number."""
    if n < 0:
        raise ValueError("Cannot calculate square root of negative number")
    return n ** 0.5


# Define output schema for structured results
class MathResult(BaseModel):
    answer: float = Field(description="The final numerical answer")
    explanation: str = Field(description="Step-by-step explanation of the solution")
    operations_used: list[str] = Field(description="List of operations performed")


# Track tool usage with hooks
@hook("tool_call")
async def log_tool(context, agent, tool, parameters, **kwargs):
    print(f"  → {tool.name}({parameters})")


@hook("tool_result")
async def log_result(context, agent, tool, result, **kwargs):
    print(f"  = {result.result}")


# Create the agent
agent = Agent(
    name="MathAgent",
    description="Solves mathematical problems step by step",
    instructions="""You are a precise math assistant. When solving problems:
1. Break down complex calculations into simple steps
2. Use the appropriate tool for each operation
3. Show your work clearly in the explanation
4. Always verify your answer makes sense""",
    tools=[add, subtract, multiply, divide, power, sqrt],
    output_schema=MathResult,
    hooks=[log_tool, log_result],
    verbose=False
)


async def main():
    problems = [
        "What is 15 + 27?",
        "Calculate (5 + 3) * 4",
        "What is the square root of 144, then add 5?",
        "If I have $100 and spend 35%, how much do I have left?",
    ]

    for problem in problems:
        print(f"\n{'='*50}")
        print(f"Problem: {problem}")
        print("-" * 50)

        result = await agent.process(problem)

        print(f"\nAnswer: {result.answer}")
        print(f"Explanation: {result.explanation}")
        print(f"Operations: {', '.join(result.operations_used)}")


if __name__ == "__main__":
    asyncio.run(main())

Sample Output

==================================================
Problem: Calculate (5 + 3) * 4
--------------------------------------------------
  → add({"a": 5, "b": 3})
  = 8
  → multiply({"a": 8, "b": 4})
  = 32

Answer: 32
Explanation: First, I added 5 and 3 to get 8. Then I multiplied 8 by 4 to get 32.
Operations: add, multiply

==================================================
Problem: What is the square root of 144, then add 5?
--------------------------------------------------
  → sqrt({"n": 144})
  = 12
  → add({"a": 12, "b": 5})
  = 17

Answer: 17
Explanation: The square root of 144 is 12. Adding 5 gives us 17.
Operations: sqrt, add

Key Concepts Demonstrated

  1. Multiple tools: The agent has six different math operations
  2. Error handling: Division and sqrt handle edge cases
  3. Structured output: Results include answer, explanation, and operations
  4. Hooks: Track tool calls for visibility
  5. Multi-step reasoning: Agent chains operations for complex problems

Variations

Without Output Schema

For simpler use cases, skip the schema:
agent = Agent(
    name="SimpleMathAgent",
    tools=[add, subtract, multiply, divide]
)

result = await agent.process("What is 10 * 5?")
print(result)  # "50" or "The answer is 50"

With Memory

Remember previous calculations:
agent = Agent(
    name="MemoryMathAgent",
    tools=[add, subtract, multiply, divide],
    enable_memory=True
)

await agent.process("Calculate 100 * 5 and remember it as 'total'")
await agent.process("What was the total I calculated earlier?")

Next Steps