Journal

Uvnitř carBotu: tržní analytika začíná nejistotou, ne sběrem dat.

carBot mění nekonzistentní automobilové inzeráty v rozhodovací systém opřený o dohledatelné důkazy. Nejtěžší není data stáhnout, ale uchovat jejich původ, odmítnout slabá srovnání a vysvětlit, proč konkrétní skóre vůbec vzniklo.

Datové zpracováníTržní analytikaHodnoceníAutomatizace

Stáhnout inzeráty je snadná ukázka a velmi špatná definice hotového systému. Skutečná práce začíná až potom: které číslo je nájezd a které limit záruky, zda dvě URL popisují stejné fyzické auto, které záznamy spolu vůbec smějí do jedné kohorty a kdy má systém raději odmítnout skórovat.

Syntetický přehled carBotu s původem jednotlivých polí, srovnatelnou tržní kohortou, kontrolami modelu a vysvětlitelným skóre.
Cesta od zdrojového důkazu k rozhodnutí. Čísla jsou ilustrační a data syntetická; produkční záznamy, SPZ, VINy, kontakty ani datové odpovědi tržišť obrázek neobsahuje.

Inzerát je tvrzení, ne řádek v tabulce.

Text od prodejce míchá fakta, formátovací nehody, obchodní omáčku a prázdná místa. Stejné vozidlo se může vrátit pod jinou URL, s přeloženým popisem, novou cenou nebo neúplnou identitou. Aukční záznam má navíc jinou životní logiku než běžná nabídka. Parser, který z toho vyrobí jeden čistý řádek a původní podklady zahodí, právě zničil důkaz potřebný k ladění každého pozdějšího rozhodnutí.

carBot proto ukládá syrovou i normalizovanou podobu společně. Parsované pole nese hodnotu, zdroj extrakce, původní fragment a pravidly odvozenou třídu jistoty. Ta není vydávána za kalibrovanou pravděpodobnost. Jde o provozní metadata: dost na to, aby slabý záznam skončil ve frontě ke kontrole, aby šlo porovnat dvě verze parseru a aby člověk dohledal, proč systém konkrétní hodnotu přijal.

ProblémCo carBot uchováváProč na tom záleží
Nejednoznačný textHodnotu, původní fragment, použité pravidlo a třídu jistoty.Chybu lze zopakovat a opravit, ne pouze ručně přepsat.
Opakovaný příjemStabilní otisk URL, otisk obsahu a verzi parseru.Stejný vstup nevytvoří nový záznam ani zbytečné opakované parsování.
Částečná aktualizaceDříve známé hodnoty a samostatnou historii ceny a stavu.Chybějící pole v nové odpovědi nesmaže kvalitnější starší důkaz.

Pipeline vede od důkazu přes stav a model až k akci.

Jádrem je asynchronní zpracování v Pythonu nad PostgreSQL, doplněné typovaným inspekčním API a webovou aplikací pro analytika. Adaptéry zdrojů běží odděleně. Normalizace a uložení probíhají po jednotlivých inzerátech; přepočet trhu, hodnocení, skládání identity a vyhodnocení upozornění jsou samostatné následné fáze.

Globální a paměťově drahé kroky se serializují. Dva zdroje mohou stahovat paralelně, ale dvě současné rekonstrukce stejných kohort by zvýšily špičku paměti a mohly si přepisovat hromadné aktualizace. To je důležitý detail: orchestrátor není jen provozní obal kolem modelu. Určuje, nad jakým stavem model opravdu počítá.

{
  "identity": {
    "url_fingerprint": "stable-source-key",
    "duplicate_of": null
  },
  "raw": {
    "price": "localized seller text",
    "description": "retained source evidence"
  },
  "parsed": {
    "mileage_km": {
      "value": 142000,
      "confidence": "medium",
      "source": "labeled-description-fragment"
    }
  },
  "lineage": {
    "content_hash": "change-detection-hash",
    "parser_version": "ruleset-version"
  },
  "decision": {
    "score": 82.4,
    "method": "guarded-regression",
    "sample_count": 41
  }
}

Ukázka je záměrně syntetická a zredukovaná. Podstatné je rozdělení: identita endpointu, původní důkaz, normalizované pole, datový původ a rozhodnutí nejsou jeden anonymní blob.

Parser řadí důkazy; nevěří první shodě.

Regulární výraz sám o sobě není problém. Problém je kontext. Jeden popis může obsahovat skutečný nájezd, limit záruky i vzdálenost k prodejci, pokaždé se stejnou jednotkou. carBot nejdřív sbírá kandidáty, odmítá známé rušivé kontexty, upřednostňuje explicitně označené údaje, kontroluje jejich věrohodnost a ukládá důvod vítězné volby.

Pořadí zdrojů důkazu je součástí parseru. Titulek bývá stručný a často silný pro model nebo motorový kód; popis je bohatší, ale hlučnější. Specifický víceslovný alias musí vyhrát před kratším obecným tokenem. Rok vložený do plného data nesmí projít jako modelový rok. Lokalizované zápisy čísel a měn se musí rozdělit dřív, než se z nich stane numerická hodnota.

# Zjednodušený vzor pro řazení důkazů.
def choose_mileage(text: str) -> ScoredField:
    candidates = collect_mileage_candidates(text)

    usable = [
        c for c in candidates
        if not in_warranty_context(c, text)
        and not in_location_distance_context(c, text)
        and plausible_mileage(c.value)
    ]

    if not usable:
        return ScoredField(None, confidence=Confidence.NONE, source="not_found")

    winner = min(usable, key=lambda c: (c.pattern_priority, -c.confidence_rank))
    return ScoredField(winner.value, winner.confidence, winner.pattern_name)

Metadata o jistotě nejistotu zviditelní, ale sama ji nekalibrují. Současné třídy vznikají z pravidel, nejsou to pravděpodobnosti. Kohortové dotazy dnes stojí hlavně na přítomnosti hodnoty, věrohodnosti, stavu a deduplikaci; ne každý vzorek je ještě vážený podle skutečně naměřené kvality parseru. Další konkrétní práce je proto dvojí: izolovat selhání po jednotlivých polích a propojit vstup do kohort s pozorovanou přesností extrakce.

Správná kohorta je důležitější než sofistikovaný model.

Levné auto nemusí být dobrý obchod. Může být jen porovnané se špatnými auty. carBot nejprve rozdělí aktivní, neduplikované záznamy podle identity vozidla a tržního kontextu, vyřadí populace s nekompatibilní cenovou semantikou, ořízne extrémní nabídkové ceny pomocí IQR a teprve potom počítá mediány.

Systém preferuje čerstvou kohortu stejného roku. Když je segment tenký, sáhne do delší historie a teprve jako poslední možnost rozšíří rok. Výsledkem je model nabídkových cen, nikoli realizovaných obchodů. Inzeráty jsou korelované, vybírané prodejci a neříkají, za kolik auto opravdu změnilo majitele. Medián je užitečný pro řazení a průzkum, ne jako slib prodejní ceny.

-- Redukovaný tvar kohortového dotazu.
WITH base AS (
  SELECT cohort_key, price, mileage
  FROM active_listings
  WHERE duplicate_of IS NULL
    AND price IS NOT NULL
),
quartiles AS (
  SELECT cohort_key,
         percentile_cont(0.25) WITHIN GROUP (ORDER BY price) AS q1,
         percentile_cont(0.75) WITHIN GROUP (ORDER BY price) AS q3
  FROM base
  GROUP BY cohort_key
),
clipped AS (
  SELECT b.*
  FROM base b
  JOIN quartiles q USING (cohort_key)
  WHERE b.price BETWEEN q.q1 - :iqr_multiplier * (q.q3 - q.q1)
                    AND q.q3 + :iqr_multiplier * (q.q3 - q.q1)
)
SELECT cohort_key,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY price) AS median_price,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY mileage) AS median_mileage,
       count(*) AS sample_count
FROM clipped
GROUP BY cohort_key;

Skóre je hlídaná heuristika pro pořadí, ne věštírna.

Výpočetní jádro nejprve odmítne zjevně neplatné vstupy. Přesný duplikát, chybějící identita vozidla nebo záznam bez ceny zůstane explicitně bez skóre. U způsobilého záznamu zkusí OLS odhad s nájezdem, stářím a výkonem, potom menší model s nájezdem a stářím a nakonec medián srovnatelné kohorty.

Hierarchie je důležitější než regrese. Model projde jen s dostatečným počtem vzorků, přijatelnou shodou na trénovacích datech a věrohodnou predikcí. Každý úspěšný výsledek nese použitou metodu, počet vzorků, očekávanou cenu, reziduum a relevantní metadata kvality. Konečné pořadí může přidat relativní nájezd, kontext prodejce nebo jazyk o poškození, ovšem s detekcí negace. Úkolem je seřadit frontu analytika, ne certifikovat hodnotu vozu.

# Redukovaný průchod hodnocením; váhy a prahy nejsou zveřejněné.
def estimate_price(record, models, cohorts):
    for model in (models.ols_mileage_age_power, models.ols_mileage_age):
        prediction = model.try_predict(record)
        if prediction and prediction.fit_is_usable and prediction.is_plausible:
            return Estimate(
                value=prediction.value,
                method=model.name,
                evidence={"samples": model.n, "r_squared": model.r_squared},
            )

    cohort = cohorts.best_available_for(record)
    if cohort and cohort.sample_count >= cohort.minimum_required:
        return Estimate(
            value=cohort.median_price,
            method="cohort_median",
            evidence={"samples": cohort.sample_count, "window": cohort.window},
        )

    return None  # I neskórovaný záznam je platný výsledek.

Současná OLS implementace řeší normálové rovnice přímo a používá koeficient R² na trénovacích datech. Výhodou je malý počet závislostí a dobrá vysvětlitelnost. Nevýhodou jsou neškálované prediktory, kolinearita a nestabilní koeficienty v malých ročních pásmech. Produkčně silnější cesta vede přes QR nebo SVD, opodstatněnou regularizaci, validaci dělenou v čase a predikční intervaly. Do té doby je regrese jedním hlídaným odhadem v systému náhradních cest, nikoli hlavním marketingovým tvrzením.

Způsob spouštění je součást analytické metody.

Zpracování může celý den vracet HTTP 200 a přitom potichu přestat být užitečné. carBot sleduje klouzavou úspěšnost, percentily latence, omezování provozu, prázdné výsledkové stránky a pokrytí klíčových polí. Náhlý pokles úspěšnosti extrakce modelu nebo nájezdu znamená změnu struktury zdroje, i když samotný požadavek stále „funguje“.

Jednotlivé zdroje mají vlastní tempo a ochrannou prodlevu. Databázový zámek nepustí druhý proces spouštěče a globální následné zpracování probíhá sériově. Zámek spolu s časovou prodlevou sloučí nadbytečnou práci po souběžném dokončení zdrojů. Kompromis je otevřený: dnešní runner funguje jako orchestrátor uvnitř jednoho procesu, ne jako trvanlivý distribuovaný správce úloh. Po restartu navazuje z databázového stavu, nikoli z uloženého grafu úloh.

API a analytické rozhraní proto ukazují záznamy s nízkou jistotou, metodu a detail skóre, historii ceny, zdraví zdrojů, změny kvality extrakce, události identity, stav spouštěče i konfiguraci upozornění. Přehled není dekorace kolem sběru dat. Je to místo, kde jsou předpoklady modelu vidět.

Identita inzerátu a identita vozidla jsou dva různé problémy.

Otisk URL odpovídá na otázku „viděli jsme tento zdrojový záznam?“ Neprokazuje, že dvě různé nabídky popisují dvě různá auta. carBot proto drží deduplikaci na úrovni zdroje odděleně od identity vozidla. Přesně ověřený VIN může spojit výskyty napříč zdroji a časem. Kandidát odvozený z obrazu nebo registrační značky zůstává důkazem s evidovanou mírou jistoty, nikoli automatickým kanonickým klíčem.

Syrové identifikátory, výřezy značek, odpovědi registrů a konkrétní metody získávání dat zůstávají mimo článek. Publikovatelná myšlenka je stupnice jistoty: slabý důkaz může navrhnout spojení, ale záznamy smí sloučit až důkaz silnější.

Vyhodnocení upozornění je čisté; způsob doručení je přesně přiznaný.

Sledovací filtry jsou čisté predikáty nad inzerátem. Výsledek vznikne jen pro smysluplnou událost, například novou shodu nebo pokles ceny přes nastavený práh. Klíč události obsahuje inzerát, filtr a identitu změny. Unikátní databázové omezení potlačí běžné opakování a současně dovolí, aby další změna ceny vytvořila nové upozornění.

Není to magické doručení „právě jednou“. Když externí oznámení uspěje a zápis deduplikačního záznamu selže, opakování může zprávu odeslat dvakrát. Zámek jediného spouštěče toto okno zmenšuje; transakční outbox a idempotentní klíč poskytovatele by je uzavřely důsledněji. Přesně pojmenovat tuto semantiku je součást spolehlivého provozu.

Pět lekcí, které platí i mimo automobilová data.

1. Normalizace bez dohledatelného původu je slepá ulička.

Původní fragmenty, verze parseru a content hash mění chybu na reprodukovatelný test. Bez nich je každá oprava jen odhad, co se asi stalo.

2. Míra jistoty musí ovlivňovat systém, ne jen štítek v rozhraní.

Třída jistoty má smysl tehdy, když podle ní funguje fronta ke kontrole, přijetí vzorku do kohorty, alert i vysvětlení výsledku.

3. Skromný model nad správnou kohortou porazí chytrý model nad směsí.

Nejdražší model nevyřeší nekompatibilní cenovou populaci. Kvalita srovnání přichází před výběrem algoritmu.

4. Odmítnutí skórovat je korektní funkce.

Chybějící identita, slabý počet vzorků nebo nevěrohodná predikce mají zastavit výrobu falešné přesnosti.

5. Provozní stav je součást kvality modelu.

Změna struktury zdroje, dvojitý spouštěč, zastaralý přepočet nebo nejasné doručení mohou znehodnotit výsledek, aniž by se změnil jediný řádek modelu.

V praxi tyto principy podporují triáž obchodů, hlídání cenových změn, tržní výzkum, kontrolu parseru, aukční analýzu, časové osy identity a provoz zdrojů. Stejná architektura se hodí pro reality, nákupní datové feedy, řízení rizik tržišť a další vertikální data, kde vstupy představují tvrzení, ne měření.

Co záměrně nepublikujeme.

Článek neobsahuje přístupové údaje, postupy pro konkrétní zdroje, neveřejné koncové body, surové výpisy inzerátů, osobní údaje, identifikátory vozidel, produkční snímky, detaily obcházení ochran zdrojů, přesné modelové prahy ani interní bezpečnostní materiál. Ukázky jsou syntetické redukce architektury, nikoli produkční záznamy nebo zdrojový kód.

Dalším milníkem je kalibrace.

Nejhodnotnější práce už není další adaptér zdroje. Je to měřená zpětná vazba: změnit opravy analytika na testovací případy parseru, počítat přesnost a úplnost extrakce po jednotlivých polích, pouštět nebo vážit kohortové vzorky podle pozorované kvality a sledovat stabilitu skóre v čase.

Stejnou disciplínu potřebuje identita: generování kandidátů šetrné k soukromí, explicitní stupně důkazu a lidská kontrola před tím, než se přibližná shoda stane kanonickou. Doručování alertů může přejít na transakční outbox s idempotencí u poskytovatele. Nejsou to efektní položky na slidu. Jsou to přesně ty části, které z užitečného interního žebříčku dělají obhajitelnou platformu tržní inteligence.

Zpět do JournaluVýzkum, systémy a technické poznámky z praxe.

Máte podobný systém k postavení?

Napište, co řešíte. Vrátíme se s konkrétním dalším krokem.

Kontaktovat Gloryck