Lekcija 01 · Mentalni modelLesson 01 · Mental model
Agent nije prompt. Agent je petlja.An agent isn't a prompt. An agent is a loop.
Prompt pitaš jednom i dobiješ jedan odgovor. Agent radi drugačije: posmatra, misli, deluje, proverava — pa iznova, sve dok se cilj ne ispuni. U toj petlji se krije stvarna moć agenata.A prompt asks once and answers once. An agent works differently: it observes, thinks, acts and verifies — then again, until the goal is met. That loop is where the real power of agents lies.
Kad si imao jedan poziv modela i jedan odgovor, sve je zavisilo od toga koliko savršeno formulišeš prompt. Model nije mogao da vidi posledice svojih reči — pogodio je ili promašio.When you had one model call and one answer, everything hinged on how perfectly you phrased the prompt. The model couldn't see the consequences of its words — it either hit or missed.
Moderni modeli su dovoljno sposobni da se sami vrate na pravi put ako im daš petlju sa povratnom informacijom. Zato se težište pomerilo: nije toliko važno da savršeno kažeš šta hoćeš, koliko da napraviš dobru petlju u kojoj model može da proba, vidi rezultat i pokuša ponovo.Modern models are capable enough to correct course on their own if you give them a loop with feedback. So the center of gravity moved: it matters less that you perfectly state what you want, and more that you build a good loop where the model can try, see the result, and try again.
Prompt · jedan pokušajPrompt · one shot
Ulaz → izlaz. Nema pristupa stvarnom svetu, nema provere, nema drugog pokušaja. Greška u prvom koraku zatruje ceo odgovor.Input → output. No access to the real world, no verification, no second try. An error in the first step poisons the whole answer.
Loop · procesLoop · process
Ulaz → akcija → rezultat → korekcija → … Model radi, čita posledice i menja plan dok ne stigne do cilja.Input → action → result → correction → … The model acts in the world, reads the consequences, and revises its plan until it reaches the goal.
Prompt engineering se ne gasi — seli se sa nivoa „napiši savršenu rečenicu" na nivo „dizajniraj petlju": koje alate dajem, kako proveravam svaki korak, kada agent staje.Prompt engineering isn't dying — it's moving from "write the perfect sentence" up to "design the loop": which tools I hand over, how I verify each step, when the agent stops.
Šta ćeš na kraju umetiWhat you'll be able to do
Da objasniš zašto je looping potisnuo prompting kao glavnu veštinu.Explain why looping has displaced prompting as the core skill.
Da od nule napišeš radnu agentsku petlju sa Claude API-jem.Write a working agent loop from scratch with the Claude API.
Da dodaš verifikaciju, kočnice i drugi alat — i da namerno slomiš petlju da vidiš čemu služe.Add verification, brakes, and a second tool — and deliberately break the loop to see what each one is for.
Da prepoznaš napredne obrasce i vidiš kako Claude Code koristi baš ovu petlju.Recognize the advanced patterns and see how Claude Code runs this exact loop.
Konkretan ciljConcrete goalNa kraju ćeš imati:By the end you'll have:fajl agent.py, komandu python agent.py, i izlaz u kom agent sam popravi zadatak/mod.py dok pytest ne prođe.the file agent.py, the command python agent.py, and output where the agent fixes zadatak/mod.py on its own until pytest passes.
Lekcija 02 · AnatomijaLesson 02 · Anatomy
Pet delova svake petljeThe five parts of every loop
Svaki agent, ma koliko složen, ima ovih pet sastojaka. Ako nešto od ovoga fali, agent ili ne radi ili ne zna kad je gotov.Every agent, however complex, has these five ingredients. If one is missing, the agent either doesn't work or doesn't know when it's done.
CiljGoalŠta znači „gotovo".What "done" means.Mora biti proverljiv — „testovi prolaze", a ne „uradi lepo". Bez merljivog cilja petlja ne zna kad da stane.It must be checkable — "tests pass," not "do it nicely." Without a measurable goal the loop can't know when to stop.
PercepcijaPerceptionKontekst koji ulazi u model.The context that enters the model.Trenutno stanje sveta: fajlovi, izlaz prethodne komande, istorija koraka. Ovo je „posmatraj".The current state of the world: files, the output of the previous command, the history of steps. This is "observe."
RasuđivanjeReasoningModel bira sledeći potez.The model picks the next move.Na osnovu cilja i konteksta odlučuje koju akciju da preduzme. Ovo je „misli".Based on the goal and context it decides which action to take. This is "think."
AlatiToolsRuke agenta.The agent's hands.Funkcije koje menjaju svet ili donose informaciju: čitaj fajl, pokreni komandu, pretraži, upiši. Ovo je „deluj".Functions that change the world or fetch information: read a file, run a command, search, write. This is "act."
VerifikacijaVerificationProvera i uslov izlaska.The check and the exit condition.Da li je akcija približila cilju? Test, linter, provera rezultata — i odluka: nastavi ili stani. Ovo je „proveri".Did the action move us closer to the goal? A test, a linter, a result check — and the decision: continue or stop. This is "verify."
ZapamtiRememberAlati su ono što agenta razlikuje od čet-bota.Tools are what separate an agent from a chatbot.Bez alata model samo priča. Sa alatima on menja svet i vidi posledice — a to je preduslov da petlja uopšte ima smisla.Without tools the model just talks. With tools it changes the world and sees the consequences — the precondition for a loop to mean anything at all.
U narednim lekcijama svaki od ovih pet delova dobija svoje mesto u kodu. Vredi da ih zapamtiš kao proveru: kad tvoj agent zabaguje, skoro uvek fali ili se lomi jedan od ovih pet.In the coming lessons each of these five parts gets its place in the code. Keep them as a checklist: when your agent misbehaves, one of these five is almost always missing or broken.
Lekcija 03 · SkeletLesson 03 · Skeleton
Najprostija petlja, u pseudokoduThe simplest loop, in pseudocode
Pre nego što dodamo pravi model, evo suštine. Cela ideja agenta staje u nekoliko redova.Before we add a real model, here's the essence. The whole idea of an agent fits in a few lines.
petlja.py
# kontekst = sve što model treba da vidi ovog kruga
kontekst = [pocetni_zadatak]
while not cilj_ispunjen(kontekst):
potez = model.odluci(kontekst) # MISLI
rezultat = izvrsi(potez) # DELUJ (tool)
kontekst.append(rezultat) # POSMATRAJ
if iscrpljen_budzet():
break # zaštita
# context = everything the model needs to see this round
context = [initial_task]
while not goal_met(context):
move = model.decide(context) # THINK
result = run(move) # ACT (tool)
context.append(result) # OBSERVE
if budget_exhausted():
break # safety brake
Sve ostalo — okviri, biblioteke, multi-agent sistemi — samo su varijacije ove petlje. Ako razumeš ovih par redova, razumeš 80% priče.Everything else — frameworks, libraries, multi-agent systems — is just a variation on this loop. Understand these few lines and you understand 80% of the story.
Primeti da su četiri koraka sa dijagrama tu: model.odluci je misli, izvrsi je deluj, kontekst.append je posmatraj, a cilj_ispunjen je proveri. U sledećim lekcijama zamenjujemo svaku od ovih fiktivnih funkcija pravim kodom.Notice the four steps from the diagram are all here: model.decide is think, run is act, context.append is observe, and goal_met is verify. In the next lessons we replace each of these placeholder functions with real code.
Lekcija 04 · PripremaLesson 04 · Setup
Postavi okruženje za radSet up your working environment
Pravimo pravog agenta u Pythonu. Treba ti Python 3.10+, jedan paket i API ključ. Pet minuta posla.We're building a real agent in Python. You need Python 3.10+, one package, and an API key. Five minutes of setup.
1 — Virtuelno okruženje i paket1 — Virtual environment and package
# in the terminal
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install anthropic
2 — API ključ2 — API key
Napravi ključ na console.anthropic.com i postavi ga kao promenljivu okruženja. Nikad ga ne piši direktno u kod.Create a key at console.anthropic.com and set it as an environment variable. Never hardcode it.
export ANTHROPIC_API_KEY="sk-ant-..." # dodaj u ~/.zshrc da ostane
export ANTHROPIC_API_KEY="sk-ant-..." # add to ~/.zshrc to persist it
Zašto promenljiva okruženjaWhy an environment variableAko ključ ubaciš u kod, lako završi na GitHub-u i neko ga zloupotrebi. SDK ga sam pročita iz ANTHROPIC_API_KEY — ti ga nigde ne pominješ u kodu.If you put the key in your code it easily ends up on GitHub and gets abused. The SDK reads it from ANTHROPIC_API_KEY on its own — you never mention it in code.
3 — Zadatak za agenta3 — A task for the agent
Da bi agent imao šta da popravlja, napravi mali projekat sa namerno pokvarenom funkcijom i testom. Agent će ga popraviti u lekciji 06.So the agent has something to fix, create a tiny project with a deliberately broken function and a test. The agent will fix it in lesson 06.
zadatak/mod.py
def saberi(a, b):
return a - b # BUG: treba +
def saberi(a, b):
return a - b # BUG: should be +
zadatak/test_mod.py
from mod import saberi
def test_saberi():
assert saberi(2, 3) == 5
assert saberi(0, 0) == 0
from mod import saberi
def test_saberi():
assert saberi(2, 3) == 5
assert saberi(0, 0) == 0
Pokreni pytest u toj fascikli — test pada. To je naš merljiv cilj: petlja radi dok test ne prođe.Run pytest in that folder — the test fails. That's our measurable goal: the loop runs until the test passes.
Ako zapneTroubleshootingModuleNotFoundError: anthropic — nisi u venv-u ili nisi uradio pip install. · pytest: command not found — pip install pytest. · Model greška / NotFoundError — proveri da je naziv modela tačan i da ti nalog ima pristup. · 401 / ključ — ANTHROPIC_API_KEY nije postavljen u ovom istom terminalu. · 429 / rate limit — sačekaj; SDK sam ponavlja pokušaj.ModuleNotFoundError: anthropic — you're not in the venv or didn't pip install. · pytest: command not found — pip install pytest. · Model error / NotFoundError — check the model name is exact and your account has access. · 401 / key — ANTHROPIC_API_KEY isn't set in this same terminal. · 429 / rate limit — wait; the SDK retries on its own.
✓ CheckpointCheckpointPokreni pytest u fascikli zadatak/. Treba da vidiš 1 failed — to je polazno stanje i merljiv cilj cele petlje. Zapamti taj ispis: kad na kraju umesto njega bude 1 passed, to nije agent rekao da je gotov, nego je test to potvrdio.Run pytest inside the zadatak/ folder. You should see 1 failed — that's the starting state and the measurable goal of the whole loop. Remember that output: when it later reads 1 passed, that isn't the agent claiming it's done, it's the test confirming it.
Lekcija 05 · Radni agentLesson 05 · Working agent
Prvi agent: tool use loopYour first agent: the tool-use loop
Sad zamenjujemo pseudokod pravim modelom i jednim alatom. Ključna ideja: model ne izvršava alat sam — on traži poziv, tvoj kod ga izvrši i vrati rezultat u sledeći krug.Now we swap the pseudocode for a real model and a single tool. The key idea: the model doesn't run the tool itself — it requests a call, your code executes it and feeds the result back into the next round.
agent.py
import subprocess
import anthropic
client = anthropic.Anthropic() # čita ANTHROPIC_API_KEY iz okruženja
MODEL = "..." # naziv aktuelnog modela (vidi napomenu ispod)
# --- 1. definiši alat: šta agent sme da radi ---
tools = [{
"name": "pokreni_komandu",
"description": "Izvrši shell komandu u fascikli zadatak/ i vrati izlaz.",
"input_schema": {
"type": "object",
"properties": {"cmd": {"type": "string"}},
"required": ["cmd"],
},
}]
# --- 2. tvoja implementacija alata ---
def pokreni_komandu(cmd):
r = subprocess.run(cmd, shell=True, cwd="zadatak",
capture_output=True, text=True)
return (r.stdout + r.stderr) or "(bez izlaza)"
# --- 3. petlja ---
messages = [{"role": "user",
"content": "Popravi bug u mod.py tako da pytest prolazi."}]
for korak in range(25): # gornja granica koraka = zaštita
resp = client.messages.create(
model=MODEL,
max_tokens=4096,
tools=tools,
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use": # IZLAZ: model je gotov
tekst = next((b.text for b in resp.content if b.type == "text"), "")
print(tekst)
break
# model je zatražio alat -> mi ga izvršimo (DELUJ)
rezultati = []
for blok in resp.content:
if blok.type == "tool_use":
izlaz = pokreni_komandu(blok.input["cmd"])
print("→", blok.input["cmd"])
rezultati.append({
"type": "tool_result",
"tool_use_id": blok.id,
"content": izlaz,
})
messages.append({"role": "user", "content": rezultati}) # POSMATRAJ
import subprocess
import anthropic
client = anthropic.Anthropic() # reads ANTHROPIC_API_KEY from the environment
MODEL = "..." # the current model name (see the note below)
# --- 1. define the tool: what the agent is allowed to do ---
tools = [{
"name": "run_command",
"description": "Run a shell command in the zadatak/ folder and return its output.",
"input_schema": {
"type": "object",
"properties": {"cmd": {"type": "string"}},
"required": ["cmd"],
},
}]
# --- 2. your implementation of the tool ---
def run_command(cmd):
r = subprocess.run(cmd, shell=True, cwd="zadatak",
capture_output=True, text=True)
return (r.stdout + r.stderr) or "(no output)"
# --- 3. the loop ---
messages = [{"role": "user",
"content": "Fix the bug in mod.py so that pytest passes."}]
for step in range(25): # step ceiling = safety brake
resp = client.messages.create(
model=MODEL,
max_tokens=4096,
tools=tools,
messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use": # EXIT: the model is done
text = next((b.text for b in resp.content if b.type == "text"), "")
print(text)
break
# the model requested a tool -> we run it (ACT)
results = []
for block in resp.content:
if block.type == "tool_use":
output = run_command(block.input["cmd"])
print("→", block.input["cmd"])
results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": output,
})
messages.append({"role": "user", "content": results}) # OBSERVE
Pokreni sa python agent.py. Agent će sam da izlista fajlove, pročita mod.py, uvidi bug, i pokuša popravku — sve kroz jedan jedini alat koji ume da pokrene komandu.Run it with python agent.py. The agent will list files, read mod.py, spot the bug, and attempt a fix on its own — all through a single tool that can run a command.
Koji model upisatiWhich model to useNaziv modela namerno stoji kao MODEL = "..." — modeli se smenjuju brže nego što se tutorijali ažuriraju. Uzmi aktuelni naziv iz Anthropic dokumentacije i upiši ga na to jedno mesto; ostatak koda se ne dira.The model name is deliberately left as MODEL = "..." — models turn over faster than tutorials get updated. Grab the current name from the Anthropic docs and set it in that one place; the rest of the code stays untouched.
BezbednostSecurityOvaj alat pušta shell komandu koju je smislio model (subprocess.run(cmd, shell=True)). U ovom tutorijalu to je bezbedno jer radiš nad probnom fasciklom zadatak/ na svojoj mašini. Nikad ne pokreći ovako nešto nad tuđim ulazom ili u produkciji bez ograde — allowlist dozvoljenih komandi, sandbox ili kontejner. To je tema 2. dela.This tool runs a shell command the model made up (subprocess.run(cmd, shell=True)). It's safe here because you're working on the toy zadatak/ folder on your own machine. Never run something like this on someone else's input or in production without a guardrail — an allowlist of permitted commands, a sandbox, or a container. That's the subject of Part 2.
Isečak vs pun kodSnippet vs full codeIsečci u tekstu su ponegde skraćeni radi jasnoće. Kompletan fajl koji možeš da pokreneš (agent.py + zadatak/) je na GitLab-u u folderu deo-1-agentske-petlje/ — ne očekuj da svaki isečak radi sam za sebe.The snippets in the text are sometimes shortened for clarity. The complete, runnable file (agent.py + zadatak/) is on GitLab in the deo-1-agentske-petlje/ folder — don't expect every snippet to run standalone.
Pet delova, ponovoThe five parts, againmessages = percepcijaperception · create = rasuđivanjereasoning · tools = rukehands · stop_reason = verifikacija/izlazverification/exit · range(25) = kočnica. To je ceo agent.the brake. That's the whole agent.
Kako izgleda messages posle jednog krugaWhat messages looks like after one round
Najčešći kamen spoticanja: rezultat alata nije običan tekst — to je blok tool_result u user poruci, povezan sa zahtevom preko istog tool_use_id. Ovako niz izgleda kad model zatraži alat pa mu mi vratimo izlaz:The most common stumbling point: a tool's result isn't plain text — it's a tool_result block inside a user message, tied to the request by the same tool_use_id. Here's how the array looks once the model requests a tool and we feed the output back:
oblik messages nizashape of the messages array
messages = [
{"role": "user", "content": "Popravi bug u mod.py..."},
# 1) model TRAŽI alat (assistant poruka):
{"role": "assistant", "content": [
{"type": "text", "text": "Pogledaću mod.py."},
{"type": "tool_use", "id": "toolu_01",
"name": "pokreni_komandu",
"input": {"cmd": "cat mod.py"}},
]},
# 2) MI vraćamo rezultat — isti tool_use_id ih povezuje:
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_01",
"content": "def saberi(a, b):\n return a - b"},
]},
]
messages = [
{"role": "user", "content": "Fix the bug in mod.py..."},
# 1) the model REQUESTS a tool (assistant message):
{"role": "assistant", "content": [
{"type": "text", "text": "Let me look at mod.py."},
{"type": "tool_use", "id": "toolu_01",
"name": "run_command",
"input": {"cmd": "cat mod.py"}},
]},
# 2) WE return the result — the same tool_use_id links them:
{"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_01",
"content": "def saberi(a, b):\n return a - b"},
]},
]
Ali primeti slabost: petlja veruje modelu na reč. Kad model kaže „gotov sam", mi mu verujemo — čak i ako test i dalje pada. To rešavamo u sledećoj lekciji.But notice the weakness: the loop takes the model at its word. When the model says "I'm done," we believe it — even if the test still fails. We fix that in the next lesson.
✓ CheckpointCheckpointPokreni python agent.py i prati ispis krug po krug. Treba da vidiš najmanje dva poziva alata pre nego što model uopšte pokuša popravku — prvo pogleda šta ima, pa pročita fajl. Ako agent odmah piše izmenu bez čitanja, opis alata mu je previše sugestivan.Run python agent.py and follow the output round by round. You should see at least two tool calls before the model even attempts a fix — first it looks around, then it reads the file. If the agent writes a change straight away without reading, your tool description is steering it too hard.
Lekcija 06 · VerifikacijaLesson 06 · Verification
Ne veruj modelu — proveriDon't trust the model — verify
Najpouzdaniji uslov izlaska nije „model kaže da je gotov", nego spoljna provera koja prođe. Neka petlja sama pokreće testove i staje tek kad su zeleni.The most reliable exit condition isn't "the model says it's done," it's an external check that passes. Let the loop run the tests itself and stop only when they're green.
agent.py
def testovi_prolaze():
r = subprocess.run("pytest -q", shell=True, cwd="zadatak",
capture_output=True, text=True)
return r.returncode == 0
for korak in range(25):
resp = client.messages.create(
model=MODEL, max_tokens=4096,
tools=tools, messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
# model misli da je gotov -> MI proveravamo
if testovi_prolaze():
print("✓ Testovi prolaze. Zadatak završen.")
break
# nije gotovo: vrati ga u petlju sa konkretnim feedbackom
messages.append({"role": "user", "content":
"Testovi i dalje padaju. Pokreni pytest, pogledaj grešku i popravi."})
continue
rezultati = []
for blok in resp.content:
if blok.type == "tool_use":
rezultati.append({"type": "tool_result", "tool_use_id": blok.id,
"content": pokreni_komandu(blok.input["cmd"])})
messages.append({"role": "user", "content": rezultati})
def tests_pass():
r = subprocess.run("pytest -q", shell=True, cwd="zadatak",
capture_output=True, text=True)
return r.returncode == 0
for step in range(25):
resp = client.messages.create(
model=MODEL, max_tokens=4096,
tools=tools, messages=messages,
)
messages.append({"role": "assistant", "content": resp.content})
if resp.stop_reason != "tool_use":
# the model thinks it's done -> WE verify
if tests_pass():
print("✓ Tests pass. Task complete.")
break
# not done: send it back into the loop with concrete feedback
messages.append({"role": "user", "content":
"The tests still fail. Run pytest, read the error, and fix it."})
continue
results = []
for block in resp.content:
if block.type == "tool_use":
results.append({"type": "tool_result", "tool_use_id": block.id,
"content": run_command(block.input["cmd"])})
messages.append({"role": "user", "content": results})
Sad je uslov izlaska čvrst: petlja se ne oslanja na samopouzdanje modela nego na returncode pytesta. Ako model pogreši i test i dalje pada, agent dobija jasnu poruku i nastavlja — tako izgleda samokorekcija.Now the exit condition is solid: the loop relies on pytest's returncode, not the model's confidence. If the model gets it wrong and the test still fails, the agent gets a clear message and continues — that's self-correction in action.
Ovo je najvažnija lekcija celog tutorijala: verifikacija zatvara petlju. Bez nje agent je samo pričljiv; sa njom je pouzdan.This is the most important lesson in the whole tutorial: verification closes the loop. Without it an agent is just talkative; with it, it's reliable.
ŠablonPatternVerifikator ne mora biti pytest. Može biti linter, kompajler, HTTP status 200, poređenje sa očekivanim izlazom — bilo šta što objektivno kaže „jeste / nije".The verifier doesn't have to be pytest. It can be a linter, a compiler, an HTTP 200, a comparison against expected output — anything that objectively says "yes / no."
✓ CheckpointCheckpointPusti petlju do kraja. Poslednji ispis treba da bude 1 passed, i tek posle njega petlja staje. Pa uradi obrnutu probu: ručno pokvari mod.py posle uspešnog prolaza i pokreni ponovo — agent ne sme da stane dok test ponovo ne prođe. Ako stane, izlaz ti visi o rečima modela, ne o testu.Let the loop run to the end. The last line should be 1 passed, and only after it does the loop stop. Then run the reverse check: break mod.py by hand after a successful run and start again — the agent must not stop until the test passes again. If it stops, your exit condition hangs on the model's word, not on the test.
Lekcija 07 · KontrolaLesson 07 · Control
Kako petlja zna da staneHow the loop knows to stop
Uslov izlaska je najvažnija — i najčešće zaboravljena — odluka. Agent bez dobrog izlaza ili se vrti u krug, ili troši budžet, ili staje prerano. U praksi kombinuješ više kočnica.The exit condition is the most important — and most often forgotten — decision. An agent without a good exit either spins in circles, burns its budget, or stops too early. In practice you combine several brakes.
Cilj-signalGoal signalModel kaže da je gotov.The model says it's done.Kao stop_reason != "tool_use" — nema više poteza. Najslabiji signal, ne verovati mu samom.Like stop_reason != "tool_use" — no more moves. The weakest signal; don't trust it alone.
VerifikatorVerifierSpoljna provera prođe.An external check passes.Testovi zeleni, linter čist. Najpouzdaniji signal jer ne veruje modelu na reč.Tests green, linter clean. The most reliable signal because it doesn't take the model at its word.
Max korakaMax stepsTvrda granica.A hard limit.Nikad ne pusti petlju bez gornje granice iteracija — osigurač protiv beskonačnog vrtenja.Never run a loop without an upper bound on iterations — the fuse against infinite spinning.
BudžetBudgetTokeni ili vreme.Tokens or time.Stani kad potrošnja pređe prag. Čuva ti novčanik i spasava od zaglavljenih petlji.Stop when spending crosses a threshold. Protects your wallet and against stuck loops.
KonvergencijaConvergenceNema napretka.No progress.Ako se N krugova zaredom ništa ne menja, stani i prijavi — agent je zaglavio.If nothing changes for N rounds in a row, stop and report — the agent is stuck.
MAX_STEPS = 25
MAX_TOKENS = 200_000
spent = 0
previous_output = None
no_change = 0
for step in range(MAX_STEPS): # brake 1: max steps
resp = client.messages.create(model=MODEL, max_tokens=4096,
tools=tools, messages=messages)
spent += resp.usage.input_tokens + resp.usage.output_tokens
if spent > MAX_TOKENS: # brake 2: budget
print("Budget spent — stopping.")
break
if resp.stop_reason != "tool_use" and tests_pass():
print("✓ Done.") # brake 3: verifier
break
# ... run the tools, get 'output' ...
if output == previous_output: # brake 4: convergence
no_change += 1
if no_change >= 3:
print("No progress for 3 rounds — the agent is stuck.")
break
else:
no_change = 0
previous_output = output
Ne moraš uvek sve četiri — ali max koraka je obavezan uvek. To je jedina kočnica koja te čuva od agenta koji troši novac u beskonačnoj petlji dok ti spavaš.You don't always need all four — but max steps is mandatory, always. It's the one brake that saves you from an agent burning money in an infinite loop while you sleep.
✓ CheckpointCheckpointSpusti MAX_KORAKA na 2 i zadaj nešto što se ne može rešiti u dva poteza. Petlja mora da izađe posle drugog kruga i da to jasno kaže — ne da se vrti dalje i ne da tiho vrati prazan rezultat. Poruka o razlogu izlaska je ono što ćeš čitati u produkciji u tri ujutru.Drop MAX_STEPS to 2 and give it something that can't be solved in two moves. The loop must exit after the second round and say so clearly — not keep spinning, and not quietly return an empty result. That exit message is what you'll be reading in production at three in the morning.
Lekcija 08 · Učenje kroz kvarLesson 08 · Learning by breaking
Namerno slomi petljuBreak the loop on purpose
Zaštite ćeš razumeti tek kad vidiš šta se dešava bez njih. Uradi ove eksperimente svesno — svaki ti pokazuje čemu služi jedna kočnica.You'll only understand the safeguards once you see what happens without them. Do these experiments deliberately — each one shows you what a brake is for.
Eksperiment 1 — skini max korakaExperiment 1 — remove the step limit
Zameni for korak in range(25) sa while True i daj agentu zadatak koji ne može da reši (npr. „popravi test" a fajl je read-only). Gledaj kako se vrti unedogled i troši tokene. Pouka: zato max koraka postoji.Replace for step in range(25) with while True and give the agent a task it can't solve (e.g. "fix the test" while the file is read-only). Watch it spin forever and burn tokens. Lesson: that's why max steps exists.
Vrati se na petlju iz lekcije 05 (bez testovi_prolaze) i ubaci suptilan bug koji model „popravi" pogrešno. Agent će samouvereno reći „gotovo" iako test pada. Pouka: bez spoljne provere agent laže u dobroj nameri.Go back to the loop from lesson 05 (without tests_pass) and introduce a subtle bug the model "fixes" incorrectly. The agent will confidently declare "done" while the test fails. Lesson: without an external check, the agent lies in good faith.
Eksperiment 3 — progutaj grešku iz alataExperiment 3 — swallow the tool's error
loša implementacijabad implementation
def pokreni_komandu(cmd):
try:
r = subprocess.run(cmd, shell=True, cwd="zadatak",
capture_output=True, text=True)
return r.stdout # LOŠE: gutamo stderr i greške
except Exception:
return "" # LOŠE: model ne zna da je puklo
def run_command(cmd):
try:
r = subprocess.run(cmd, shell=True, cwd="zadatak",
capture_output=True, text=True)
return r.stdout # BAD: we swallow stderr and errors
except Exception:
return "" # BAD: the model can't tell it broke
Sa ovom verzijom, kad komanda pukne, model dobija prazan string i nema pojma šta se desilo — pa ponavlja istu grešku. Pouka: uvek vrati jasnu poruku greške u kontekst; greška je informacija, ne smetnja.With this version, when a command fails the model gets an empty string and has no idea what happened — so it repeats the same mistake. Lesson: always return a clear error message into the context; an error is information, not noise.
Dobar agent nije onaj koji nikad ne pogreši, nego onaj čija petlja vidi grešku i oporavi se. Zato namerno lomljenje uči više od uspešnog prolaza.A good agent isn't one that never errs, but one whose loop sees the error and recovers. That's why breaking it on purpose teaches more than a clean run.
Lekcija 09 · Više alataLesson 09 · More tools
Drugi alat: kad agent počne da biraA second tool: when the agent starts choosing
Pravo agentsko ponašanje počinje kad model ima više alata i sam mora da odluči koji da upotrebi. Dodajmo odvojene alate za čitanje i pisanje fajla.Real agentic behavior begins when the model has several tools and must decide which to use. Let's add separate tools for reading and writing a file.
alati.py
tools = [
{
"name": "citaj_fajl",
"description": "Vrati sadržaj fajla iz fascikle zadatak/.",
"input_schema": {"type": "object",
"properties": {"putanja": {"type": "string"}},
"required": ["putanja"]},
},
{
"name": "pisi_fajl",
"description": "Upiši sadržaj u fajl u fascikli zadatak/ (prepisuje).",
"input_schema": {"type": "object",
"properties": {"putanja": {"type": "string"},
"sadrzaj": {"type": "string"}},
"required": ["putanja", "sadrzaj"]},
},
{
"name": "pokreni_testove",
"description": "Pokreni pytest i vrati izlaz.",
"input_schema": {"type": "object", "properties": {}},
},
]
def izvrsi_alat(ime, ulaz):
if ime == "citaj_fajl":
return open(f"zadatak/{ulaz['putanja']}").read()
if ime == "pisi_fajl":
open(f"zadatak/{ulaz['putanja']}", "w").write(ulaz["sadrzaj"])
return "Upisano."
if ime == "pokreni_testove":
r = subprocess.run("pytest -q", shell=True, cwd="zadatak",
capture_output=True, text=True)
return r.stdout + r.stderr
return f"Nepoznat alat: {ime}"
tools = [
{
"name": "read_file",
"description": "Return the contents of a file in the zadatak/ folder.",
"input_schema": {"type": "object",
"properties": {"path": {"type": "string"}},
"required": ["path"]},
},
{
"name": "write_file",
"description": "Write contents to a file in the zadatak/ folder (overwrites).",
"input_schema": {"type": "object",
"properties": {"path": {"type": "string"},
"content": {"type": "string"}},
"required": ["path", "content"]},
},
{
"name": "run_tests",
"description": "Run pytest and return the output.",
"input_schema": {"type": "object", "properties": {}},
},
]
def run_tool(name, args):
if name == "read_file":
return open(f"zadatak/{args['path']}").read()
if name == "write_file":
open(f"zadatak/{args['path']}", "w").write(args["content"])
return "Written."
if name == "run_tests":
r = subprocess.run("pytest -q", shell=True, cwd="zadatak",
capture_output=True, text=True)
return r.stdout + r.stderr
return f"Unknown tool: {name}"
U petlji sada samo pozivaš izvrsi_alat(blok.name, blok.input) umesto jedne fiksne funkcije. Model sam odlučuje redosled: prvo citaj_fajl da vidi bug, pa pisi_fajl da ga popravi, pa pokreni_testove da proveri. Tu se rasuđivanje najbolje vidi.In the loop you now just call run_tool(block.name, block.input) instead of one fixed function. The model decides the order itself: first read_file to see the bug, then write_file to fix it, then run_tests to check. That's reasoning in action.
Dizajn alataTool designDobar opis alata je pola posla.A good tool description is half the battle.Model bira alat na osnovu description polja — piši ga jasno, kao uputstvo kolegi. Loš opis = pogrešan izbor alata.The model picks a tool based on its description field — write it clearly, like an instruction to a colleague. A vague description means the wrong tool gets chosen.
Ovo je tačno arhitektura koju koristi Claude Code: desetine alata (čitaj, piši, grep, bash, edit…), a model bira koji i kojim redosledom. Sledeća lekcija pokazuje kako se to skalira.This is exactly the architecture Claude Code uses: dozens of tools (read, write, grep, bash, edit…), with the model choosing which and in what order. The next lesson shows how that scales.
✓ CheckpointCheckpointPokreni agenta sa oba alata i zapiši redosled poziva. Skoro uvek ide citaj_fajl pa pisi_fajl — model prvo gleda, pa menja. Ako krene od pisanja, skrati opis alata za pisanje i pojačaj opis alata za čitanje; redosled poteza podešavaš opisima, ne kodom petlje.Run the agent with both tools and note the order of the calls. It's nearly always read_file then write_file — the model looks before it changes. If it starts with writing, trim the description of the write tool and strengthen the read one; you tune the order of moves through descriptions, not through the loop's code.
Lekcija 10 · ObrasciLesson 10 · Patterns
Kad prostoj petlji treba nadogradnjaWhen the simple loop needs an upgrade
Osnovna petlja rešava iznenađujuće mnogo. Kad zafali, biraš jedan od ovih dokazanih obrazaca — svaki je i dalje petlja, samo pametnije uređena.The basic loop solves surprisingly much. When it falls short, you reach for one of these proven patterns — each is still a loop, just organized more cleverly.
ReActmisli + delujthink + act
Model naizmenično obrazlaže („mislim ovako, jer…") pa radi. Kad rasuđivanje ispiše, potezi su bolji i lakše se debaguju.The model alternates between reasoning out loud ("I think this, because…") and acting. Written reasoning makes moves better and easier to debug.
Plan-and-executeplan pa izvršenjeplan then run
Prvo napravi ceo plan, pa ga izvršava korak po korak. Bolje za duge zadatke gde se lako izgubi nit.First build the whole plan, then execute it step by step. Better for long tasks where it's easy to lose the thread.
Reflectionsamokritikaself-critique
Poseban korak gde agent kritikuje sopstveni rad pre nego što ga proglasi gotovim. Hvata greške koje bi inače prošle.A dedicated step where the agent critiques its own work before declaring it done. Catches mistakes that would otherwise slip through.
Verifier loopnezavisni sudijaindependent judge
Drugi model (ili isti sa drugim zadatkom) proverava rezultat prvog. Ako obori — nazad u petlju. Snažno protiv „ubedljivo ali pogrešno".A second model (or the same one with a different job) checks the first one's result. If it rejects — back into the loop. Strong against "convincing but wrong."
Multi-agentviše petljimany loops
Više agenata sa svojim petljama radi paralelno ili u lancu — jedan istražuje, drugi piše, treći proverava. Skalira, ali dodaje složenost.Several agents with their own loops work in parallel or in a chain — one researches, one writes, one checks. It scales, but adds complexity.
Ne komplikuj preranoDon't over-engineerPočni uvek od najproste petlje sa dobrom verifikacijom. Obrazac dodaj tek kad konkretno vidiš gde prosta petlja puca — ne unapred „za svaki slučaj".Always start with the simplest loop plus solid verification. Add a pattern only once you concretely see where the simple loop breaks — not upfront "just in case."
Lekcija 11 · U praksiLesson 11 · In practice
Claude Code je tačno ova petljaClaude Code is exactly this loop
Alat koji možda već koristiš nije magija — to je petlja iz ovog tutorijala, samo sa mnogo alata, dobrom verifikacijom i pažljivim kočnicama.The tool you may already use isn't magic — it's the loop from this tutorial, just with many tools, solid verification, and careful brakes.
PercepcijaPerceptionTvoj prompt + stanje repozitorijuma.Your prompt + the repo state.Fajlovi, izlaz komandi, istorija razgovora — sve ulazi u kontekst svakog kruga.Files, command output, conversation history — all of it enters the context each round.
AlatiToolsRead, Edit, Write, Bash, Grep, Glob…Isti princip kao tvoj izvrsi_alat, samo bogatiji skup.The same principle as your run_tool, just a richer set.
RasuđivanjeReasoningModel bira koji alat i kojim redosledom.The model picks which tool and in what order.Pročita fajl, pokrene test, popravi, ponovo pokrene — sam vodi tok.Read a file, run a test, fix, run again — it drives the flow itself.
VerifikacijaVerificationPokreće tvoje testove i lintere.It runs your tests and linters.Ne veruje sebi na reč — proverava rezultat, baš kao u lekciji 06.It doesn't take itself at its word — it verifies the result, just like in lesson 06.
KočniceBrakesPotvrde pre opasnih akcija, granice.Confirmations before risky actions, limits.Pita pre nepovratnih poteza — to je odgovor na prejaki alat iz lekcije 05, rešen dozvolom umesto zabranom.It asks before irreversible moves — the answer to the over-powered tool from lesson 05, solved by permission rather than prohibition.
Sad kad si sam sklopio petlju, unutrašnjost Claude Code-a ti više nije crna kutija. Kad ti agent nešto pogrešno uradi, znaćeš da pitaš: fali li verifikacija? je li opis alata loš? je li kontekst prerastao prozor?Now that you've assembled the loop yourself, the inside of Claude Code is no longer a black box. When an agent does something wrong, you'll know what to ask: is verification missing? is a tool description poor? did the context outgrow the window?
To je najveća vrednost ovog znanja: ne da napraviš svog agenta od nule svaki put, nego da razumeš i kontrolišeš agente koje već koristiš.That's the biggest payoff of this knowledge: not to build your own agent from scratch every time, but to understand and control the agents you already use.
Da li se petlja piše ručno i u produkcijiDo you hand-write the loop in production tooKratko: najčešće ne. Postoje gotova rešenja koja ovu petlju pišu umesto tebe — od pomoćnika u samom SDK-u do celog harnesa sa ugrađenim alatima. Ovde je pišemo rukom namerno, jer dok je ne sklopiš sam, svaki gotov alat ti je crna kutija. Šta se od toga bira i kada, razloženo je na kraju 3. dela.Short answer: usually not. There are ready-made options that write this loop for you — from a helper inside the SDK itself to a full harness with built-in tools. We write it by hand here on purpose, because until you've assembled it yourself every ready-made tool is a black box. Which one to pick and when is laid out at the end of Part 3.
Lekcija 12 · KrajLesson 12 · The end
Zaključak i šta daljeWrap-up and next steps
Prošao si put od „prompt je jedan pokušaj" do radnog agenta koji sam popravlja kod i proverava svoj rad. Evo šta dalje da uradiš da ti znanje ostane.You've gone from "a prompt is a single shot" to a working agent that fixes code and verifies its own work. Here's what to do next so the knowledge sticks.
Sklopi agenta iz lekcije 05 do krajaBuild the agent from lesson 05 end to endi pusti ga da popravi zadatak/mod.py. Gledaj svaki korak petlje u terminalu.and let it fix zadatak/mod.py. Watch each step of the loop in the terminal.
Dodaj verifikaciju iz lekcije 06Add the verification from lesson 06i uveri se da staje tek kad su testovi zeleni, ne kad model kaže.and make sure it stops only when the tests are green, not when the model says so.
Uradi sva tri eksperimenta lomljenjaDo all three breaking experimentsiz lekcije 08 — tu se znanje zaista slegne.from lesson 08 — that's where the knowledge really settles.
Zameni zadatak svojimSwap in your own task— daj agentu pravi bug iz tvog projekta i vidi dokle stigne.— give the agent a real bug from your project and see how far it gets.
Probaj jedan napredni obrazacTry one advanced pattern— najlakši je reflection: dodaj korak „iskritikuj svoje rešenje pre nego što kažeš gotovo".— the easiest is reflection: add a step that says "critique your solution before you declare it done."
Jedna rečenica za ponetiOne sentence to take awayInteligencija agenta ne dolazi iz savršenog prompta, nego iz strukture petlje:An agent's intelligence doesn't come from the perfect prompt, but from the structure of the loop:jasan cilj, pravi alati, tvrda verifikacija i sigurne kočnice.a clear goal, the right tools, hard verification, and safe brakes.
Kad ti je petlja jasna, nastavi na Agenta bez nadzora (memorija, ograde, evali, observability) pa na Agenta kao proizvod (pravi alati, MCP, agent kao servis).Once the loop makes sense, continue to The Unattended Agent (memory, guardrails, evals, observability) and then The Agent as a Product (real tools, MCP, the agent as a service).
Ceo kod iz ovog tutorijala — agent.py, primer zadatka i uputstvo za pokretanje — je na GitLab-u: gitlab.com/webmaric/tutorials (folder deo-1-agentske-petlje/). Pročitaj kod pre pokretanja i radi u venv-u.All the code from this tutorial — agent.py, the sample task, and run instructions — is on GitLab: gitlab.com/webmaric/tutorials (the deo-1-agentske-petlje/ folder). Read the code before running and work in a venv.