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.

[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-POZADAVKY.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.