Co smyčka dělá a kdo za co odpovídá
Agent, který volá nástroje, v jádru není nic magického: je to cyklus. Podle návodu OpenRouteru smyčka znovu a znovu posílá modelu konverzaci i seznam dostupných nástrojů, provádí volání, která si model vyžádal, přidává výsledky do historie a ptá se, co dál. Končí, když model žádné další volání nepožaduje nebo když zasáhne podmínka zastavení, kterou určila aplikace. Rozdělení rolí je přitom jasné: model vybírá nástroj, ale argumenty zpracovává, funkci spouští a o zastavení rozhoduje aplikace. Návod to předvádí v TypeScriptu s OpenRouter SDK.

Ukázka pracuje s lokálními daty o počasí, takže k vyzkoušení nepotřebujete žádnou další službu. Důležitá je drobnost, na které se snadno chybuje: definice nástrojů se mají posílat v každém požadavku, i v navazujících po vrácení výsledků. Každý výsledek navíc nese původní toolCallId, podle kterého model pozná, na které volání odpovídá, a na každé volání připadá jeden výsledek. V první iteraci příklad vynucuje nástroj hodnotou toolChoice „required“, aby se vždy prošla tato cesta. Dál používá „auto“, jinak by model nemohl skončit textem.
Pojistky proti zacyklení a chybám
Nejde jen o šťastný scénář. Neplatný JSON v argumentech, neznámý název funkce nebo selhání handleru se podle návodu nemají projevit pádem smyčky, ale vrátit modelu jako výsledek nástroje. Model pak může upravit argumenty, zvolit jiný nástroj, nebo selhání vysvětlit uživateli. Jinak je to u chyb na úrovni požadavku, tedy u autentizace, sítě a podobných potíží. Ty mají smyčku opustit, aby je mohla zpracovat okolní aplikace. Rozlišení tak odděluje chyby, které model dokáže opravit, od těch, které řešit nemůže.
Limit počtu iterací je jediná tvrdá pojistka. Kontroluje se po odpovědi modelu a před spuštěním nástrojů. Důvod je praktický: výsledek nástroje se uplatní až v dalším požadavku, takže nástroje spuštěné na poslední povolené iteraci by měly vedlejší účinky, které by model už nikdy neviděl ani nemohl popsat. Funkce runAgent() proto odmítne limit, který není kladné bezpečné celé číslo. Hodnoty jako 0, 1.5 nebo NaN by se s počítadlem nikdy nerovnaly a smyčka by běžela, dokud model žádá nástroje.
Druhá pojistka míří na uvázlého modelu, který pořád dokola volá totéž. Smyčka z názvu nástroje a řetězce argumentů spočítá otisk a po třetím shodném volání běh zastaví. Podle návodu tři opakování dovolí jeden opakovací pokus a model, který se zasekl, se přesto zastaví rychle. Autoři zdůrazňují, že práh i výchozí hodnota maxIterations jsou rozhodnutí aplikace, nikoli výchozí nastavení OpenRouteru. Tyto hodnoty tedy patří mezi věci, které si musí každý vývojář zvolit sám.
Ukázkový běh a paralelní volání
Autor uvádí výstup jednoho běhu s modelem google/gemini-3-flash-preview. V první iteraci vrátil model dvě volání get_weather najednou a smyčka je provedla paralelně; ta trvala 3930 milisekund. Ve druhé iteraci už model nástroje nežádal, smyčka vrátila text a trvala 1417 milisekund. Závěr zněl, že Lagos je teď teplejší než Londýn. Autor ale výslovně varuje, že váš běh se může lišit počtem iterací, formulací odpovědi i latencí. Jde tedy o jedno předvedení, nikoli o měření spolehlivosti.

Paralelní spouštění přes Promise.all() je v ukázce v pořádku, protože obě vyhledávání počasí jsou na sobě nezávislá. Návod ale radí volání spouštět postupně, pokud jste neověřili, že nástroje nesdílejí stav. Typický příklad: zápis a následné čtení téhož záznamu nesmějí běžet souběžně, jinak by čtení mohlo vrátit starý stav. Rychlost z paralelismu se tak vyplatí jen tam, kde si volání navzájem nepřekážejí.
Záložní modely a růst historie
Parametr models přijímá seřazený seznam. Podle OpenRouteru se zkusí další model, když první vrátí chybu, včetně překročení limitů rychlosti, výpadku poskytovatele nebo odmítnutí moderací. Smyčka pak z result.model zjistí, který model skutečně odpověděl. K tomu firma uvádí funkci Auto Exacto, která běží ve výchozím stavu u každého požadavku s nástroji. U modelů s více poskytovateli je řadí podle propustnosti, úspěšnosti volání nástrojů a výsledků vlastního benchmarku místo ceny. Kdo dává přednost zásahům do cache, může ji vypnout přes provider.sort „price“ nebo variantou :floor.

Poslední praktický problém je růst historie. Zprávy se s každým požadavkem posílají znovu, takže velké výsledky nástrojů ji rychle nafouknou. Návod radí vybrat jen pole, která model potřebuje, obsah zkrátit, nebo uložit celý objem jinde a vrátit odkaz. Zkrácený výsledek má zůstat platný, například jako JSON s náhledem, původní velikostí a příznakem zkrácení, ne jako řetězec useknutý uprostřed. Text je návod výrobce platformy a opírá se o jednu ukázku; nedokládá, jak často smyčka v produkci selže, ani nesrovnává modely.

Grafy a schéma pocházejí z původního článku uvedeného ve zdrojích.
Zdroje
- Build a Reliable Tool-Calling Agent Loop on OpenRouter — OpenRouter Blog
Článek vznikl s pomocí AI; podklady a odkazy jsou uvedeny výše.