DEV Community

Srinivasaraju Tangella
Srinivasaraju Tangella

Posted on Edited on

Your LLM Is Not an AI Agent: Build a Real AI Agent Using Python, Tool Calling, and APIs

An LLM can explain how to perform a task. But can it actually perform that task?

This is where AI application engineering begins.

We hear about AI agents, autonomous systems, agentic workflows, function calling, MCP, and intelligent automation almost every day.

But there is an important engineering question:

How does an AI model interact with real applications, execute functions, retrieve information, and perform actions?

Let's build a working foundation instead of discussing AI agents only from a theoretical perspective.

In this article, we'll build a Python-based AI application where an LLM decides when to use a calculator, your Python application executes the function, and the result returns to the LLM.

By the end, you'll understand the architecture behind tool-calling AI applications.

1. First, understand the difference

Consider a simple request:

"Calculate the total cost of 25 servers at $120 each."

A traditional LLM might respond with the calculated answer.

But an application-integrated AI system can do something more interesting:

  1. Understand the user's request.
  2. Identify that a calculation is required.
  3. Select an available calculator function.
  4. Generate structured function arguments.
  5. Send the function call to the Python application.
  6. Execute the calculation.
  7. Return the result to the LLM.
  8. Generate a natural-language response.

The distinction is important.

An LLM does not automatically execute your Python functions. Your application is responsible for executing the requested function.

Why Are We Using a Simple Calculator Example?

You might wonder why we are using a basic calculator example instead of a real-world application.

The reason is simple: our objective is to understand the tool-calling architecture, not the complexity of the business application.

2.Understand the architecture

Let's examine the components.

  • User: Provides the request.
  • LLM: Understands the request and selects a tool.
  • Tool schema: Defines available functions and parameters.
  • Python application: Receives and executes function calls.
  • Tool result: Returns structured output.
  • LLM: Uses the result to generate the final response.

Architecture flow

Notice something important: the Python application controls execution.

The LLM selects the function, but the application decides whether and how to execute it.

3. Project requirements

We need:

  • Python 3.10+
  • OpenAI Python SDK
  • OpenAI API key
  • Basic Python programming knowledge

Install dependencies:

pip install openai python-dotenv

Create the project structure:

ai-tool-calling/
│
├── main.py
├── tools.py
├── requirements.txt
├── .env
└── README.md

4. Configure environment variables

Create ".env":

OPENAI_API_KEY=your_api_key
OPENAI_MODEL=your_supported_model

Never hardcode API credentials in application source code or commit ".env" to Git.

5. Build the calculator tool

Create "tools.py".

def calculator(operation, a, b):

operations = {
    "add": lambda: a + b,
    "subtract": lambda: a - b,
    "multiply": lambda: a * b,
    "divide": lambda: a / b
}

if operation not in operations:
    raise ValueError("Unsupported operation")

if operation == "divide" and b == 0:
    raise ValueError("Division by zero is not allowed")

return operations[operation]()
Enter fullscreen mode Exit fullscreen mode

We have implemented four operations.

Notice that we are not using Python's "eval()".

Why?

Because executing arbitrary model-generated expressions introduces unnecessary security risks.

We expose only predefined operations.

6. Define the tool schema

This is one of the most important parts of tool calling.

The LLM needs to understand:

  • What functions are available?
  • When should a function be selected?
  • What parameters does it require?
  • What type of values should it provide?

Define the tool:

calculator_tool = {
"type": "function",
"function": {
"name": "calculator",
"description": "Perform basic mathematical calculations",
"parameters": {
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": [
"add",
"subtract",
"multiply",
"divide"
]
},
"a": {
"type": "number"
},
"b": {
"type": "number"
}
},
"required": ["operation", "a", "b"],
"additionalProperties": False
},
"strict": True
}
}

The schema describes the function to the model.

It does not execute the function.

That responsibility belongs to Python.

7.Implement the AI application

Create "main.py".

import os
import json

from dotenv import load_dotenv
from openai import OpenAI

from tools import calculator

load_dotenv()

client = OpenAI(
api_key=os.getenv("OPENAI_API_KEY")
)

MODEL = os.getenv("OPENAI_MODEL")

calculator_tool = {
"type": "function",
"function": {
"name": "calculator",
"description": "Perform mathematical calculations",
"parameters": {
"type": "object",
"properties": {
"operation": {
"type": "string",
"enum": ["add", "subtract", "multiply", "divide"]
},
"a": {"type": "number"},
"b": {"type": "number"}
},
"required": ["operation", "a", "b"],
"additionalProperties": False
},
"strict": True
}
}

def execute_tool(name, arguments):

if name != "calculator":
    raise ValueError("Unknown tool")

return calculator(**arguments)
Enter fullscreen mode Exit fullscreen mode

def ask_ai(user_question):

messages = [
    {
        "role": "user",
        "content": user_question
    }
]

response = client.chat.completions.create(
    model=MODEL,
    messages=messages,
    tools=[calculator_tool],
    tool_choice="auto"
)

assistant_message = response.choices[0].message

messages.append(assistant_message)

if assistant_message.tool_calls:

    for tool_call in assistant_message.tool_calls:

        function_name = tool_call.function.name

        arguments = json.loads(
            tool_call.function.arguments
        )

        result = execute_tool(
            function_name,
            arguments
        )

        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps({
                "result": result
            })
        })

    final_response = client.chat.completions.create(
        model=MODEL,
        messages=messages,
        tools=[calculator_tool]
    )

    return final_response.choices[0].message.content

return assistant_message.content
Enter fullscreen mode Exit fullscreen mode

if name == "main":

question = (
    "Calculate the total cost of 25 servers "
    "at 120 dollars each."
)

answer = ask_ai(question)

print("\nAI Response:")
print(answer)
Enter fullscreen mode Exit fullscreen mode

8.Execute the application

Run:

python main.py

Expected execution flow:

User Request:
Calculate the total cost of 25 servers
at 120 dollars each.

LLM Decision:
Use calculator function.

Function Arguments:
{
"operation": "multiply",
"a": 25,
"b": 120
}

Python Execution:
calculator("multiply", 25, 120)

Function Result:
3000

Final AI Response:
The total cost of 25 servers
at $120 each is $3,000.

The exact wording of the final response can vary.

9.What actually happened?

Let's understand the execution from an engineering perspective.

Component| Responsibility
LLM| Understands the request
Tool schema| Defines available capabilities
Function selection| Identifies the required function
Python| Executes the function
Tool response| Returns execution output
LLM| Generates the final response

This is the foundation of an application-integrated AI agent.

10.What happens when the tool fails?

Production applications must handle failures.

Consider these scenarios:

  • Division by zero.
  • Invalid function arguments.
  • Unknown tool names.
  • API timeout.
  • API authentication failure.
  • Unexpected function output.

Do not assume that every model-generated function call is valid or safe.

Implement:

  1. Input validation.
  2. Function allowlisting.
  3. Exception handling.
  4. API timeout and retry policies.
  5. Execution logging.
  6. Rate limiting.
  7. Permission checks for sensitive operations.

For operations that modify external systems, introduce explicit authorization and approval controls.

11.Take this project to the next level

The calculator is only a starting point.

Imagine exposing multiple tools to the same LLM.

Tool| Capability
Calculator| Mathematical operations
Database query| Retrieve application data
Weather API| Retrieve weather information
Ticket API| Retrieve support tickets
Inventory API| Check product availability
File search| Retrieve document information
Monitoring API| Retrieve system metrics

Now the LLM can select different application capabilities depending on the user's request.

This is where application engineering meets agentic AI.

12.Production architecture considerations

A working demonstration is not automatically a production-ready AI agent.

Before deploying one, consider:

Reliability

  • API failures
  • Retry mechanisms
  • Timeouts
  • Idempotency

Security

  • API key management
  • Function allowlisting
  • Input validation
  • Least-privilege access
  • Human approval for sensitive actions

Observability

  • Tool selection accuracy
  • Function execution latency
  • Model response latency
  • API failures
  • Token consumption
  • Cost per request

Scalability

  • Concurrent requests
  • Asynchronous execution
  • Queue-based processing
  • Rate limiting
  • Horizontal scaling

Testing

  • Unit tests for individual functions
  • Integration tests for model interactions
  • Invalid argument tests
  • Failure scenario tests
  • End-to-end tests

13.Where should we go next?

Once you understand tool calling, you can progressively build more sophisticated applications.

A possible engineering progression:

  1. Single-tool AI application.
  2. Multiple-tool AI application.
  3. Tool calling with FastAPI.
  4. Database-integrated AI assistant.
  5. Retrieval-Augmented Generation (RAG).
  6. Vector database integration.
  7. MCP server integration.
  8. Multi-agent orchestration.
  9. Stateful workflows using LangGraph.
  10. Production deployment with Docker and Kubernetes.
  11. Monitoring, security and cost management.

12.AI application evaluation and reliability engineering.

Each stage introduces a new engineering challenge.

Final Thoughts

The biggest misconception about AI agents is that an LLM alone makes an application intelligent.

In reality, a useful AI application requires much more:

  • Model intelligence
  • Application logic
  • Tool integration
  • Data access
  • Security boundaries
  • Execution control
  • Observability
  • Reliability engineering

The future of AI application development is not just about writing better prompts. It is about engineering reliable systems around intelligent models.

I would like to hear from the developer community:

  1. Are you currently implementing tool calling in your applications?
  2. Do you prefer direct function calling or an agent framework?
  3. What is the biggest challenge you face when taking AI prototypes into production?

Share your experience in the comments. Let's learn from real implementations, not just AI terminology.


References

Top comments (0)