GMS Asistent — Changelog  ·  ← chat   admin

Changelog

Všechny podstatné změny projektu GMS Asistent (chatbot pro vyhledávání zboží

na gms.cz). Formát dle Keep a Changelog.

Konvence: každý release má verzi + datum a čas (Europe/Prague). Časy F1–F3
odpovídají git commitům.

[Nevydáno]

Přidáno

mezi prostředími (DEV → PROD) s diffem a ověřením po zápisu; bez --apply

jen náhled. Nastavení je v DB každého prostředí, deploy ho nepřenáší.

Opraveno

hadici" — navrhovaná otázka na prezentaci): plánovač PostgreSQL u EXISTS

s OR v podmínce volil nested loop se 118 M porovnáními. Filtr parametrů

je teď dva korelované poddotazy (produkt / varianta) přes index owner_id

a sémantické hledání s omezeními nejdřív filtruje (MATERIALIZED CTE) a

až pak řadí podle vektoru — jinak by HNSW index po filtru vrátil 0 řádků.

Ověřeno na DEV DB: 1,9 s, stejné výsledky. Nový regresní scénář hlídá

navrhované otázky do 20 s. Cizí review docs/reviews/2026-09-18-search-perf.md.

„stranách", „str." (stejné tvary jako pojistka v pipeline) — 18. 9. test

padal na správné odpovědi.

Přidáno

detail ARCHITECTURE §19:

SMC-AS2201F-02-08A): přesný lookup v DB před LLM, „jen kód" bez

extrakce a hledání, u složeného dotazu produkt na prvním místě, shoda na

variantu vrátí jen tu variantu; regex ověřen na 528 reálných dotazech

(rozměry, normy, jednotky se nechytají); 2 nové regresní scénáře;

otázky a odpovědi (Excel „VIP DEN", 19 unikátních párů), odpověď

doslova bez LLM při shodě (embedding ≥ 0,90 nebo ≥ 0,85 + 2

rozlišující slova; 27/27 na parafrázích), výchozí vypnuto, štítek v

administraci, GET /api/admin/faq/preview;

dotaz, pamatuje si stav, výchozí z administrace (widget_tts_always);

voice-journey.cjs má nový krok;

jiná formulace, běžný dotaz nesepne; přeskočí se, když je režim vypnutý)

a v voice-journey důkaz skutečného přehrávání (stav ⏹ + velikost

streamovaného audia přes Performance API, ~580 kB PCM).

kód, seq scan 104 ms → index); cizí review docs/reviews/2026-09-16-zadani-16-9.md

(7 nálezů, 5 opraveno, 1 vyvrácen, 1 předchozí chování).

Opraveno

škáluje jen do 1,35×, název se zkracuje výpustkou, tlačítka se nezalamují —

✕ dřív vypadlo z obrazovky (ruční mobilní proklik 16. 9.).

četl zbytek skriptu ze stdin — vzdálený krok tiše skončil po prvním docker

příkazu (chytila zkouška obnovy). Tělo vzdálených bloků je ve funkci main

volané na konci. restore_test.sh navíc bere POSTGRES_DB s fallbackem a

hlásí neúspěch vzdáleného kroku.

když odpověď čerpá z úseku katalogu výrobce (obsahuje značku nebo číslo

z něj) a model stranu neuvedl, připojí se "Zdroj: katalog X, strana N v PDF"

z úseků, které model skutečně dostal. Regrese 16. 9. chytila, že

flash-lite instrukci k citaci občas ignoruje (zásada "spolehlivost

deterministicky"). Cizí review docs/reviews/2026-09-16-catalog-citation.md.

výchozí 14 = dosavadní vzhled). Škáluje všechny texty widgetu naráz

(CSS proměnná --k = px/14, 21 velikostí), ne jen zprávy. Přání

zadavatele při předvádění; PROD se nemění, dokud to admin nenastaví.

Cizí review přes AIHub (docs/reviews/2026-09-16-widget-font-size.md):

výšky vstupu a tlačítek se škálují taky.

Vizuální předloha widget-iframe-UI-standalone-.png (DEV) znovu založena —

důvod: DEV má nastaveno 16 px (odchylka 3,45 %), ne změna designu.

Opraveno

sadě běží jako poslední a iframe z DEV se načítal déle (flake 16. 9. 2026,

samostatně 16/16 OK). Jen test, chování aplikace beze změny.

[0.28.0] – 2026-09-16 – hlasová odpověď (IMS #5045, 15. 9.)

Přidáno

odpověď asistenta rovnou přehraje; u psaných dotazů je u odpovědi tlačítko

▶ Přehrát / ⏹ Zastavit (i u historie). U odpovědí s produkty se čte jen

shrnutí („Našel jsem 3 produkty. Nejlépe odpovídá …") — karty zůstávají

na obrazovce; markdown a odkazy se nečtou; delší text se zkrátí po větě

(600 znaků). Rozhodnutí zadavatele 16. 9.: cloud, Google Gemini

(gemini-3.1-flash-tts-preview, hlas Charon; čeština ověřena poslechem),

záloha OpenAI gpt-4o-mini-tts (onyx). Ne hlas prohlížeče: mužský český

hlas má jen část Windows, Android/iOS česky jen ženský.

volajícího (endpoint nejde použít jako TTS libovolného textu); stejné

ověření, limity a Origin kontrola jako hlasový vstup; audio se streamuje

(PCM 16 bit mono 24 kHz, první chunk ~1 s — změřeno na DEV) a nikde

neukládá. Widget přehrává po chuncích přes Web Audio API (mp3 zálohy přes

<audio>). /api/widget/chat a historie nově vrací message_id.

dotazu, výběr hlasu (Charon, Orus, Iapetus, Algenib, Sadaltager).

Cena ≈ 0,1 Kč za přečtenou odpověď (≈450 výstupních tokenů, $10/1M).

vlastní kvóta TTS (nesdílí limit chatu; TTS_RATE_*), LRU cache hotového

audia — opakované přehrání téže odpovědi nestojí nic, AudioContext se

odemyká synchronně v gestu (iOS Safari), PCM explicitně little-endian,

klient bez Web Audio dostane WAV (format: "wav"), spotřeba a cena TTS

do logu i při přerušení, message_id i po idempotentním retry,

klientovi jen stabilní tts_failed, revoke blob URL, ořez po slově,

TTS_FALLBACK_PROVIDER odděleně od chatu.

cache, wav, streaming). Neověřeno na iPhonu/Safari (přehrání ve vloženém okně).

jen identifikátor) už není falešný poplach.

[0.27.0] – 2026-09-16 13:20 – soulad s template: migrace, DB role, brána podle běžícího DEV

Přidáno

e-shopu (DEV kopie i ostrý www.gms.cz jen načtením), ne jen na podvržené;

regression.sh ho pouští, na PROD jen se SHOP_USER/SHOP_PASS

(commit 6b04a92, doplněno zpětně).

Claude + Codex GPT-5.5 + Gemini 3.1 Pro přes AIHub, 20 nálezů, všechny

schváleny k opravě (jen DEV). Tento release je řeší:

Změna prošla cizím review přes AIHub (docs/reviews/2026-09-16-template-soulad.md,

Codex + Gemini, 12 připomínek: 8 potvrzeno a opraveno — mj. CI grep -E -P,

hesla DB rolí s apostrofy, sourcování .env v deployi, savepointy místo

transakcí v migrate — 3 vyvráceny, 1 nepřijata s důvodem).

tests/regression/dev.json + prod.json (widget key a cesta projektu se

doplní z prostředí při běhu), vizuální předlohy

tests/regression/baselines/, výstupy a snímky v gitignorovaném

tmp/regression/ místo /tmp/gms-*. Skill regrese je jen spouštěč.

Test skenu tajemství tests/test_secret_scan.sh je nově zaregistrovaný

v sadě (dosud ho nikdo nepouštěl).

běží právě nasazený release (EXPECTED_VERSION).

v .env.example (chybělo 14), verze v kódu = poslední v CHANGELOGu,

[Nevydáno] existuje.

historií + check-secrets.sh), docker compose config, build image,

blokace .env*/klíčů/velkých binárek (§9.1).

(celé schéma, idempotentní) + 0002_hnsw_indexes.sql; app/migrate.py +

python scripts/migrate.py [--status|--dry-run] (tabulka schema_migrations,

checksum, pg_advisory_lock). Aplikace při startu žádné DDL nespouští —

inline CREATE TABLE IF NOT EXISTS / ALTER … ADD COLUMN z chatlog, widget,

adminsettings, catalog, sync, stock, heureka, extract_docs, embed_products

jsou pryč, migrate.check_schema() jen hlásí, co čeká. deploy.sh migrace

aplikuje před přepnutím releasu a před nimi dělá dump

(backups/pre-migrate-<release>.sql.gz). ingest_partner.py --no-migrate

místo --schema. Vyzkoušeno na kopii DEV DB.

TRUNCATE), gms_migrate (vlastník, DDL), gms_backup (jen SELECT), s

default privileges pro nové tabulky. Compose bere DATABASE_URL a

MIGRATE_DATABASE_URL z .env. DEV přepnuto 16. 9. 2026; PROD až s jeho

deployem (role + .env nejdřív).

ověřena** (scripts/restore_test.sh: dump 112 MB za 19 s, pg_restore 15 s,

počty řádků shodné, migrace na kopii zelené).

jsou kód, ne dump (dump s COPY/INSERT se dál odmítá); ${VAR:?hláška}

není hodnota; heslo v URL se neplete s ${USER:-gms}. Testy 23 → 29.

Změněno

commit, time, errors, layers_ok, base, release; **commit je

commit releasu, který na DEV skutečně běží** (readlink current), ne

lokální HEAD. deploy.sh prod navíc ověří, že DEV právě běží HEAD.

Přepínače sady anglicky: --quick, --modules=, --theme=; obejití

brány FORCE_NO_REGRESSION=1; GMS_ENV=prod místo PROSTREDI (§5.4).

soubor, ne názvy adresářů; rollback jde po historii dozadu a historii za

cílem urizne (druhý rollback nevrátí verzi, ze které se právě uteklo).

Přepnutí current je i při rollbacku atomické (ln -sT + mv -Tf),

drží flock, a retence releasů běží až po zeleném smoke testu.

Deploy, rollback i auto-rollback po smoke jdou jednou cestou

(switch_release).

(zapisoval do chat_logs); místo toho GET /, /changelog (verze

v HTML), /admin/settings, autorizace /api/logs → 401 a ai_ok

z health. E2E chat zůstává na DEV.

sekce pro testovací ENV; lokální compose bez tichého hesla DB

(POSTGRES_PASSWORD:?) a s výchozím refreshem 6:30 jako README (§6.2).

(citovaná strana), ne na slovo „katalog" v textu (commit a06b86c,

doplněno zpětně).

Opraveno

první odpověď po startu trvá déle a falešný FAIL poslal zdravý deploy do

auto-rollbacku (ověřeno: auto-rollback fungoval, .history uříznuta, DEV

zůstal na předchozím releasu). deploy.sh po návratu čeká 8 s před smoke.

do sady nedostaly — první běh psal snímky do /tmp a předlohy zakládal znovu.

Předlohy DEV znovu založeny z 0.27.0 (stejný vzhled, důvod: cesta, ne design).

-n zahodí stdin i s heredocem a krok tiše skončí s exit 0 (první pokus

o nasazení 0.27.0 na DEV: migrace ani přepnutí releasu neproběhly, smoke to

chytil na verzi v health). -n zůstává jen u ssh s přímým příkazem.

Odstraněno

(commit 6d43ceb, doplněno zpětně).

[0.26.2] – 2026-09-11 – hlas končí sám, testuje se doopravdy

Přidáno

(Firefox) nahrává přes MediaRecorder, který sám nepozná konec řeči —

zákazník musel klikat dvakrát. Hlasitost se nově měří přes Web Audio

(AnalyserNode, RMS) a nahrávání se ukončí po 1,5 s ticha; když nikdo

nezačne mluvit, skončí po 6 s. Ruční stop i 60s strop zůstávají.

V Chrome se nemění nic — tam konec řeči pozná SpeechRecognition.

Chromiu se podstrčí skutečná nahrávka jako mikrofon a vypne se Web Speech

API, takže projde celá serverová cesta (MediaRecorder → /api/widget/voice

→ přepis → zpráva → odpověď). Změřeno: auto-stop zkrátil nahrávání

z 48 s na 7 s.

[0.26.1] – 2026-09-10 – nálezy regrese opraveny, sada hlídá i katalogy, hlas a náklady

Opraveno

293 px místo celé obrazovky. Chytila regresní sada hned první běh.

4,34:1, pod limitem WCAG 4,5:1 → #5b6878 (5,19:1).

dotykový cíl a 16px písmo (pod 16px iOS při kliknutí přiblíží celou

stránku); textarea chatu dostala aria-label, přihlašovací pole label for.

(jiné než strana v PDF) — cituje se „strana N v PDF" a prompt to výslovně

žádá.

hlavičku s gradientem viděl jako bílý text na bílé: 21 z 23 nálezů bylo

falešných a skutečné dva se v tom šumu ztratily. Modul teď rozumí

gradientům (bere nejhorší zarážku) a přeskakuje neaktivní prvky, které

WCAG 1.4.3 vyjímá. Modul formulářů kliká jen na viditelný popisek.

Přidáno (rozšíření testů)

a citovat stranu, produkt musí dostat katalogovou stránku), hlas

(přiložená nahrávka → /api/widget/voice → přepis do 15 s pod $0,002)

a rozpočet: průměrná i nejdražší cena dotazu a p95 latence proti

stropu. Katalogový kontext zdvojnásobil cenu odborných dotazů

($0,011 → $0,023) — bez rozpočtu by se to poznalo až na faktuře.

pojistka odmítne a že jde smazat (fixture tests/gui/data/).

zapsat jedinou stránku, přijatý uloží stránky i úseky, PDF bez textové

vrstvy se odmítne s vysvětlením.

[0.26.0] – 2026-09-10 – hlasový vstup, boční panel, katalogy výrobců (IMS #5045)

Tři body ze zadání zákazníka (úkol IMS #5045). Vše proti DEV, PROD nedotčen.

Přidáno

rovnou, aby se při předvádění neklikalo. Rozpoznává primárně prohlížeč

(Web Speech API, cs-CZ, průběžný přepis) — zdarma a okamžitě; loader

proto deleguje na iframe allow="microphone", bez toho prohlížeč mikrofon

v cizím rámu nepustí. Kde API není (Firefox) nebo selže, nahraje se zvuk

a přepíše ho náš server (POST /api/widget/voice, Gemini, záloha

OpenAI). Audio se nikde neukládá; platí allowlist typů, limit

velikosti, rate limit per identita i IP a přepínač v administraci.

Vlastní whisper na VPS jsme změřili a zamítli: medium int8 přepisoval

10 s audia 12,8 s a bral 2,2 GB RAM ze sdílených 7,8 GB, small byl česky

nepoužitelný. Cloud stojí ~$0,0005 za dotaz a VPS nezatíží.

malé. Panel jde přes 75 % šířky (nastavitelné 40–95 %) a celou výšku;

zákazník přepne ikonou v hlavičce a volba se mu pamatuje. Dosavadní

„ukotvení" tím bylo nahrazeno jedním zobrazením s nastavitelnou šířkou,

karty smí mít v panelu 3 sloupce. Na mobilu se nic nemění (fullscreen).

v administraci (nová záložka Katalogy), značku **zadává admin —

z PDF se nehádá**. Indexace běží na pozadí: text stránek → objednací kódy

→ párování s feedem → pojistka → stránky u produktů + úseky s embeddingy.

V odpovědi pak asistent zná parametry, které ve feedu nejsou, a u otázek

dostane odborný kontext s citací („podle katalogu Lüdecke, str. 31").

Katalog může být anglicky, odpověď zůstává česky.

zboží z našeho feedu na aspoň 30 % stran. Odmítnutý katalog jde

přeindexovat, až se produkty do feedu dostanou.

Opraveno

značky (SMC-AF20-F01B-D), katalog je tiskne bez něj (AF20-F01B-D).

Bez odříznutí přesného prefixu vycházel katalog SMC na 0 % shod, přestože

gms.cz 562 SMC produktů prodává; po opravě 56 % stran / 1 899 produktů.

objednacích kódů a na reálném katalogu dával 2 % (kandidátů z n-gramů je

řádově víc než skutečných kódů). Podíl stran se zbožím z feedu je stabilní:

Lüdecke 55 %, SMC 56 %, katalog mimo sortiment 0 %.

[0.25.0] – 2026-09-10 – testování klikáním a regresní sada (podle led-loga)

Do teď měl projekt jen unit testy s mocky. Ty nechytí, že se bublina na

e-shopu nevykreslí, že CSP nepustí iframe, že se po reloadu ztratí historie,

nebo že chatbot 15 dní jede na záložním provideru (CHANGELOG 0.23.0). Nově

čtyři vrstvy, popsané v docs/TESTS.md; všechny běží

výhradně proti DEV (proti ai-gms.elevante.cz se samy odmítnou) a nic

nenasazují — PROD zůstává jen na výslovné „yes".

Přidáno

Playwright): produktová stránka www.gms.cz (Playwright ji podvrhne — jen náš

<script src=…/widget.js>, vše ostatní jde na skutečný DEV, takže CSP

frame-ancestors i kontrola Origin běží naostro) → bublina → „Díváte se

na: PRODUKT" + návrhy → dotaz na tento produkt → hledání s kartami →

reload = historie → Nový chat → mimo obor → mobil → přihlášení přes HMAC

(převzetí anonymní historie, jiné zařízení, špatný podpis odmítnut).

Nic se nevkládá SQL. 24 kontrol, snímky do /tmp/gms-customer-journey.

a správné heslo → změna titulku → skutečný widget ho ukáže hned →

Historie verzí → Obnovit → titulek zpět → preset uložit/smazat → logy

konverzací (sdílené přihlášení, detail) → Odhlásit. Po pádu uklidí přes

API. 20 kontrol.

vícekrokové konverzace proti živému DEV, ověřují se jen deterministické

příznaky v debug a tvrdé filtry (pozdrav bez LLM, off-topic, provozní

dotaz, injection, 2" → DN 47,8–53,8 jen KAMLOK, jen spony, „ta první",

dědění rozměru, srovnání není negace, skladem, chemikálie) + **zdraví AI

vrstvy** u každé odpovědi (žádný fallback, rerank běží, cena > 0, odkazy

jen z dat). ≈ 13 volání LLM, ~$0,15 za běh.

scripts/regression.sh [--rychle|chat|gui|unit]): dostupnost stránek,

autorizace (anonym → /api/logs, /api/admin/*, /api/metrics,

/widget-demo, /widget bez klíče = odmítnuto), formuláře, axe/WCAG,

kontrast, překryvy, výkon proti rozpočtu, vizuální porovnání s předlohou,

historie běhů v regression-history/ (negitováno). Skill dostal podporu

přihlášení bez uživatelského jména (login: null, selektor_uspech).

nasazením; docs/TESTS.md; deploy.sh dev připomene regresi.

Opraveno (nálezy z prvního běhu sady, po odsouhlasení)

produktu** — „hadice na benzín, vnitřní průměr 19 mm" na stránce

PETROTEC 25 vracelo jen tutéž hadici bez karet. Zpráva, která pojmenuje

typ zboží a přidá rozměr, je nové hledání (pipeline.is_new_search,

debug.page_ctx_new_search); otázka na vlastnost bez typu („vydrží

10 bar?") se dál váže k prohlíženému produktu.

Referrer-Policy, HSTS, X-Frame-Options: DENY mimo /widget, kde

framing řídí CSP frame-ancestors). Traefik žádné nepřidával.

/changelog): relativní ARCHITECTURE.md/API.md/CHANGELOG.md vedou

na jejich stránky, placeholdery a soubory bez stránky zůstanou textem

místo odkazu na 404.

Nálezy z prvního běhu (neopraveno, k rozhodnutí)

přibližuje), textarea debug chatu bez popisku, dotykové cíle 27 px.

stavu 1,92:1. (Ostatní hlášení kontrastu jsou artefakty měření —

průhledné pozadí standalone widgetu a JSON stránky.)

[0.24.0] – 2026-09-05 – widget na produkci, zbývající nálezy z review

Přidáno

i WIDGET_ID_SECRET jako DEV, takže podpisy identity z backendu gms.cz

zůstávají platné. WIDGET_ALLOWED_ORIGINS omezené na gms.cz (CSP

frame-ancestors se vynucuje), /widget-demo vypnuté. Historie konverzací

(59 konverzací / 150 zpráv) přenesena z DEV, aby o ni zákazníci nepřišli.

Totéž omezení origins nasazeno i na DEV — do teď tam bylo *, tedy bez CSP

a bez kontroly Origin.

i zboží na objednávku. Nově se ukáže jen skladové; když skladem není nic,

nabídka zůstane (radši něco než prázdno).

vhodnost potvrdit. Detekce je deterministická (kyseliny, louhy, peroxidy,

rozpouštědla…); prompt pak vhodnost potvrdit nesmí a produkty nabídne jen

jako předvýběr.

nebylo vidět, co provoz stojí. Reálná cena za dotaz vyšla $0,0115

(90 % připadá na krok odpovědi).

provider nebo neběží rerank. Veřejně a bez METRICS_KEY, protože

konfigurace Nagiosu je ve veřejném repu a klíč do ní nepatří. Doplněny

dva Nagios checky (PROD i DEV) do vps/nagios/chatbot-nagios.cfg.

Opraveno

mají jen těžkohořlavost či teplotní odolnost, asistent to musí říct.

chránička na vedení elektřiny. Elektroinstalace, potraviny, pitná voda

a ATEX mají vlastní normy; bez opory v datech se náhrada podle tvaru

nebo materiálu nenabízí.

[0.23.1] – 2026-09-04 – tvrdé filtry přestaly lhát

Nálezy z regresních kol zopakovaných s funkčním rerankem + nezávislý review

(GPT-5.5), všechny ověřené proti produkčním datům.

Opraveno

hadice DN 12 a minimálně 300 bar" se vypsala varianta DN 5 za 153 Kč,

jako by požadavek splňovala. Ten produkt v DN 12 na 300 bar nemá žádnou

variantu; filtr variant vrátil prázdno a soft fallback pak ukázal všech 12

variant bez jakéhokoli označení. Nově se rozlišuje „filtrováno" a

„rozvolněno": rozvolněný produkt jde na konec výpisu, přijde bez rozměrů

varianty a s příznakem, že zadané rozměry nesplňuje.

z interního seznamu, ne z vykreslené odpovědi. Odpověď začínala PETROTEC 10,

ale „ta druhá" sáhla po úplně jiném produktu.

gms.cz/nbr-65-…. Odkaz mimo předaná data se opraví podle názvu produktu,

nerozpoznatelný se rozbalí na prostý text (links_fixed v debugu).

DN 100" propouštělo těsnění s DN 80, které má náhodou vnitřní průměr 100 mm.

Aliasy (průměr ↔ DN ↔ JS) jsou pojistka proti různému pojmenování téhož

rozměru, ne povolení zaměnit rozměry; použijí se, jen když produkt parametr

přesného názvu vůbec nemá.

se nabízela spona 32-50, která se s požadavkem dotkne v jediném bodě.

[0.23.0] – 2026-09-04 – Gemini 2.5 retirováno: konec tichého fallbacku

Při přípravě testu thinkingu se ukázalo, že **od 20. 8. 22:49 vracela všechna

volání Gemini 404** („model no longer available to new users"). Pipeline tiše

přešla na FALLBACK_PROVIDER=openai (gpt-4o) a **rerank se 15 dní vůbec

nespouštěl** (359 ze 423 chatů rerank_error: 404). Embeddingy jedou dál,

hledání zasažené nebylo. Cena za dotaz byla $0,019 místo dokumentovaných

~$0,003.

Opraveno

ne ten, který odpověděl; proto výpadek 15 dní nikdo neviděl. Nově obsahuje

skutečně použité modely (rerank: null = rerank neproběhl) a vedle nich

configured.

nebyl v candidates_token_count; s uvažováním podhodnocovala naše cena

spotřebu zhruba 9×.

místo objektu, padal první extracted.get(...) na AttributeError a zákazník

dostal 500. Pole se rozbalí, nesmysl = prázdná extrakce.

Přidáno

rerank_failed a souhrnný ok. Přesně tohle chybělo, aby se výpadek

poskytovatele ohlásil sám. Patří na Nagios.

[0.22.3] – 2026-09-04 – navazující dotazy drží rozměr, zámek rodiny spojek

Nálezy z nezávislého review (GPT-5.5) nad výstupem regresních kol.

Opraveno

„ta druhá — kolik stojí?" po dotazu na 19mm hadici odpovídalo cenou 6mm

varianty (92 Kč místo 257 Kč). Extrakce u follow-upu rozměry nezopakuje, tak

se přebírají z minulého kola, když jde zjevně o totéž téma (shodná hledaná

fráze, odkaz řadovou číslovkou nebo meta-dotaz na cenu/sklad). Změna tématu

omezení nedědí.

krátkého meta-dotazu se varianty dotahovaly BEZ rozměrového filtru, takže se

ukázala cena a parametry jiné varianty.

katalog je píše „KAMLOK" a sémantika je nespojí (s „kamlok" ve frázi se najdou

všechny, s „Camlock" ani jeden). Katalogový tvar se do hledané fráze doplní

deterministicky, stejně jako u synonym médií („kejda" → fekálie).

odpověděl „Kamlockový typ spojky v nabídce nemáme" a nabídl GUILLEMIN,

přestože katalog KAMLOK v DN 50 má. Rodina spojek (kamlok/camlock, GEKA,

Guillemin, Storz, Perrot, Bauer) je nezaměnitelná a filtruje se tvrdě; když

ji opravdu nemáme, seznam se nechá projít, ať může asistent poctivě nabídnout

alternativu.

[0.22.2] – 2026-09-04 – nálezy ze širokého regresního kola

Nová sada 35 dotazů napříč sortimentem (vícekrokové konverzace, palce, negace,

odkazy na dřívější nabídku, kontext stránky, poradní režim).

Opraveno

vyhodnotilo jako požadavek na sponu a „vnitřní" u produktu jako koncovka.

Zároveň teď zabírá česká flexe („gumová" = guma).

nebo PVC?" extrakce vyhodnotila jako exclude=[PVC], filtr vyhodil přesně to,

co měl zákazník porovnat, a odpověď nabídla hadičku na huštění pneumatik.

PVC?" zbylo v hledání jen „gumová hadice" a asistent nabídl hadičku na huštění

pneumatik a asfaltérskou hadici. U srovnávacích dotazů se do hledání přibalí

celá zpráva BEZ srovnávané alternativy — s „PVC" ve frázi vrátila sémantika

zase samé PVC hadice. Nově odpoví „pro horkou vodu je lepší guma, konkrétně

EPDM" a nabídne CALORTEC.

„další průměry nemáme", i když produkt další rozměry má. Do dat odpovědi jde

nově pole dostupne_rozmery = distinct hodnoty rozměrových parametrů napříč

všemi variantami (z dat, nic se nedopočítává).

[0.22.1] – 2026-09-04 – typ zboží jako filtr, deterministická kategorie

Přidáno

spojku, naviják, manometr, tyč nebo trubku, nedostane hadice. Typ se z dotazu

vytěží regexem (ne LLM, takže je stabilní) a porovná s názvem a kategorií

produktu. Pojistky proti přefiltrování: bez pojmenovaného typu se nefiltruje

vůbec a produkt s nerozpoznatelným typem se nevyhazuje. Ověřeno proti

produkčním datům na 20 dotazech napříč sortimentem — u žádného nezmizely

správné výsledky (např. „olejiodolná pryžová deska" a „hydraulická hadice"

20→20), zato „trubka odolná na UV" 20→5 a zbyly hliníkové trubky místo

potravinářského silikonu a stahovacích pásek.

Opraveno

jako „nejčastější mezi produkty", takže se mezi běhy měnila a u smíšených

výsledků byla nesmyslná (koncovky → „Čistící spreje, maziva"). Nově ji

spočítá pipeline; když výsledky pokrývají kategorie roztříštěně (nejčastější

nemá ani polovinu), řádek s kategorií se vynechá úplně.

[0.22.0] – 2026-09-04 – opravy z vyhodnocení reálných konverzací

Analýza 133 reálných dotazů z DEV (kde běží widget z gms.cz) + nezávislý review

Gemini 3.1 Pro a GPT-5.5. Všechny opravy jsou deterministické, v pipeline.

Opraveno

„polyamidová tyč průměr 50 mm" vracel PTFE a EPDM a asistent odpovídal

„nemáme", přestože PA TYČ PA6 SILON Ø50 existuje. U plochého sortimentu

(tyče, desky, pryže, profily, plasty) se průměrové omezení filtruje i přes

Tloušťka (mm); u hadic se nemění nic (tam by šlo o tloušťku stěny).

a desek se nevypisoval (chyběl ve whitelistu klíčových parametrů), takže

asistent tvrdil „5 mm nemáme", i když filtr 5 mm prošel. Whitelist doplněn

o tloušťku, šířku a průměr; filtrovaný parametr jde první a limit ho neuřízne.

25 mm" se ukazovalo „od 48 Kč", což byla cena varianty Ø3 mm. Když varianty

prošly rozměrovým filtrem, cena i rozpětí se počítají jen z nich.

widget_result_format=cards instruovalo model, ať produkty nevypisuje. To

je správně pro widget (karty vykreslí sám), ale platilo to globálně, takže

přes API i v debug UI chodily odpovědi úplně bez produktů. Nově si režim

karet vyžádá volající (widget), API vypisuje produkty vždy.

k večeři?" na stránce hadice dostávalo tip na večeři, protože pojistka

„na produktové stránce nikdy mimo obor" se uplatňovala bezpodmínečně. Nově

jen u dotazu, který na produkt opravdu míří (zájmeno nebo dotaz na vlastnost).

hledalo pořád tutéž hadici, protože extrakce vrátila název prohlíženého

produktu. Nově se hledá podle zprávy zákazníka.

domýšlel („skladem 40,1 ks" u metrážní hadice). Množství se předává jako text

bez jednotky a prompt zakazuje ji doplnit i vypisovat názvy datových polí.

a kolik metrů máte skladem?" po nabídce produktů extrakce občas označila za

mimo obor. V rozjeté diskuzi s nabídnutými produkty se meta-dotaz a otázka

drží v oboru deterministicky.

jednotky doplňoval „kusů“ i u metrážního zboží; do dat odpovědi jde teď jen

u dotazu na sklad/množství.

Přidáno

nabídky", ale přivítání. Řeší se deterministicky, bez volání LLM (a bez ceny).

a glykol mezi výsledky na benzín. Prompt reranku navíc explicitně zakazuje

míchat typy (spojka × hadice) a média.

přepsat na „nehořlavý".

pojmenování rozměru u tyčí, nečíselné hodnoty Průměr (mm) (965 z 1 425),

překlep v kategorii „bezníny" a koncovky zařazené mezi čisticí spreje,

a export tabulky chemických odolností.

[0.21.4] – 2026-08-22 – lišta „Dotaz k produktu" nad rozjetým chatem

Přidáno

objeví se nad zprávami lišta „💬 Dotaz k produktu: NÁZEV" — jedním klikem

otevře nový chat s kontextem daného produktu (rovnou i s návrhy otázek k němu).

V prázdné konverzaci se lišta neukazuje (tam kontext ukáže uvítací obrazovka).

[0.21.3] – 2026-08-22 – dotaz na prohlížený produkt nenabízí cizí zboží

Opraveno

míru?", „jakou má tvrdost?", „je skladem?") už nevypisují cizí produkty —

odpoví se jen k prohlíženému zboží, bez karet (zákazník ho má před sebou).

Alternativy se ukážou, jen když si o ně řekne („máte levnější alternativu?",

„máte ji v jiném průměru?", „jaké tloušťky nabízíte?") — deterministicky.

[0.21.2] – 2026-08-22 – návrhy otázek podle typu produktu

Změněno

(deterministicky z kategorie/názvu, bez LLM volání navíc): hadice → média,

provozní tlak, poloměr ohybu, jiný průměr; pryže → tvrdost a pevnost,

odolnost olejům, tloušťky, řezání na míru; spojky → na jakou hadici, materiál,

tlak; plasty/tyče → rozměry, teplota, obrábění; podlahoviny → protiskluznost,

údržba. Ke každému typu se přidá „Je skladem a kolik stojí?" a „Máte levnější

alternativu?".

{nazev} (doplní název prohlíženého produktu); prázdné pole = automatika.

[0.21.1] – 2026-08-22 – asistent odpovídá NA produkt ze stránky

Opraveno / přidáno

případ z gms.cz): extrakce nově dostává název prohlíženého produktu a navíc

je deterministická pojistka — s kontextem stránky se krátký dotaz vždy váže

na daný produkt (on_topic + hledaný text = jeho název) a produkt se připne

mezi výsledky.

i parametry na ÚROVNI PRODUKTU (materiál, tvrdost, pracovní teplota, pevnost,

tažnost, měrná hmotnost, barva…) — dosud se posílaly jen rozměrové parametry

variant, takže asistent na dotaz „z čeho je?" neuměl odpovědět.

z katalogových listů (PDF/OCR)** + pokyn odpovídat vyčerpávajícím způsobem

z jeho dat (režim B0 v promptu).

[0.21.0] – 2026-08-22 – asistent zná stránku, na které zákazník je

Přidáno

zeptat „*je tato hadice vhodná na naftu?*" bez uvedení názvu. Loader posílá

referenci (data-product / window.gmsWidgetProduct / URL stránky), server ji

použije VÝHRADNĚ jako klíč do naší DB (search.get_by_ref) — nikdy nejde do

promptu; nenalezeno či duplicitní slug = tichý fallback bez kontextu.

ukazovacím zájmenu („tato/tento/je to vhodné") se produkt **připne mezi

výsledky** — deterministicky, ne přes prompt.

s debounce — asistent ví o změně produktu i bez reloadu stránky.

produktové stránky** (admin). Celé vypnutelné v adminu (widget_page_context).

[0.20.0] – 2026-08-21 – styl „velký asistent" + kompaktní karty

Přidáno

webu): bublina zůstává, ale vyskočí velký panel s uvítací obrazovkou (pozdrav +

podtitulek na jemném gradientu), klikacími návrhy otázek (editovatelné

v adminu, 1/řádek, max 6) a plovoucím zaobleným vstupem. Volí se v adminu

(widget_style), klasický chat zůstává výchozí.

Kompaktní karty — v kompaktním režimu LLM odpoví jen 1–3 větami

(+ odkaz na kategorii) a produkty se zobrazí POUZE jako bohaté karty:

obrázek, název, krátký popis, klíčové parametry, cena a dostupnost.

[0.19.1] – 2026-08-21 – refaktor administrace (přehledné záložky)

Změněno

Prompt, Widget, Limity, Historie verzí, Zabezpečení) místo jedné dlouhé stránky;

IA odsouhlasena GPT-5.5 + Gemini. Presety a rychlé vzhledy nahoře v příslušné

záložce, potvrzovací dialogy před použitím/smazáním presetu a obnovou verze.

při neuložené změně (dirty-state, ochrana před odchodem se změnami).

Admin heslo v logu skryté — přihlášení je sdílené, box se ukáže jen když

heslo chybí nebo je špatné.

[0.19.0] – 2026-08-21 – pojmenované presety + verzování konfigurace

Přidáno

(všechna widget_* nastavení): „Uložit jako…", Použít, Smazat; unikátní název

per scope, klíče striktně whitelistované dle scope.

immutable verze (tabulka settings_versions, jedna DB transakce, retence 100);

historie v adminu se změněnými klíči a poznámkou; **„Obnovit" zkopíruje starou

verzi jako novou** — edituje se vždy jen nejnovější. Optimistic lock

(expected_version → 409 při souběhu dvou panelů); stará verze se před obnovou

validuje aktuálním modelem (evoluce schématu).

(localStorage), takže se zadává jen jednou.

[0.18.1] – 2026-08-21 – vzhledy widgetu, dark mode, sloupce karet

Přidáno

zelený, Tmavý jantar) + widget_theme light/dark — widget má kompletní

tmavou paletu (pozadí, bubliny, vstup, karty).

pro počítač a mobil (loader posílá do iframe detekci zařízení).

vrací); uložení nezměněného defaultu se normalizuje na „výchozí", aby budoucí

vylepšení promptu nezůstala zmrazená.

[0.18.0] – 2026-08-21 – admin nastavení: vše konfigurovatelné za běhu

Přidáno

změna hesla z UI; oddělené od DEBUG_PASSWORD, které gate-uje veřejný chat) —

konfigurace se ukládá do tabulky app_settings a platí OKAMŽITĚ bez restartu.

Návrh zreviewován GPT-5.5 + Gemini 3.1 Pro (Pydantic validace všech hodnot

proti CSS/XSS injection, scrypt místo holého sha256, config přes API endpoint).

toggle cena / dostupnost / kategorie / výrobce / PDF listy; prompt (doplňkové

instrukce i úplný override s resetem); widget — barva, pozice L/R, velikost

bubliny, rozměry okna, font, titulek, uvítání, placeholder, patička, auto-open;

produktové karty pod odpovědí (volitelně s obrázky z feedu); rate limity.

(widget.js i widget.html se stylují podle admin nastavení).

[0.17.1] – 2026-08-21 – čerstvost dat v /api/health (sync_fresh)

Přidáno

úspěch i selhání pojistkou); /api/health nově vrací last_sync

(čas, ok, zdroj, stáří v hodinách) a sync_fresh = poslední sync OK

a mladší než SYNC_FRESH_HOURS (default 25 h) — pro vzdálený monitoring

(Icinga content-check "sync_fresh":true).

[0.17.0] – 2026-08-21 – jednotný feed forai.xml (ceny 100 % variant)

Změněno

5 původních feedů. Přináší ceny u 100 % variant (dřív ~26 % přes Heureka

párování), přesné URL produktů i variant, kategorie cestou, FEATURE =

product-level parametry, sklad u všech variant. Návrh migrace zreviewován

GPT-5.5 + Gemini 3.1 Pro.

rollback): soft-delete + upsert produktů a SKU, hromadná náhrada parametrů

(jedna transakce, COPY), FTS rebuild, **re-embedding jen produktů se změněným

textem** (id se nemění → embeddingy a doc_text z PDF OCR se zachovávají).

Pojistky: min. produktů, max. % deaktivací, min. počet parametrů.

ve forai chybí (požadavek na GMS v docs/FEED-REQUIREMENTS.md).

jen pro legacy rollback (ze scheduleru se s forai flow nevolají).

[0.16.0] – 2026-08-20 18:40 – embedovatelný chat widget pro e-shop (bublina)

Přidáno

a získá bublinu vpravo dole s kompletním chatem (static/widget.js loader +

static/widget.html UI v iframe, postMessage handshake s kontrolou originu,

mobilní fullscreen 100dvh + safe-area, lazy-load iframe, přístupnost).

wg_messages): přihlášený uživatel pokračuje v konverzaci i po návratu či na

jiném zařízení, anonym přes id v localStorage; po přihlášení se anonymní

konverzace převedou k účtu. In-memory paměť pipeline se seeduje z DB

(přežije restart). Debug chat_logs běží dál nezávisle.

zapracovány): veřejný site key (WIDGET_KEY) + CSP frame-ancestors

allowlist + kontrola Origin i na API + identity verification à la Intercom

(WIDGET_ID_SECRET, HMAC-SHA256 podpis user_id s kanonickým prefixem —

bez podpisu nelze číst cizí historii).

+ globální denní strop, zámek konverzace proti paralelním zprávám,

idempotence msg_id (retry klienta neplatí LLM dvakrát), limit délky zprávy.

(default 90) při startu aplikace.

v anonymním i přihlášeném režimu. Integrace popsána v docs/API.md §9.

[0.15.1] – 2026-08-20 17:45 – deploy a smoke-test skripty pro CHATBOT VPS

Přidáno

kontrolami: pyflakes + celá pytest suite PŘED deployem (fail = deploy se nekoná),

nasazuje jen commitnutý kód (git archive HEAD), .env na serveru nikdy nepřepisuje,

po deployi automaticky smoke test. **Prod (ai-gms.elevante.cz) vyžaduje interaktivní

potvrzení „yes"** — v souladu s pravidlem, že do produkce se nasazuje jen na výslovný

souhlas (dev ai-gms.elevante.work je default).

(status + database), HTTP→HTTPS 301, expirace certifikátu (≥14 dní) a end-to-end

/api/chat (≥1 výsledek). Exit kód pro použití v automatizaci.

CHANGELOG serveru, compose stacků, zálohy, monitoring); vps/CREDENTIALS.md

je v .gitignore.

[0.15.0] – 2026-07-11 – 5 iteračních regresních kol (40+ konverzací)

Přidáno / opraveno napříč koly 4–8

markdown odkaz na PDF z dat (katalogovy_list_pdf).

se odpovídá přímo (superlativy z dat), bez refine boilerplate (_QUESTION_RE).

kejda/močůvka/splašky → fekálie, hobliny → piliny – embedding zákaznický

termín nespojí, LLM instrukce nestačila.

a rerank množství/cenu/dostupnost při hodnocení ignoruje.

hledaného textu beze změny → pool z příbuzné řady; reference s cenou,

produkt sám se mezi „levnějšími" neuvádí.

náhrada za produkt = on_topic (bez vymyšlených důvodů náhrady);

prompt-injection → off-topic.

„3 nejlepší/možnosti/varianty" mění počet výsledků; multi-item dotaz

(hadice + spony) vedlejší položku neignoruje mlčky.

Vývoj skóre kol (10 konverzací/kolo, stále těžší scénáře)

kolo 3: 4,45 → kolo 4: 4,4 → kolo 5: 4,5 → kolo 6: 4,5 → kolo 7: 4,4 → kolo 8: ~4,6

[0.14.0] – 2026-07-11 – Konverzační inteligence (2 regresní kola × 10 konverzací)

Přidáno / opraveno (kolo 1)

„ta první/druhá/poslední" (resolve_ordinal).

„deska A na B metrů" = plocha, ne tloušťka.

Přidáno / opraveno (kolo 2)

„ta druhá, jakou má cenu?" odpoví cenou konkrétního produktu (dřív „nemáme").

skladem aspoň 20 metrů?" odpovídá reálnými čísly skladu dřív nabídnutých

produktů (skladem_mnozstvi v datech odpovědi).

+ instrukce reranku/odpovědi.

gms.cz místo „není z naší nabídky".

odpověď doporučí ověření s technikem GMS.

[0.13.0] – 2026-07-10 – Reálná skladová dostupnost (sklady.xml)

Přidáno

(30 558 záznamů: onstock/reserved/onstockfrom, klíč = skus.code, 100%

shoda). app/stock.py: stock_qty = max(onstock − reserved, 0),

restock_date. 15 084 SKU reálně skladem (dřív konstantní

DELIVERY_DATE=0 z Heureky u všeho).

„na objednávku" — z reálného skladu; Heureka delivery_days jen fallback

pro SKU bez záznamu; bez dat se vynechá. Pojistka MIN_STOCK_ITEMS.

Ručně: scripts/apply_stock.py.

hlavně ceny per ordercode, URL skupin a integrita exportů (EAN

odstraněn na žádost zadavatele).

[0.12.0] – 2026-07-10 – Kategorie u 99,7 % sortimentu (číselník skupin z XML)

Přidáno

(676 skupin: guid → název, parent) — nikde nedokumentovaný, ale existuje.

app/groups.py: kategorie produktu = nejhlubší z jeho <groups> GUIDů,

cesta průchodem stromu, URL = slug cesty (ověřený vzor). **Pokrytí kategorií

vzrostlo z 2 653 na 5 654 z 5 673 produktů (99,7 %)** — hydraulika, PU

spirály atd. teď mají odkaz na kategorii. Vše z XML (žádný web).

ceny/dostupnost/URL. Denní refresh: feedy → sync → kategorie → enrich.

Pojistka MIN_GROUPS (torzo číselníku nic nepřepíše). Ruční běh:

scripts/apply_categories.py.

ve skupinách; P1 sklad, P2 ceny, P4 integrita ověřeny jako skutečně chybějící.

[0.11.0] – 2026-07-10 – Relevance: palce, aliasy průměrů, rozvolnění, oprava slabých dotazů

Z vyhodnocení 20 testovacích dotazů (16× výborné, 2× slabé) – opravy slabých míst:

Přidáno / opraveno

se z dotazu vytěží regexem a převedou na mm jako range ±6 % (2" → 47,8–53,8);

závity (G 1/2") se ignorují. Nespolehlivý převod LLM se přepíše.

→ „spojka kamlok 2\"" teď vrací skutečné KAMLOKy DN 50 (dřív 1 nesouvisející trn),

„zahradní hadice 1/2\"" vrací AQUATEC ⌀ 12,5–13 (dřív se rozměr zahodil).

na hadici ↔ Průměr ↔ Trubka) – tvrdý filtr už nevyřadí správný produkt jen

kvůli jinému názvu téhož rozměru.

pool se doplní rozvolněnými kandidáty; přesné shody nesou označení, které

rerank preferuje (dřív se rozvolňovalo jen při 0 výsledcích).

u ≤ 2 kandidátů se respektuje (konec rozporu „nemám nic vhodného" + 1 výsledek).

(„polyamidová tyč" → jen PTFE), asistent to řekne na rovinu.

nescrapuje. Chybějící údaje (kategorie mimo Heureku, sklad, ceny části

sortimentu) řeší požadavky na vývojáře GMS — docs/FEED-REQUIREMENTS.md

(sklad a ceny per ordercode, číselník skupin s URL, kontrola integrity

exportu; vše aditivní).

[0.10.0] – 2026-07-10 – Sync katalogu (soft-delete) + pojistky proti vadnému exportu

Přidáno

scripts/sync_catalog.py): položky zrušené na e-shopu se označí

products.active=false (soft-delete) a hledání je vyřadí — data vč.

embeddingů zůstávají (návrat položky = jen reaktivace). Nové položky se

vloží vč. SKU, parametrů, fulltextu a embeddingu; SKU existujících produktů

se přidají/odeberou dle feedu. Zrušené zboží mizí z výsledků do 24 h.

sync odmítne feed s < INGEST_MIN_PRODUCTS (1000) produkty nebo deaktivaci

SYNC_MAX_DEACTIVATE_PCT (20 %) aktivních; plný import (ingest_partner.py)

odmítne feed menší než INGEST_MIN_KEEP_PCT (90 %) stavu DB — před TRUNCATE,

vynucení --force; enrich přeskočí Heureka feed s < HEUREKA_MIN_PRODUCTS

(500) produkty; fetch_feeds.sh stahuje do .tmp + kontrola velikosti a XML

hlavičky (vadný download nepřepíše funkční feed).

2 zrušené deaktivovány (zmizely z hledání), SKU +132/−16; pojistky odmítly

3-produktový feed (sync i ingest), DB nedotčena.

Změněno

[0.9.0] – 2026-07-10 – Automatický denní refresh dat + monitoring

Přidáno

scripts/scheduler.py): každý den v 5:00 (REFRESH_HOUR, TZ=Europe/Prague)

stáhne feedy z gms.cz (atomicky, sanity-check velikosti a XML hlavičky – při

chybě zůstane starý soubor) a obohatí DB z Heureky (ceny/sklad/kategorie).

Plný reingest katalogu zůstává ruční (chrání embeddingy).

(?key= nebo hlavička X-Metrics-Key; bez klíče v configu endpoint

neexistuje – 404). Vrací:

off-topic, avg+p95 latence, tokeny a cena celkem i za 24 h),

[0.8.0] – 2026-07-10 – Ceny, dostupnost, kategorie + navádění na upřesnění

Přidáno

ceny („od X Kč" / přesná u spárované varianty), dostupnost („skladem" z

DELIVERY_DATE), kategorie s odkazem (URL = slug cesty, ověřený vzor

gms.cz) a přesné URL produktů/variant. Napojení: produkt přes slug názvu

(93,4 % Heureka produktů), varianta→SKU přes hmotnost / ≥2 číselné

parametry, jen jednoznačné shody (79,0 % variant). Dřívější napojení cen

přes přesnou shodu názvu bylo 0% (tiše rozbité) — nahrazeno.

Celá kategorie: název. Nové sloupce v products/skus (viz schema).

pipeline z dat zjistí, které parametry se u nalezených produktů liší

(refine_hints — průměr, tlak…), asistent vyzve k upřesnění, ale produkty

+ kategorii vypíše vždy. Deterministická detekce (žádná číselná omezení

v dotazu), žádné hádání.

bez ztráty embeddingů, vhodné pro cron) i jako součást ingest_partner.py.

refine_hints + integrace.

Známá omezení dat (k dohodě s GMS)

feed (množství per ordercode) + číselník skupin (guid→název).

[0.7.1] – 2026-07-10 – Refactoring do modulů + testovací suite

Změněno (chování beze změny)

konfliktů): app/server.py je JEN HTTP vrstva; orchestrace chatu přesunuta do

app/pipeline.py (run_chat, HistoryStore, ChatError, náklady,

normalizace omezení); monolitický app/llm.py rozdělen na balíček

app/llm/ (providers.py, prompts.py, rerank.py, retry.py +

fasáda __init__.py se stabilním API); noise-filtr vyčleněn do app/noise.py.

(dřív inline v _answer_prompt).

Přidáno

(normalize_constraints, desired_count, cost_usd, HistoryStore),

celé flow run_chat (happy path, off-topic, fallback, rerank chyby, paměť

session), search (slugify, blend, SQL fragmenty omezení), noise-filtr,

prompty, rerank (dedup/rozsahy/fallback), auth (HMAC podpisy, debug heslo),

HTTP vrstva (TestClient). Kořenový conftest.py pro import path.

Odstraněno

0.8.0-dev) smazána vč. originu — funkce se nezachovává.

[0.7.0] – 2026-06-20 15:36 – Migrace na Gemini, logování tokenů/ceny, oprava relevance

AI vrstva → Google Gemini (jedna firma)

gemini-2.5-flash-lite. Vypnuté „thinking" (rychlost+cena). ~4–5× levnější než gpt-4o.

ukázal výrazně lepší relevanci na těžkých dotazech (pára→CALORTEC, kyselina→CHEMITEC,

odsávání pilin→FLEXADUR). Práh relevance rekalibrován na 0.62 (Gemini sims jsou vyšší).

Sledování nákladů

Relevance

Integrace

[0.6.8] – 2026-06-20 14:21 – Klíčové parametry u každého produktu (default)

Změněno

kompaktní klíčová sada – Vnitřní/Vnější průměr, Provozní tlak, Poloměr ohybu,

příp. DN/Délka (max 5). Balast (balení, hmotnost, série, barva, normy) se

nevypisuje. Funguje i v poradním/srovnávacím režimu.

[0.6.7] – 2026-06-20 14:15 – Poradní režim (srovnávání a doporučování)

Přidáno

/ vhodnější, rozdíl mezi materiály/typy) na základě oborových znalostí –

nejdřív krátká věcná odpověď, pak doporučené produkty z katalogu.

ceny) si NIKDY nevymýšlí – jen z dat, jinak odkáže na stránku produktu;

produkty doporučuje jen reálné z katalogu. (Princip „nehádat" se týká čísel,

ne obecné rady.)

[0.6.6] – 2026-06-20 14:09 – 5 výsledků, odpovědi na dotazy z DB

Změněno / přidáno

až 10. Asistent vypisuje VŠECHNY dohledané produkty.

teplota, tlak, norma, atest, vhodnost), asistent odpoví STRUČNĚ a JEN z dat

(detail / dokumentace / parametry). Když údaj v datech není → odkáže na stránku

produktu, nic si nevymýšlí.

[0.6.5] – 2026-06-20 13:57 – Parametry na vyžádání + oprava zdůvodnění

Opraveno / přidáno

je teď VYPÍŠE u každého produktu (tvar „Název: hodnota", jen užitečné – bez

Balení/Hmotnost/Série/Barva). Defaultně se parametry stále neukazují.

(Dřív na žádost o parametry nic neukázal.)

z dotazu zákazníka, nezužuje význam (dřív „hadice na vodu" → „potravinářské").

[0.6.4] – 2026-06-20 13:42 – Reálná diskuze: meta-dotazy a žádost o parametry

Opraveno (na základě reálné diskuze na TESTu)

(text), ne surová historie+zpráva. Dřív „Ukaž mne parametry daných produktů"

naředilo dotaz a mezi palivové hadice se připletla potravinářská.

a odkazem (konkrétní čísla neuvádí – přesné parametry/ceny jsou na stránce produktu).

[0.6.3] – 2026-06-20 13:24 – Code review: opravy chyb

Opraveno

se top-k dle podobnosti (dřív „nic nenalezeno" i u relevantních produktů).

(po reranku, 4), ne pro celý pool 20 kandidátů. Méně round-tripů na chat.

fallback), _matching_skus ukáže všechny SKU produktu (dřív 0 variant).

Ověřeno (review)

(trny, spojky, vsuvky, redukce). Funguje správně.

[0.6.2] – 2026-06-20 11:10 – Chat UI: jen čistá odpověď

Změněno

„Nalezené produkty" s tabulkami SKU/rozměrů/tlaků. Tyto detaily jsou teď uvnitř

sbaleného 🔧 Debug panelu (na rozkliknutí pro ladění). Hlavní chat ukazuje

jen čistou odpověď (produkt + popis + odkaz na e-shop).

[0.6.1] – 2026-06-20 10:57 – Pevný formát odpovědi (jen produkt + popis + odkaz)

Změněno

- název — krátký popis (≤8 slov) + na konci

🔎 [Zobrazit vše na e-shopu]. **Žádné výpisy rozměrů/tlaků/teplot/norem/

materiálů/cen** – ty si zákazník zobrazí na e-shopu.

Pozn.

changelog): noise filtr balastu (ne-hadice) v hledání + LLM-reranker

(gpt-4o-mini: pool 20 → top 4, vyhazuje příslušenství/koncovky) + on_topic gate

uznává popis použití.

[0.6.0] – 2026-06-19 19:40 – Extrakce dat z dokumentů (PDF+OCR) a zpřesnění

Added — maximální extrakce dat

PDF (text vrstva, PyMuPDF) + interní OCR (tesseract, ces+eng+deu) na scany

i obrázky. Resilientní (cache, resume, limit paměti proti OOM, strip NUL).

→ 9 324 dokumentů, 4 393 produktů má doc_text (~18,7M znaků).

atesty z katalogových listů (i cizojazyčných, shrnuté česky). Zpřesnění výstupů.

Zlepšeno — relevance a robustnost (iterativně přes scripts/eval3.py, 20 odborných otázek)

→ topicky relevantní výsledky (nic se nehádá, odpověď ukáže reálné parametry).

Přesné rozměry zůstávají, když existují (benzín Ø50 → jen Ø50).

s lo/hi=None (dřív nulovala výsledky, např. „Camlock z nerezu" → 0).

(konec kolísání stejného dotazu mezi v=0 a v=5).

ověřeno: zhoršoval skóre); doc_text se využívá jen ve fulltextu a v odpovědi.

Výsledky (eval3, 20 odborných dotazů, LLM-soudce)

předpo
spokojenost4.10/54.20–4.35/5 (stabilní)
false-negativy (v=0)několik (Camlock, EN 12115…)0

existují, ale embedding nadřazuje příbuzné položky; hraniční ranking, ne chyba.

[0.5.2] – 2026-06-19 17:28 – Changelog na webu, UI drobnosti

Added

app/mdrender.py – minimální Markdown→HTML bez závislostí). Odkazy

„Changelog" a „Admin" v hlavičce chatu.

Změněno

Pozn. (infrastruktura)

certů); dry-run obnovy gms.x86.cz = success.

[0.5.1] – 2026-06-19 17:23 – Recall a HTTPS

Zlepšeno

od LLM (dobrá diakritika) + celého dotazu (zachová kontext jako teplota/médium).

„najdi hadici na benzin" → opět 5 výsledků (dřív 1); „pryž nad 150 °C" → 5.

Ověřeno cílenými testy (benzín/polyamid/pryž 150°C/voda = 5, banán = 0).

Nasazení

443 SSL blok + redirect 80→443 v nginx-proxy-manager:/data/nginx/custom/http.conf.

Ověřeno z internetu: https://gms.x86.cz → 200, http → 301. Cert expiruje

2026-09-17, automatická obnova přes cron na droidu (certbot renew + reload).

[0.5.0] – 2026-06-19 17:06 – F4: Relevance přes evaluaci 55 otázek, robustnost, TEST/rproxy

Zlepšeno (iterativně přes scripts/eval2.py – 55 reálných zákaznických otázek po kategoriích)

těsnící desky, podlahoviny, profily, tyče, technické plasty) – přestaly se

chybně zahazovat dotazy typu „polyamidová tyč", „pryž nad 150 °C".

spolehlivější relevance (false-negativy zmizely).

produkt, LLM poradí obecně a doptá se (bez výmyslu produktů). Mimo-obor →

stručná hláška bez volání LLM.

Robustnost

(automatický exponenciální backoff, respektuje retry-after); zmenšen prompt

odpovědi (méně tokenů). Eval harness má pacing.

Výsledky evaluace (55 otázek, LLM-soudce spokojenosti)

baselinepo iteracích
spokojenost3.73/54.20/5 (+12,6 %)
slabé odpovědi (≤2/5)121
nejslabší kategoriePlasty 2.0, Pryže 3.3vše ≥ 4.0

Nasazení / infrastruktura

/data/nginx/custom/http.conf → 192.168.254.199:45080). HTTPS = TODO

(Let's Encrypt přes NPM GUI).

[0.4.0] – 2026-06-19 15:16 – F3: Logování, admin, multi-turn, evaluace relevance

Added

přežije re-ingestaci): každý chat se ukládá (zpráva, extrakce, výsledky, odpověď,

časy, ok/error, session_id).

/api/logs/{id} (chráněné DEBUG_PASSWORD): procházení diskuzí, detail,

filtr podle session, „Kopírovat odkaz na log", deep-linky.

LLM extrakce dostává historii → zákazník může postupně upřesňovat (5× pokračovat).

banán, auto) → vrací NIC (radši nic než nerelevantní).

relevantní ~0.43+, nesmysly ~0.30–0.39).

zadání, multi-turn), LLM-soudce spokojenosti.

výpis až 5 produktů s odkazy; default limit 5.

Opraveno (přes iterace eval harness)

(dřív vracel keyword balast).

slova (ta embedovala špatně) → opravena regrese vágních dotazů.

Výsledky evaluace (21 diskuzí, LLM-soudce)

baselinepo iteracích
správné chování18/21 (86 %)21/21 (100 %)
spokojenost (relevantní)3.56/53.72/5
spokojenost (celkem)3.48/53.71/5

– přijdou z plné DB.

Analýza PDF (závěr)

smíšené (i .jpg, právní prohlášení). body_text už pokrývá podstatu.

Selektivně jen pro produkty s prázdným popisem. Větší přínos = ceny + kategorie.

[0.3.0] – 2026-06-19 14:31 – F2: Sémantika, relevance, ověřeno v Dockeru

Added

(OpenAI text-embedding-3-small, 1536 dim) do products.embedding (pgvector),

HNSW index. Pro všech 5 665 produktů spočítáno.

(MIN_SIM=0.30) – přirozený dotaz řadí embeddingy, rozměry zůstávají tvrdým

filtrem; fulltext (FTS) je fallback. → vrací jen relevantní výsledky.

https://www.gms.cz/?query=<název>; asistent ho uvádí jako markdown odkaz.

Ověřeno v běžícím Dockeru (PostgreSQL+pgvector)

343 878 hodnot parametrů**.

NBR/FUB/CLC…, FKM ponorná), žádné navijáky/pistole/požární hadice. ✓

s odkazy). Klíče v .env (negitováno).

Opraveno

Pozn.

eshopové hledání podle názvu; přesné URL doplníme z plné DB.

[0.2.0] – 2026-06-19 14:00 – F1: Microservice (FastAPI + PostgreSQL + AI)

Rozhodnutí (odsouhlaseno zadavatelem)

model claude-opus-4-8).

Added

pgvector embedding, tsvector fulltext s unaccent). ID jako TEXT

(katalogové kódy vč. SERVICE*, NV260347 – o žádnou položku nepřijdeme).

přes COPY, stavba fulltextu, doplnění cen z Heureka feedu.

rozměrů nad parsovanými čísly (na úrovni produktu i SKU).

extrakce omezení + generace odpovědi nad dohledanými daty (lazy import SDK).

ochrana), volitelné heslo pro debug UI.

(chráněné), /api/chat (LLM + debug info), debug UI na /.

(extrahované filtry, časy, nalezené produkty/SKU/rozměry).

scripts/fetch_feeds.sh.

12 prochází.

Ověřeno

klíčů (smoke test TestClient: /api/health 200, UI na /, čisté 503/502).

45 592 SKU, 343 878 hodnot parametrů, 0 sirotků).

Čeká na zadavatele (blokuje finální ověření)

hybrid search + build image.

[0.1.0] – 2026-06-19 13:40 – F0: Průzkum a datový základ

Added

docs/, static/, tests/).

„nesmí odhadovat“: uchovává raw hodnotu, numeriku parsuje jen když je

jednoznačná (rozsahy, české čárky, převod MPa/kPa→bar), jinak ponechá text.

+ FTS5 fulltext bez diakritiky).

zabezpečené API, debug UI, nasazení).

Zjištění z dat (ověřeno)

ponechán jako přesný text.

Partnerské feedy (přidáno, bohatší zdroj)

popisy, 9 324 PDF datasheetů, 12 481 obrázků, vazby skupin a souvisejících.

(produkty i SKU), ø 6,7 parametru/položku.

Infrastruktura (poznámky)

na RPROXY → HTTPS do Docker kontejneru.