Files
nanobot-runtime/plans/flight-search-skill.md
2026-07-22 12:32:23 +02:00

6.1 KiB
Raw Blame History

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 35 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

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 <search_id> Vypíše top N výsledků seřazených podle ceny
list-searches Seznam všech hledání
delete-search <id> 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 <search_id> a prezentuje uživateli top 35

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 <search_id> a ověřit formátování
  7. Ověřit, že flex date kombinace respektují min_stay constraint