# flight-search skill ## Kontext User pravidelně hledá letenky (např. PRG→PTY na Vánoce 2026). Chce automatizovaný skill, který vyhledává letenky přes KAYAK, ukládá výsledky do SQLite a umožňuje dotazování nad historii. Později se napojí na cron pro pravidelné hledání a alertování. ## Rozhodnutí z plánovací fáze - **Zdroj dat**: Jen KAYAK (spolehlivě funguje přes Jina extractor) - **Výstup**: Top 3–5 nabídek v tabulce (aerolinka, trasa, cena, doba letu, přestup) - **Layover**: Max 1 přestup, v tabulce uvést čas přestupu - **DB**: Uložit vše — definice hledání i nalezené výsledky, původní měnu + CZK - **Cron**: Neimplementovat teď, ale DB schema připravit pro rozšíření - **Měna**: Vždy hledat s `curr=CZK`, uložit `price_czk`; pokud je viditelná původní měna, uložit i tu - **Flex data**: ±3 dny, ale kombinace odlet+návrat musí splňovat `min_stay_days` ## Postup ### 1. Vytvořit adresářovou strukturu skillu ``` skills/flight-search/ ├── SKILL.md └── scripts/ └── flight_search.py ``` DB se auto-vytvoří v `db/flight_search.sqlite`. ### 2. DB schema ```sql CREATE TABLE searches ( id INTEGER PRIMARY KEY AUTOINCREMENT, origin TEXT NOT NULL, -- IATA kód (PRG) destination TEXT NOT NULL, -- IATA kód (PTY) dep_date TEXT NOT NULL, -- ISO date (2026-12-21) ret_date TEXT NOT NULL, -- ISO date (2027-01-04) adults INTEGER NOT NULL DEFAULT 1, max_layovers INTEGER NOT NULL DEFAULT 1, min_stay_days INTEGER NOT NULL DEFAULT 1, flex_days INTEGER NOT NULL DEFAULT 0, currency TEXT NOT NULL DEFAULT 'CZK', status TEXT NOT NULL DEFAULT 'active', -- active, completed, failed created_at TEXT NOT NULL, completed_at TEXT ); CREATE TABLE results ( id INTEGER PRIMARY KEY AUTOINCREMENT, search_id INTEGER NOT NULL REFERENCES searches(id), airline TEXT NOT NULL, route TEXT NOT NULL, -- "PRG→AMS→PTY / PTY→AMS→PRG" dep_date TEXT NOT NULL, -- skutečný datum odletu (může se lišit od search.dep_date při flex) ret_date TEXT NOT NULL, dep_time TEXT, -- čas odletu arr_time TEXT, -- čas příletu layovers INTEGER NOT NULL DEFAULT 0, layover_info TEXT, -- "AMS 2h 15m" duration TEXT, -- celková doba letu "12h 30m" price REAL, -- cena v původní měně price_currency TEXT NOT NULL DEFAULT 'CZK', price_czk REAL NOT NULL, -- cena v CZK booking_url TEXT, found_at TEXT NOT NULL ); CREATE INDEX idx_results_search ON results(search_id); CREATE INDEX idx_results_route ON results(route); ``` ### 3. CLI skript `flight_search.py` Podpříkazy: | Příkaz | Popis | |--------|-------| | `create-search` | Vytvoří záznam hledání, vrátí `search_id` + seznam KAYAK URL k fetchovat | | `add-result` | Přidá jeden výsledek k hledání | | `add-results` | Přidá víc výsledků najednou (JSON ze stdin) | | `results ` | Vypíše top N výsledků seřazených podle ceny | | `list-searches` | Seznam všech hledání | | `delete-search ` | Smaže hledání + výsledky | **`create-search`** generuje KAYAK URL: - Základní URL: `https://www.kayak.com/flights/{origin}-{destination}/{dep_date}/{ret_date}/{adults}adults?sort=price_a&curr={currency}` - S flex dny: generuje všechny platné kombinace (dep ± flex, ret ± flex, ret - dep >= min_stay_days) - Max 15 URL (cap na rozumný počet requestů) - Strategie: středový datum pár první, pak expanduje - Výstup: JSON s `search_id` a `urls` pole **`add-result`** přijímá parametry jako CLI flagy: ``` flight_search.py add-result --search-id 1 --airline "KLM" --route "PRG→AMS→PTY" ... ``` **`results`** formátuje tabulku: ``` #1 KLM PRG→AMS→PTY / PTY→AMS→PRG 12h30m 1 stop (AMS 2h15m) 15 420 CZK #2 TAP PRG→LIS→PTY / PTY→LIS→PRG 14h10m 1 stop (LIS 1h45m) 16 200 CZK ``` ### 4. SKILL.md Skill je **agent-driven** (jako deep-research). Agent: 1. Získá parametry od uživatele (origin, destination, dates, adults, flex, min_stay, max_layovers) 2. Zavolá `create-search` → získá search_id a seznam URL 3. Pro každou URL zavolá `web_fetch` 4. Z fetched markdown parsuje letové výsledky (aerolinka, trasa, časy, cena, přestupy) 5. Uloží výsledky přes `add-result` 6. Zavolá `results ` a prezentuje uživateli top 3–5 SKILL.md obsahuje: - Kdy se skill aktivuje ("hledej letenky", "flight search", "letenky do X") - Postup orchestration (krok za krokem) - KAYAK URL konstrukce (včetně flex data strategie) - Parsing guidance (co hledat v markdown výstupu KAYAKu) - Formátování výstupu pro uživatele - Command reference pro `flight_search.py` ### 5. KAYAK URL parametry k ověření Při implementaci ověřit: - Filter na max 1 přestup: pravděpodobně `fs=stops=0` (0 additional stops), ale nutno testovat - Flex dates: zda KAYAK má nativní flex parameter (např. `flexible_dates=3`), což by snížilo počet requestů - Pokud nativní flex nefunguje, použít strategii generování kombinací z bodu 3 ### 6. CZK přepočet - KAYAK URL vždy s `curr=CZK` → ceny rovnou v CZK - `price_czk` = cena z KAYAKu - `price_currency` = CZK - Pokud by se později přidaly zdroje v jiné měně, přidat exchange rate fetch (např. z ČNB API) ### 7. Cron příprava (neimplementovat) DB schema je připraveno pro rozšíření: - Přidat tabulku `watchers` s threshold cenou a notifikačním kanálem - Přidat flag `is_watched` do `searches` - Cron job by volal `create-search` + fetch + `add-result` + kontrola threshold ## Ověření 1. Vytvořit skill adresář a skript 2. Spustit `create-search --origin PRG --destination PTY --dep 2026-12-21 --ret 2027-01-04 --adults 3 --flex 3 --min-stay 14 --max-layovers 1` 3. Ověřit, že generované URL jsou platné KAYAK odkazy 4. Fetchnout jednu URL přes `web_fetch` a ověřit, že vrací letové výsledky 5. Uložit výsledek přes `add-result` a ověřit v DB 6. Zavolat `results ` a ověřit formátování 7. Ověřit, že flex date kombinace respektují min_stay constraint