runtime data
This commit is contained in:
150
plans/flight-search-skill.md
Normal file
150
plans/flight-search-skill.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 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 <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 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 <search_id>` a ověřit formátování
|
||||
7. Ověřit, že flex date kombinace respektují min_stay constraint
|
||||
Reference in New Issue
Block a user