Řízený AI agent v PHP: model plánuje, aplikace kontroluje zápis

Open-source demo odděluje rozhodování LLM od výpočtů a změn stavu. Na skladovém scénáři ukazuje whitelist nástrojů, explicitní právo zápisu a opětovnou validaci těsně před vytvořením návrhu objednávky.

7. října 2026 Redakce aidevelopers.cz

Ukázky AI agentů často končí ve chvíli, kdy model zavolá funkci a terminál vypíše úspěch. Projekt php-llm-agent-demo od Martina Šuránka se soustředí na obtížnější část: kdo smí rozhodnout o změně stavu a která data musí aplikace před zápisem znovu ověřit.

Jde o malý referenční projekt bez aplikačního frameworku a bez externích Composer balíčků. Agent přes OpenAI Responses API kontroluje zásobu produktu a může připravit návrh objednávky. Právě omezený rozsah je jeho výhodou — bezpečnostní hranice nejsou schované pod několika vrstvami abstrakcí.

Model řídí postup, ne doménová pravidla

Agent má k dispozici čtyři nástroje: vyhledání produktu, načtení pravidel pro doplnění zásob, deterministický výpočet a vytvoření návrhu objednávky. Model volí jejich pořadí a rozhoduje, zda potřebuje další informaci. Nemá však přístup k shellu, obecnému souborovému systému ani libovolnému aplikačnímu zápisu.

Registr nástrojů funguje jako whitelist. Neznámé jméno odmítne a zapisující nástroj bez přepínače --allow-write vrátí strukturovanou chybu. Responses API dostává parallel_tool_calls: false, takže se nástroje v jednom kroku nespouštějí souběžně, a smyčka má limit osmi kol. Parametr store: false navíc vypíná ukládání vygenerované odpovědi pro pozdější načtení přes API.

To jsou vynucené vlastnosti programu. Vedle nich obsahuje systémová instrukce pravidla pro chování modelu: výstup nástrojů má považovat za nedůvěryhodná data, návrh smí vytvořit jen na výslovnou žádost a nesmí tvrdit, že akce proběhla, pokud nástroj nevrátil ok: true. Prompt pomáhá model vést, ale oprávnění k zápisu mu sám neuděluje.

Množství 17 nevymyslel model

Přiložený scénář pracuje s produktem USB-HUB-01. Na skladě je 8 kusů, průměrný denní prodej jsou 3 kusy, dodací lhůta 5 dní a bezpečnostní zásoba 10 kusů. Cílovou zásobu počítá PHP:

ceil(3 × 5 + 10) = 25 kusů
25 − 8 = 17 kusů k doplnění

Model může vysvětlit výsledek a zvolit další krok, ale samotný výpočet provádí RestockCalculator. Při dostatečné zásobě vrací nulové množství a informaci, že doplnění není potřeba.

Toto rozdělení je důležitější než použitý model. LLM je vhodný pro interpretaci požadavku a plánování, zatímco cenu, množství, limity nebo nárok uživatele má počítat deterministický kód, který lze otestovat. V produkci by totéž platilo například pro slevy, finanční převody nebo změny přístupových práv.

Zápis má druhou kontrolní bránu

Nejzajímavější detail je schéma nástroje create_purchase_draft. Model mu neposílá počet kusů. Předává pouze SKU a textový důvod. Nástroj znovu načte produkt i pravidla, zopakuje výpočet a odmítne zápis, pokud produkt neexistuje nebo už není potřeba nic doplňovat.

Jde o jednoduchou obranu proti zastaralému rozhodnutí. Mezi prvním načtením dat a požadavkem na zápis se totiž stav mohl změnit. Agent proto nespoléhá na hodnotu, kterou model viděl o několik kroků dříve. Množství 17 přebírá až z nového výpočtu těsně před vytvořením souboru.

Ani povolený zápis neodešle skutečnou objednávku. Vznikne lokální JSON se stavem draft, určený k lidské kontrole. Projekt tím odděluje přípravu akce od jejího nevratného provedení. Pro citlivější operace je takové dvoufázové uspořádání praktičtější než obecná instrukce „nejdřív se zeptej uživatele“.

Co jsme ověřili bez API klíče

Prošli jsme zdrojový kód na revizi df60e446 a spustili přiložené testy v Dockeru s PHP 8.3. Testovací sada skončila hlášením All tests passed. Ověřuje mimo jiné přesný výpočet 17 kusů, nulové množství při dostatečné zásobě, průchod falešnou tool-calling smyčkou, zákaz zápisu bez oprávnění a odmítnutí neznámého SKU.

Test zápisu také kontroluje, že povolená operace skutečně vytvoří návrh se stavem draft a vypočteným množstvím. Živé volání OpenAI API jsme nespouštěli, takže jsme nezávisle neověřili chování konkrétního modelu, spotřebu tokenů ani reakce na síťové chyby. Offline test agentní smyčky používá připravenou náhradu API.

Projekt lze vyzkoušet přes Docker:

docker compose build
docker compose run --rm test
docker compose run --rm agent --trace \
  "Check whether SKU USB-HUB-01 needs restocking and explain the result."

Poslední příkaz vyžaduje OPENAI_API_KEY v prostředí. Vytvoření návrhu navíc vyžaduje --allow-write a výslovný požadavek v zadání.

Kde referenční demo končí

Projekt není hotový produkční framework. Přepínač pro zápis platí pro celý běh procesu; nenahrazuje přihlášení uživatele ani oprávnění ke konkrétnímu produktu. Souborový draft nemá transakci, zámek ani idempotentní klíč, takže souběžné požadavky by mohly vytvořit více návrhů. Opětovná validace zmenšuje okno pro práci se zastaralými daty, ale produkční databáze by stále potřebovala transakci nebo kontrolu verze záznamu.

Minimální HTTP klient také nemá retry strategii, backoff ani streaming. Chybí trvalý auditní log, rozpočty na počet volání a tokeny, jemnější limity jednotlivých nástrojů, skutečný schvalovací workflow a doménové evaluace. README tyto mezery otevřeně uvádí; při převzetí vzoru je důležité nepovažovat několik bezpečných výchozích voleb za kompletní bezpečnostní model.

Kdy agent dává smysl

Pro jedinou pevnou posloupnost „načti, spočítej, ulož“ by obyčejná aplikační služba byla jednodušší a předvídatelnější. Agentní smyčka začíná dávat smysl, když uživatelé formulují různé cíle, dostupné nástroje a potřebné pořadí se mění nebo je nutné reagovat na dílčí chyby.

Hodnota tohoto dema proto není v automatizaci jednoho skladu. Je v názorném rozdělení odpovědnosti: model navrhuje postup, registr omezuje dostupné schopnosti, doménový kód počítá výsledek a zapisující nástroj znovu ověřuje podmínky. To je použitelný základ i mimo PHP — a podstatně bezpečnější výchozí bod než agent s univerzálním přístupem k aplikaci.