How to build an AI agent in Python from scratch: tool calling, loop, budget
A minimal AI agent in Python with no frameworks: tools, a call loop, step and budget limits. Full code on the OpenAI SDK and an estimate of what one task costs.
5 min · · агенты · development
How do you build an AI agent in Python without dragging a big framework into the project right away? A minimal agent is about a hundred lines: a model, a few tool functions, and a loop that calls them. Below: full working code on the OpenAI SDK, a walkthrough of each part, and two safeguards you shouldn't leave an agent unattended without — a step limit and a dollar budget.
What an AI agent is
A regular model request is a question and an answer. An agent is different: the model itself decides which actions to take, sees their results, and decides what to do next. Technically it's three things:
- A model that can call tools (tool calling).
- Tools — ordinary Python functions described with a JSON schema.
- A loop: send a request → the model asks to call a tool → call it, return the result → send another request. Repeat until a final answer or until a safeguard trips.
How to build an AI agent in Python: the full code
Install the SDK and put your key in an environment variable:
pip install openai
export ZUKKO_API_KEY=fetch_your_keyThe agent below can do three things: list the files in the data folder, read a file from there, and evaluate an arithmetic expression. That's enough, for example, to ask it to add up the amounts from several invoices.
import ast
import json
import operator
import os
from pathlib import Path
from openai import OpenAI
client = OpenAI(
base_url="https://api.zukko.pro/v1",
api_key=os.environ["ZUKKO_API_KEY"],
)
MODEL = "gpt-6.1-sol"The code needs Python 3.9 or newer (because of Path.is_relative_to). Create a data folder next to it, put a couple of text files with invoices in it, and run the script.
How the code works
Tools
Each tool is a Python function plus its description in TOOLS. The model sees only the description: the name, the description text, and the parameter schema. How clearly they're written determines whether the model calls the tool at the right moment. If the agent stubbornly ignores a tool, rewrite the description first — more in the article on why an agent doesn't see files.
Note the protection inside the functions. read_file won't let the agent outside the data folder, even if the model asks for ../.env. calculate parses the expression with ast and doesn't use eval, so it can't be used to run arbitrary code. The rule is simple: anything coming from the model is untrusted input.
The loop
On every step the agent sends the whole history: the system prompt, the task, its past tool calls, and their results. If the response contains tool_calls, an assistant message with those calls is added to the history first, then one role: "tool" message per call with the matching tool_call_id. If there are no calls, that's the final answer.
Tool errors don't crash the program; they go back to the model as text. That way it can correct itself, for example by picking the right file name after "file not found".
Two safeguards
MAX_STEPScaps the number of model requests. Without it, an agent going in circles keeps running until the money runs out.MAX_COSTtracks the actual cost from theusagefield of every response. For GPT models the gateway accounts for cache reads (prompt_tokens_details.cached_tokens), so the cached part of the input is billed at its own price. For Claude in the OpenAI format there's no cache discount and all input is billed at the input price — if you switch the agent to Claude, setPRICE_CACHEDequal toPRICE_IN.
The third safeguard lives outside the code: a separate API key for the agent with a daily and monthly dollar limit and a list of allowed models. If the code accidentally goes into an endless loop, the gateway responds with a 429 and code key_daily_limit_reached and stops it. How to set it up is in the key limits docs.
What one agent task costs
Take the worst case for our example: the agent used all 10 steps, each with up to 4,000 input and 300 output tokens (input grows as the history grows, so we take an upper estimate). Prices as of October 7, 2026; formula steps × (input × input price + output × output price) / 1,000,000, ignoring cache:
| Model | Input / output, $ per 1M | 10 steps |
|---|---|---|
deepseek-v4.1-flash | 0.013 / 0.038 | 10 × (4,000 × 0.013 + 300 × 0.038) / 1M = $0.000634 |
gpt-6.1-sol | 0.27 / 1.32 | 10 × (4,000 × 0.27 + 300 × 1.32) / 1M = $0.01476 |
claude-sonnet-5 | 0.4 / 2 | 10 × (4,000 × 0.4 + 300 × 2) / 1M = $0.022 |
A MAX_COST = 0.05 budget covers such a task with room to spare, but cuts the agent off if it starts reading large files or going in circles. Prices are an example calculation; for the current list and prices, call GET https://api.zukko.pro/v1/models or open the Models page in the dashboard.
Which model to put in MODEL depends on your tasks. As a reference, the catalog has an intelligence index (the Artificial Analysis Intelligence Index): Claude Opus 5.5 — 58, GPT-6.1 Sol — 52, DeepSeek V4.1 Flash — 39. Run the same task on two or three models and compare how many steps it took, whether the answer is correct, and how much was charged. Switching models is one line: the gateway accepts any model in the OpenAI format.
What to add next
- Streaming, if answers are long: when looping over chunks, check
if chunk.choices:beforechunk.choices[0]— the last chunk arrives withusageand an emptychoices. - Confirmation for dangerous actions. Tools that change something (writing files, calling external APIs) are better run after a human confirms.
- History compaction. A long history gets more expensive with every step: old tool results can be replaced with a short summary.
- Standard ready-made tools. Instead of hand-written functions you can plug in MCP servers — what they are is covered in the article what an MCP server is.
More on the client in the Python SDK reference and the chat/completions docs. To run the example, create a key for the agent in the dashboard with a small daily limit.
FAQ
Do I need LangChain or another framework for an agent?
Not to start: the loop from the example fits in a few dozen lines, and you see every request. Frameworks help when the number of tools and agents grows.
Which model should I use for an agent?
Start with gpt-6.1-sol as a mid-price option. If the tasks are simple, try deepseek-v4.1-flash; if they're hard, claude-opus-5-5. Decide based on results on your own tasks.
Can I let the agent run terminal commands?
Technically yes, but it's the most dangerous tool there is. If you need it, run the agent in an isolated environment, restrict the command list, and confirm every run.
Why does the agent answer in text instead of calling a tool?
Most often the tool description is unclear to the model, or the task is phrased so that the tool seems unnecessary. Sharpen the description, explicitly ask for tool use in the system prompt, and try another model.
zukko
Pay 25×
less
for Claude Code & ChatGPT
One fetch_* key and base URL api.zukko.pro — for Cursor, Claude Code, Codex, and any SDK.
Related articles
- Prompt Injection: What It Is and How to Protect a Bot and an AgentHow prompt injection works through user input and third-party documents, and which measures limit the damage: data isolation, tool permissions, key limits.
- Claude Code YOLO: Permission Modes and Working Without PromptsClaude Code has no mode literally called YOLO, but it has several permission modes and flexible rules. How to stop clicking Yes without losing files.
- Claude Code: a checklist of best practices for daily workHabits that make working with Claude Code predictable: a short CLAUDE.md, a plan before edits, small tasks, checking results and keeping spend under control.
- An MCP server in Java: a minimal example with one toolWe write a minimal MCP server in Java with one tool, register it in Claude Code, and learn to find the cause when the agent doesn't see it.