An autonomous agent from scratch: a minimal working example
An agent is not a library, it is a three-step loop. Building a minimal working example on the raw API, no framework required.
4 min · · Updated · агенты · development
What an agent actually consists of
Without a framework, an agent is a three-step loop: the model receives the history and a list of available tools, decides whether to call a tool or answer in text, you execute the tool and append the result back to the history. Repeat until the model answers in text without calling a tool. A minimal agent needs nothing beyond this.
The minimal loop in Python
import requests
API_KEY = "fetch_your_key"
URL = "https://api.zukko.pro/v1/messages"
tools = [{
"name": "run_shell",
"description": "Run a shell command and return stdout",
"input_schema": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
}]This is enough for a working loop: the model asks for a command to run, gets the stdout back, decides whether it has enough data to answer, and either asks for another command or replies in text.
Choosing a model for the agent loop
Agentic loops with tools benefit from a large context, since the message history grows with every step — Claude and GPT in the catalogue hold 1,000,000 tokens (GLM holds 128K), so a loop of fifty steps with verbose command output won't hit the ceiling prematurely. For multi-step chains with several tools in a row, try gpt-5.6-sol and claude-opus-5-5, and claude-sonnet-5 when there are few steps and per-step response speed matters more; which model holds the chain better for you is something to test on your own tasks. Keep the cost of a long history in mind too: every step pays again for the whole accumulated context. Vendors charge ~10% of the input price for prompt-cache reads, while Zukko bills cached tokens at the regular price — for agents with a large repeated context, the real price gap is smaller than a head-to-head price-list comparison suggests.
Idempotency inside the loop
If the agent runs a tool with a side effect — say, a call to an external API — and the connection to the gateway drops mid-step, there's a real risk of repeating the same agent step twice and paying for it twice. Attach an Idempotency-Key based on the step number and a hash of the messages in the request to the model — a repeat with the same key and the same body within 24 hours returns the same response without a new charge.
Capping the number of steps
Without an explicit ceiling, the loop can get stuck repeating the same tool call if the model can't find a way out of the task. A simple safeguard is to count steps and force the loop to stop after a reasonable limit, returning a partial result instead of unbounded spend:
MAX_STEPS = 20
step = 0
while step < MAX_STEPS:
step += 1
...
if not tool_calls:
break
else:
print("Step limit reached, returning what we have")Twenty steps isn't a universal number, but for most tasks with one or two tools it's enough headroom, and a loop is usually visible by the fifth or sixth repeat of the same call anyway.
Estimating the loop's cost ahead of time
Before running an agent unattended on a real task, it helps to estimate an upper bound on cost: count the tokens of a typical step with POST /v1/messages/count_tokens (the call is free) and multiply by MAX_STEPS. That's not an exact figure — a given conversation may finish earlier — but it sets a sensible daily limit on the key in the dashboard, so you're not left guessing after the fact what the worst case could have cost.
Handling errors mid-loop
A long agent run will inevitably hit something from the standard set: 429 with key_daily_limit_reached, if the key has a daily spend limit set in the dashboard and the agent burns through it mid-task; 503 provider_unavailable, if a model is temporarily unavailable — worth falling back to another model from the catalogue at that step rather than dropping the whole loop; a response cutting off mid-way is almost always an HTTP client timeout, not the model running out of max_tokens, so for long tool-augmented responses the timeout should have some headroom.
Logging the transcript for debugging
It's worth saving the full exchange with the model — the message history and every tool call — to a file once the loop finishes. In practice, almost any agent bug is found by reading the step log rather than the loop's code: it shows exactly which step the model picked the wrong tool at, or where it got stuck repeating the same argument, instead of guessing from the final result what went wrong.
Cost per step
Every regular (non-streaming, as in the example above) response carries X-Zukko-Cost-Micros — the cost of that specific step in microdollars — and X-Fetch-Balance-Cents with the remaining balance (despite the name, also in microdollars: 1,000,000 = $1). If the agent runs with stream: true, these headers won't be there — check spend via /v1/usage or /v1/balance. In an agent loop it's worth logging both on every iteration: if the agent gets stuck repeating the same tool call without progress, the budget shows it before the logic does — spend climbs and X-Fetch-Balance-Cents drops faster than the task's size warrants. For agents that run unattended, it's sensible to set a daily limit on the key from its page in the dashboard right away — cheaper than catching a looping agent after the fact, on the bill.
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.