- Jazyk: TypeScript / Node.js
- Server: Express.js
- AI Integrace: OpenAI SDK (připojeno na Perplexity API, model
sonar) - HTTP Klient: Native Fetch
- Databáze / Cache: SQLite
- Testy: Jest + Supertest
Pro získání obsahu stránky jsem zvolila hybridní přístup:
- Používám HTTP request (fetch) pro stažení surového HTML textu stránky.
- Důvod: Původně zamýšlená Varianta B (čistý LLM browsing) selhávala u některých českých webů (zejména těch s
iframemenu, např. Menicka.cz). Přímé stažení textu a jeho následné předání do LLM se ukázalo jako nejspolehlivější metoda (RAG pattern).
Jako LLM model je použit Perplexity Sonar.
- Structured Output & Tool Calling:
- Model
sonarv současné době plně nepodporuje nativní parametrtools(při použití vrací chybu400 Bad Request). - Místo toho jsem implementovala Simulated Tool Calling pomocí Structured System Prompting.
- Důvod: Toto alternativní řešení plně nahrazuje funkcionalitu tool callingu:
- Struktura: Definice JSON schématu je vložena přímo do systémové instrukce (
System Message). - Validace: Prompt explicitně vynucuje datové typy (např. "Price must be integer"), čímž splňuje požadavek na normalizaci dat a strukturovaný výstup.
- Struktura: Definice JSON schématu je vložena přímo do systémové instrukce (
- Model
Pro ukládání dat využívám persistentní databázi SQLite (soubor menu.db).
- Klíč: Kombinace
URL+Datum. - Smart TTL Strategie (Invalidace):
- Standardní: Úspěšně stáhnutá data jsou uložena na 24 hodin.
- Short-lived: Pokud je menu prázdné nebo je restaurace zavřená, cache expiruje už za 30 minut (pro případ, že restaurace nahraje menu se zpožděním).
- Invalidace: Staré záznamy jsou automaticky mazány při každém novém requestu.
Aplikace ošetřuje nestandardní situace:
- HTTP Chyby: Rozlišuje mezi nedostupnou stránkou (404), timeoutem serveru a interní chybou.
- Zavřeno / Svátky: LLM detekuje fráze jako "Dnes zavřeno" nebo "Státní svátek" a vrací příznak
is_closed: trues důvodem uzavření.
Před prvním spuštěním je nutné nainstalovat potřebné balíčky:
npm install
Aby aplikace fungovala, musíte nastavit přístup k AI.
- Vytvorte soubor
.envv kořenovém adresáři projektu. - Vložte svůj API klíč:
PERPLEXITY_API_KEY=pplx-vas-klic-zde...
PORT=3000
Otevřete terminál a spusťte:
npm start
Počkejte, dokud se neobjeví zpráva: Server running on http://localhost:3000.
Pro spuštění všech testů zadejte do terminálu:
npm test
Aplikace je navržena tak, aby byla odolná vůči nestandardním situacím:
- Nedostupnost stránky (404/Timeout):
- API rozlišuje mezi chybou klienta (špatná URL) a výpadkem serveru restaurace. Vrací přesné HTTP status kódy.
- Nekonzistentní data:
- Ceny: LLM normalizuje různé formáty ("145,-", "145 Kč") na čistý
integer. - Alergeny: Pokud v textu chybí, vrací prázdné pole (prevence halucinací).
- Chybějící menu pro dnešní den:
- Pokud restaurace menu ještě nenahrála, aplikace nastaví krátkou expiraci cache (30 min), aby se data brzy zkusila stáhnout znovu.
- Zavřeno / Svátky:
- Model detekuje klíčová slova jako "Zavřeno" nebo "Státní svátek" a vrací příznak
is_closed: true.
- Cache Invalidation:
- Smart TTL: 24 hodin pro validní menu, 30 minut pro prázdné/chybné menu.
- Manual Refresh: Parametr
?refresh=truevynutí okamžité smazání cache a nové stažení dat.
- Menu pouze jako obrázek:
- Limitace: Aplikace zpracovává pouze textové HTML. Podpora pro obrázková menu (OCR / Vision LLM) je plánována jako budoucí rozšíření.
Hlavní nevýhoda spočívá v zastaralosti dat po půlnoci. Menu, které bylo uloženo v pondělí dopoledne, by mělo být neplatné v úterý ráno. Pokud vyprší až v úterý v 11:00 (tj. 24 hodin od uložení), uživatelé vidí včerejší nabídku.
Plýtvání kredity: Pokud je restaurace o víkendu standardně zavřená (is_closed: true), API zbytečně každých 30 minut spouští novou drahou analýzu LLM, aby zjistilo, že je stále zavřeno.
Vysoké zatížení: Při vysoké návštěvnosti by každých 30 minut docházelo ke hromadnému zatížení backendu dotazy na všechny restaurace, i když se menu nezměnilo.
Zajištěno použitím parametrizovaných dotazů (db.prepare().run(?)). Tím je efektivně zabráněno vkládání uživatelských dat přímo do SQL syntaxe
Izolace testů : Při spouštění testů se automaticky používá buď in-memory databáze, nebo oddělená testovací instance, aby se zabránilo kolizím dat a zaručila se izolace testů.
Separace prostředí : Konfigurace (API klíče) je striktně oddělena do souboru .env a načítána podle prostředí (Dev/Test/Prod).
Oddělení rolí: Scraped data jsou vždy striktně oddělena do role user, zatímco kritické instrukce jsou v roli system (má vyšší prioritu).
Delimitery: Neprověřená data (pageText) jsou obalena jasnými, nestandardními oddělovači (např. trojitými uvozovkami """..."""). Tyto znaky modelovi signalizují, že text uvnitř je materiál ke zpracování (data), nikoliv příkaz k vykonání.