Kako napraviti AI sistem koji radi dok spavaš

Autonoman sistem treba da izgleda dosadno. Kad ContentStudio radi kako treba, ujutru u logovima nema ničega što bi me probudilo: pozivi ka spoljnim API-jevima koji povremeno padnu se ponove, sistem nastavi dalje i niko ne mora da reaguje. Ispod je anatomija onoga što sam naučio gradeći ga, bez magije i bez demo-efekta.
Prvo odluči da li ti uopšte treba agent
Većina "autonomnih" sistema ne treba agenta, treba joj workflow. Anthropic u tekstu Building effective AI agents pravi jasnu arhitektonsku razliku: workflows su tokovi sa unapred definisanim putanjama, a agenti sami odlučuju o sledećem koraku. Sve zajedno zovu "agentic systems", ali se ponašaju drugačije u produkciji.
Moje pravilo: ako možeš da nacrtaš dijagram toka pre nego što sistem krene, to je workflow. Agenta ostavljam za korake gde se podzadaci stvarno ne mogu predvideti unapred. Isti tekst navodi upravo to kao slučaj za orchestrator-workers obrazac.
Pipeline koji istražuje, piše, optimizuje i objavljuje mogu da razložim na jasne korake. Slobodu dajem samo na mestima gde je potrebna, na primer pri istraživanju teme, gde ne znam unapred koliko izvora će trebati. Sve ostalo je običan kod sa LLM pozivima na tačno određenim mestima. Takav sistem je jeftiniji, lakši za debagovanje i, što je najvažnije za spavanje, predvidiv.
Anatomija: orchestrator, radnici i sudija
Pipeline koji radi bez nadzora na kraju liči na tri uloge, bez obzira na framework.
| Uloga | Šta radi | Šta sme da pokvari |
|---|---|---|
| Orchestrator | Deli posao, drži stanje, odlučuje šta sledeće | Samo redosled, nikad sadržaj |
| Worker | Jedan uzak zadatak (istraživanje, pisanje, SEO provera) | Samo svoj izlaz |
| Judge | Proverava izlaz po jasnim kriterijumima | Ništa, samo propušta ili vraća |
Orchestrator-worker obrazac je isti koji Anthropic opisuje u svom Research sistemu: glavni agent koordinira i delegira specijalizovanim subagentima koji rade paralelno (How we built our multi-agent research system). Judge uloga odgovara evaluator-optimizer toku, za koji Anthropic kaže da je posebno efikasan kad postoje jasni kriterijumi evaluacije i kad iterativno poboljšanje donosi merljivu vrednost.
Tu je ključna reč "jasni kriterijumi". Judge koji pita "da li je ovo dobro?" je beskoristan. Judge koji proverava konkretne stvari (da li tekst ima traženu strukturu, da li se pominju samo činjenice iz ulaznog paketa, da li je naslov u zadatoj dužini) je koristan, i većinu njegovih provera možeš da uradiš običnim kodom, bez LLM-a. LLM sudiju čuvam za ono što se ne da proveriti pravilom.
Napomena o otvorenom sporu: paralelni agenti sa zajedničkim kontekstom nisu rešeno pitanje. Postoje ozbiljne kritike (Cognition ima tekst "Don't Build Multi-Agents") koje se razilaze sa Anthropicovim pristupom. Ja sam zato konzervativan: paralelizam koristim samo tamo gde su zadaci stvarno nezavisni, a kad delovi moraju da se slažu međusobno, ide jedan agent sa jednim kontekstom.
Raspoređeni radnici: ko ih budi i šta pamte
Sistem koji radi dok spavaš mora da ima nešto što ga pokreće bez tebe. Tipičan obrazac su zakazani radnici (scheduler okida, worker preuzima posao iz reda, radi, upisuje rezultat). Tri odluke ovde presuđuju o tome da li ćeš mirno spavati.
1. Stanje živi van procesa. Worker ne sme da pamti ništa što se ne bi preživelo restart. Svaki posao ima zapis u bazi sa statusom (queued, running, done, failed), brojem pokušaja i vremenom poslednje aktivnosti. Ako proces umre usred posla, sledeći ga vidi kao "running, ali bez signala života" i preuzima.
2. Svaki korak je idempotentan. Ako objava jednog teksta može da se desi dvaput zato što se worker restartovao posle objave a pre upisa statusa, jednog dana ćeš imati duplikat uživo. Zato pre svake nepovratne akcije proveravam da li je već urađena, po ključu koji sam generisao na početku posla.
3. Jedan posao, jedan vlasnik. Lease sa istekom: worker "zakupi" posao na ograničeno vreme i produžava ga dok radi. Ako nestane, zakup istekne i posao je opet slobodan.
Ovo je, u suštini, ručno napravljeno ono što platforme za durable execution rade za tebe. Temporal, na primer, opisuje model u kom, ako proces padne, servis predaje posao drugom procesu, koji rekonstruiše stanje i nastavlja od tačke prekida (Understanding Temporal). Pominjem ga jer je dobra referenca za to šta tvoj sopstveni scheduler mora da garantuje, pa makar bio običan cron plus tabela u Postgresu.
Self-healing znači ograničen retry, ne beskonačan
"Self-healing" zvuči kao magija, a u praksi je disciplina oko tri stvari: šta ponavljaš, koliko puta, i šta radiš kad ponavljanje ne pomaže.
Temporal je ovde poučan primer. Neuspela aktivnost se ponavlja uz backoff, a aktivnost čiji worker umre biva ponovo zakazana. Princip dizajna je da neuspeh aktivnosti nikad direktno ne obara ceo workflow (Failure detection). Ali pažnja na podrazumevane vrednosti: podrazumevana retry politika za aktivnosti koristi koeficijent backoff-a 2, uz neograničeno vreme i neograničen broj pokušaja (Temporal product). To je dobar default za bibliotečku funkciju, a loš za pipeline koji svaki pokušaj plaća tokenima. Beskonačan retry LLM koraka je način da ujutru nađeš račun, a ne ispravljen sistem.
Moja pravila za retry u LLM pipeline-u:
- Greške se dele na prolazne i trajne. Timeout, 429 i 5xx su prolazni, pa ide backoff i ograničen broj pokušaja. Neispravan izlaz modela je druga vrsta greške: ponavljanje istog poziva retko pomaže, pa se menja ulaz (kraći kontekst, stroža instrukcija) ili se korak preskače.
- Svaki retry ima plafon, i po broju pokušaja i po ukupnom trošku posla. Posao koji pređe budžet staje i ide u
failed, a ne u još jedan krug. - Neuspeh jednog posla ne sme da zaustavi ostale. Pipeline za više sajtova ne staje zato što je jedan sajt imao loš dan.
MAX_ATTEMPTS = 3
BUDGET_TOKENS = 200_000 # plafon po poslu, podesi prema svom slučaju
def run_step(job, step):
for attempt in range(1, MAX_ATTEMPTS + 1):
if job.tokens_used > BUDGET_TOKENS:
return mark_failed(job, reason="budget")
try:
result = step(job)
if judge(result).passed:
return mark_done(job, result)
job.feedback = judge(result).reasons # menjam ulaz, ne ponavljam slepo
except TransientError:
sleep(2 ** attempt)
return mark_failed(job, reason="max_attempts")
Ovo nije produkcioni kod nego skica ideje: plafon, razlikovanje grešaka i povratna informacija sudije kao novi ulaz.
Sistem koji ne zna da je pokvaren ne spava mirno
Najopasniji kvar nije onaj koji ruši sistem. To je onaj posle kog sistem nastavlja da radi, a proizvodi smeće. Zato autonomni sistem treba da ima tri nivoa nadzora.
Heartbeat. Svaki worker javlja da je živ. Tišina duža od praga je alarm. Ovo hvata zamrznute procese koje retry logika ne vidi.
Provera izlaza, ne samo izvršavanja. "Posao je završen" ne znači "posao je dobar". Za svaki izlaz pratim par jeftinih signala (dužina, struktura, udeo ponovljenog teksta, da li su izvori prisutni). Nagla promena distribucije je znak da se nešto u lancu pokvarilo, često tiho, na primer izmenom na strani provajdera.
Kill switch i karantin. Ako sudija odbije više uzastopnih izlaza ili trošak po poslu naglo skoči, sistem sam sebe pauzira i javlja mi. Bolje je da ujutru nađem pauziran sistem nego devet sati loših objava. Detaljnije o ovome pišem u tekstu o kill switch-evima za agente.
Tu je i petlja koja sistem čini boljim kroz vreme: izmeri, nauči, ciljaj, generiši, objavi. ContentStudio uči iz stvarnih rezultata pretrage, pa sledeći ciklus ciljano ide ka onome što je pokazalo rezultat. Ali samo-učenje ima smisla tek kad je osnova stabilna. Petlja koja uči iz pokvarenih izlaza uči pogrešne stvari.
Šta ne sme da bude hardkodovano: modeli
Jedna praktična stvar koju sam platio iskustvom: modeli se menjaju brže nego što planiraš. Anthropic redovno izdaje nove modele i menja cene (uključujući cenu čitanja iz prompt keša), što možeš da pratiš u release notes. Ako ti je model ID zakucan na deset mesta u kodu, svaka takva promena je nedeljni projekat.
Zato:
- Model ID-jevi su u jednoj konfiguraciji, po ulozi (npr.
judge,writer,router), ne po pozivu. - Routing po težini zadatka: laki, česti zadaci idu jeftinijem modelu, teški sposobnijem. Anthropic isto opisuje kao routing obrazac.
- Pre svake zamene modela puštam zlatni skup primera kroz sudiju i poredim izlaze. Ne verujem benchmarku, verujem sopstvenim podacima.
- Cene i rokove povlačenja modela proveravam na zvaničnoj stranici pre nego što ih ugradim u proračun, jer agregatori nisu uvek tačni.
Ako gradiš na Claude-u, Agent SDK je razuman početak: daje iste alate, agent loop i upravljanje kontekstom kao Claude Code, programabilno u Pythonu i TypeScriptu, uz hooks, subagente, MCP, dozvole i sesije koje se mogu nastaviti (Agent SDK overview). Bivši Claude Code SDK je preimenovan u Claude Agent SDK, što je korisno znati kad tražiš stare tutorijale.
Šta bih ja uradio
Da danas krećem od nule u firmi u Srbiji ili regionu, ovo bi bio moj redosled:
- Počni od workflow-a, ne od agenta. Nacrtaj tok na papiru. Agenta uvedi tek na jedan korak gde je nepredvidivost stvarna.
- Pre prvog LLM poziva napravi red poslova, statuse i idempotentnost. To je dosadan deo koji odlučuje da li ćeš spavati.
- Sudija sa konkretnim kriterijumima od prvog dana. Što više provera možeš da uradiš kodom, bolje.
- Plafon na retry i budžet po poslu. Beskonačno ponavljanje je najskuplja greška koju možeš da napraviš.
- Kill switch pre nego što ti zatreba. Dodaje se za sat vremena, a bez njega se uči na lošem iskustvu.
- Modeli u konfiguraciji, zlatni skup za regresiju. Zamena modela treba da bude promena jedne linije i jedno poređenje.
Brojke o ContentStudiju (koliko dana zaredom radi bez mene, trošak po ciklusu, stopa odbijenih izlaza) namerno ne navodim ovde, jer ne želim da ih zaokružujem iz sećanja. Pratim ih u logovima, i to je upravo poenta: ako ne možeš da pročitaš te brojeve iz sopstvenih metrika, sistem još nije autonoman, samo je neprimećen.
Zatvaranje
Autonoman sistem nije onaj koji nikad ne pada, nego onaj koji pada kontrolisano, oporavlja se sam gde sme i staje sam gde mora. Ako u firmi razmišljaš o AI automatizaciji koja treba da radi bez dežurnog inženjera, rado ću razmeniti iskustva ili pogledati tvoju arhitekturu. Javi se na lazar-milicevic.com/#contact.
Često postavljana pitanja
Da li mi za autonomni AI sistem treba agent ili je dovoljan workflow?
Većini sistema koji se zovu autonomnim dovoljan je workflow, a ne agent. Ako možeš da nacrtaš dijagram toka pre nego što sistem krene, to je workflow sa unapred definisanim putanjama. Agenta ostavljam za korake gde se podzadaci stvarno ne mogu predvideti, na primer pri istraživanju teme kad ne znam koliko izvora će trebati. Workflow je jeftiniji, lakši za debagovanje i predvidiviji u produkciji.
Kako izgleda orchestrator-worker-judge arhitektura AI pipeline-a?
Pipeline bez nadzora se svodi na tri uloge. Orchestrator deli posao, drži stanje i odlučuje šta sledi, a sme da pokvari samo redosled. Worker radi jedan uzak zadatak i može da pokvari samo svoj izlaz, dok judge proverava izlaz po jasnim kriterijumima i samo ga propušta ili vraća. Većinu provera judge-a mogu da uradim običnim kodom, a LLM sudiju čuvam za ono što se ne može proveriti pravilom.
Kako da zakazani radnici prežive pad procesa ili restart?
Stanje mora da živi van procesa, u bazi, sa statusom posla (queued, running, done, failed), brojem pokušaja i vremenom poslednje aktivnosti. Svaki korak treba da bude idempotentan, pa pre nepovratne akcije proveravam da li je već urađena, po ključu generisanom na početku posla. Posao ima jednog vlasnika kroz lease sa istekom: worker ga zakupi na ograničeno vreme i produžava dok radi. Ako nestane, zakup istekne i posao je opet slobodan.
Koliko puta treba ponavljati neuspeli poziv LLM-a u produkciji?
Ograničen broj puta, uz plafon i po broju pokušaja i po ukupnom trošku posla. Podrazumevana Temporal retry politika za aktivnosti ima neograničeno vreme i broj pokušaja, što je loše za pipeline koji svaki pokušaj plaća tokenima. Prolazne greške (timeout, 429, 5xx) rešavam backoff-om, a neispravan izlaz modela ne ponavljam istim pozivom nego menjam ulaz ili preskačem korak. Posao koji pređe budžet staje.
Da li je dobra ideja koristiti više paralelnih agenata sa zajedničkim kontekstom?
To nije rešeno pitanje. Anthropic opisuje orchestrator-worker sistem sa paralelnim subagentima, dok Cognition u tekstu "Don't Build Multi-Agents" iznosi ozbiljne kritike takvog pristupa. Ja sam zato konzervativan i paralelizam koristim samo za zadatke koji su stvarno nezavisni. Kad delovi moraju da se slažu međusobno, koristim jednog agenta sa jednim kontekstom.
Gradiš nešto teško sa AI-jem ili automatizacijom? Otvoren sam za razgovor.
Javi se