🌱
Nowe produkty
★ głównyPełny pipeline: opis + score + tłumaczenia + upload
Główny tryb — wprowadź nowe produkty od dostawców z pełnym widgetem
Co robi ten tryb
End-to-end: pobranie XML-i od dostawców (Merkury/BioPlanet/EkoWital) → konsolidacja per EAN → klasyfikacja Gemini Flash (NORMALNY/SUPLEMENTY/WARZYWA) → generowanie polskich opisów (4 prompty per klasa) → Bioshi Score multimodal → tłumaczenia 23 langs → widget per język → upload IdoSell. 2 manual approval gates w Google Sheets (filtrowanie + weryfikacja).
Kiedy używać
- Wprowadzenie nowych produktów od Merkury/BioPlanet/EkoWital
- Pełny pipeline dla brakujących EAN-ów (z `brakujace_ean.csv` lub od stróża)
👤 Kroki wymagające operatora
⚠ Filtrowanie produktów
Sheets tab `add NW`: zaznacz produkty do odrzucenia (alkohol, tytoń sugerowane przez `filtr_produktow.txt`).
⚠ Weryfikacja jakości
Sheets tab `weryfikacja`: prompt `weryfikator.txt` daje ocenę 1-10. Akceptuj ≥7 lub popraw ręcznie.
⚙️ Realny pipeline (bioshi-score-v2)
path
procesy/edycja-tresci/new_products/
launcher
nowe_produkty.bat
main_module
app/main.py + runner.py + translation_runner.py + verification_runner.py
ai_model
gemini-2.5-flash (klasyfikacja + opisy) + gemini-2.5-pro (Bioshi Score multimodal)
gcp_project
edycja-tresci
gcp_region
europe-west1
input_source
brakujace_ean.csv + XML dostawców (zgrywanie_opisow/)
output_target
Google Sheets tabs (produkty/add NW/log/weryfikacja) + Drive archiwum
To są ścieżki do kodu w drugim projekcie na pulpicie — webapp w przyszłości będzie te skrypty triggerował automatycznie zamiast wymagać uruchomienia z `.bat`.
Szczegółowa instrukcja procesu
8automatycznych
4manualnych
Łącznie:~3-5h
0
Konsolidacja danych dostawców
🤖 auto
⏱ ~2 min
📥 4 input(ów) · 📤 2 output(ów) · 📝 7 sub-kroków
▼
📝 Sub-kroki
- 0.1 Wczytanie listy EAN z Google Sheet (parsowanie CSV, deduplikacja)
- 0.2 Wczytanie 3 plików CSV dostawców do pamięci (różne encoding'i — UTF-8 / Windows-1250 / latin-1)
- 0.3 Match po EAN — dla każdego EAN sprawdza w 3 plikach, preferencja: BioPlanet > Merkury > EkoWital (bo BioPlanet ma najwięcej info)
- 0.4 Konsolidacja: dla każdego EAN tworzy obiekt z polami z PIERWSZEGO dostawcy gdzie znaleziono. Drugi dostawca jako fallback dla pustych pól.
- 0.5 Normalizacja: gramatura → gramy (jeśli wpisane '500g' albo '0.5 kg'), VAT → liczba (5/8/23), zdjęcie URL → walidacja HTTPS
- 0.6 Zapis CSV do GCS bucket `bioshi-products-data/runs/{run_id}/00_konsolidacja.csv`
- 0.7 Zapis listy braków do `00_brakujace_w_dostawcach.csv` (operator dostanie info na Slacka)
📥 Input
- Lista EAN-ów z Google Sheet (kolumna `EAN` + `Dostawcy`)
- Plik Merkury: `zgrywanie_opisow/nieprzerobione/merkury_full.csv` (CSV — config.json)
- Plik BioPlanet: `zgrywanie_opisow/nieprzerobione/bioplanet_full.csv` (CSV, nie XLSX)
- Plik EkoWital: `zgrywanie_opisow/nieprzerobione/ekowital_full.csv`
📤 Output
- `00_konsolidacja.csv` — produkty z wszystkimi polami z dostawców (EAN, nazwa, marka, gramatura, VAT, zdjęcie URL, kraj pochodzenia)
- `00_brakujace_w_dostawcach.csv` — EAN-y których nie znaleziono
⚠️ Ryzyka
- EAN nie istnieje u żadnego dostawcy → trafia do `00_brakujace_w_dostawcach.csv`
- Niezgodne nazwy w plikach dostawców (np. polskie znaki źle zakodowane)
- Brak zdjęcia w danych dostawcy → pole `image_url` puste (problem na step 6)
🔔 Slack: Powiadomienie tylko jeśli >5% EAN-ów w `brakujace_w_dostawcach.csv`
💾 Archiwum:
00_konsolidacja.csv00_brakujace_w_dostawcach.csvM1
MANUAL: Operator weryfikuje braki i edytuje drzewko kategorii
👤 manual
⏱ 5-30 min (zależy od liczby braków)
📥 2 input(ów) · 📤 2 output(ów) · 📝 6 sub-kroków
▼
📝 Sub-kroki
- M1.1 Operator dostaje Slack DM: link do CSV z brakami + przycisk 'Otwórz w arkuszu'
- M1.2 Operator otwiera arkusz, ręcznie uzupełnia braki z faktur PDF od dostawców
- M1.3 Operator sprawdza drzewko kategorii w `kategorie_idosell.csv`
- M1.4 Jeśli pojawiła się nowa kategoria (np. 'Produkty sypkie/Mąki/Mąka kasztanowa') — operator ją dopisuje
- M1.5 Operator klika 'Gotowe' w Slacku → bot continuje pipeline
- Jeśli operator nie odpowie w 1h → przypomnienie. W 4h → eskalacja do innego operatora. (Tryb 'Boguś Pracoholik' wyłącza godziny ciszy.)
📥 Input
- `00_brakujace_w_dostawcach.csv`
- Slack DM z linkiem do produktów do uzupełnienia
📤 Output
- Zaktualizowany `00_konsolidacja.csv` (z dodanymi ręcznie produktami)
- Zaktualizowany `kategorie_idosell.csv` (jeśli operator dodał nową kategorię)
⚠️ Ryzyka
- Operator nie odpowiada → pipeline blocked (przypomnienia co 1h)
- Operator źle zmapuje EAN → produkt zostanie zaklasyfikowany do złej kategorii w step 1
👤 Akcje operatora
- Otworzyć link w Slacku
- Uzupełnić CSV z faktur PDF
- Zaktualizować `kategorie_idosell.csv` jeśli trzeba
- Kliknąć '✓ Gotowe' w Slacku
🔔 Slack: DM na start + przypomnienie co 1h. Kanał #bioshi-products: log akcji.
💾 Archiwum:
00_konsolidacja_v2.csvkategorie_idosell.csv1
Klasyfikacja produktów (Gemini Batch)
🤖 auto
⏱ 30-45 min (Gemini Batch async)
📥 3 input(ów) · 📤 1 output(ów) · 📝 7 sub-kroków
▼
📝 Sub-kroki
- 1.1 Buduje JSONL — jeden request per EAN. Każdy request zawiera: nazwę, markę, dane z dostawców, dostępne kategorie z drzewka
- 1.2 Upload JSONL do GCS `gs://bioshi-products-data/runs/{run_id}/01_batch_input.jsonl`
- 1.3 Submit Vertex AI Batch z `model=gemini-2.5-flash`, `response_schema=KLASYFIKACJA_SCHEMA`
- 1.4 Polling state Vertex AI co 30s. Status: queued → pending → running → SUCCEEDED
- 1.5 Po SUCCEEDED — fetch wyników z `gs://.../runs/{run_id}/01_batch_output/`
- 1.6 Parse JSONL responses, validate schema, zbieranie do CSV
- 1.7 Walidacja: jeśli `category_full_path` nie istnieje w drzewku → flag 'NIEZNANA_KATEGORIA' (operator zatwierdza w M2)
📥 Input
- `00_konsolidacja.csv`
- Prompt `klasyfikator.txt` z Firestore (default z `new_products/app/prompts/`)
- Drzewko kategorii `kategorie_idosell.csv`
📤 Output
- `01_klasyfikacja.csv` z polami: `productId`, `EAN`, `category_full_path`, `klasa` (NORMALNY/SUPLEMENTY/WARZYWA_I_OWOCE/KREATOR), `brand_normalized`, `name_normalized`
⚠️ Ryzyka
- Gemini może zaklasyfikować do nieistniejącej kategorii → flag
- Gemini może wymyślić nową markę → flag
- Vertex AI Batch może failować → retry max 3 razy
- Timeout >2h → operator dostaje Slack 'Batch trwa nietypowo długo'
🔔 Slack: Start batcha + zakończenie (po SUCCEEDED). Błędy: natychmiast.
💾 Archiwum:
01_batch_input.jsonl01_klasyfikacja.csv01_batch_metadata.jsonM2
MANUAL: Weryfikacja klasyfikacji
👤 manual
⏱ 5-15 min
📥 2 input(ów) · 📤 2 output(ów) · 📝 5 sub-kroków
▼
📝 Sub-kroki
- M2.1 Operator dostaje Slack z linkiem do strony review (`/runs/{run_id}/review/1`)
- M2.2 Strona pokazuje 4 grupy: NORMALNY/SUPLEMENTY/WARZYWA/KREATOR + sekcja 'WYMAGA UWAGI' (flagi)
- M2.3 Operator może zmienić klasyfikację per produkt (dropdown z 4 opcjami)
- M2.4 Operator zatwierdza całość → bot generuje `01_klasyfikacja_approved.csv`
- M2.5 Pipeline kontynuuje step 2
📥 Input
- `01_klasyfikacja.csv`
- Slack DM z podsumowaniem: ile NORMALNY/SUPLEMENTY/WARZYWA/KREATOR + flag'i 'NIEZNANA_KATEGORIA'
📤 Output
- Zatwierdzony `01_klasyfikacja_approved.csv`
- Lista zmian operatora (audyt) → `01_operator_overrides.csv`
⚠️ Ryzyka
- Operator zatwierdzi błędną klasyfikację → produkt dostanie zły opis w step 2
- Operator nie odpowiada → blocked
👤 Akcje operatora
- Otworzyć stronę review
- Sprawdzić flagi 'WYMAGA UWAGI'
- Ew. poprawić klasyfikację per produkt
- Kliknąć 'Zatwierdź wszystko'
🔔 Slack: DM podsumowanie + przypomnienia co 1h.
💾 Archiwum:
01_klasyfikacja_approved.csv01_operator_overrides.csv2
Generowanie opisów PL (Gemini Batch)
🤖 auto
⏱ 45-60 min
📥 3 input(ów) · 📤 1 output(ów) · 📝 7 sub-kroków
▼
📝 Sub-kroki
- 2.1 Per klasa wybiera odpowiedni prompt z Firestore (najnowsza wersja)
- 2.2 Buduje JSONL — request zawiera: nazwę, markę, kategorię, skład, gramatura, kraj. Prompt instruuje strukturę: `<h3>Opis</h3>... <h3>Wartości odżywcze</h3><table>...</table>`
- 2.3 Vertex AI Batch z `model=gemini-2.5-flash`, `maxOutputTokens=32768`
- 2.4 Polling + fetch (jak step 1)
- 2.5 Validate output HTML — sprawdza obecność wszystkich wymaganych <h3>, format <table>, brak polskich znaków źle zakodowanych
- 2.6 Jeśli walidacja fail → retry per produkt z stricter promptem
- 2.7 Output: HTML opisy po polsku, gotowe do tłumaczenia
📥 Input
- `01_klasyfikacja_approved.csv`
- Prompty: `opis_normalny.txt`, `opis_suplementy.txt`, `opis_warzywa.txt`, `kreator.txt` (Firestore)
- Dane z step 0 (skład, gramatura, kraj pochodzenia)
📤 Output
- `02_opisy_pl.csv` z polami: `productId`, `EAN`, `name_pol`, `desc_pol_html`, `meta_title`, `meta_description`
⚠️ Ryzyka
- Gemini może wygenerować HTML niezgodny z templatem → flag (operator review)
- Brakujące wartości odżywcze → Gemini pominie tabelę (akceptowalne dla niektórych klas)
- Truncation jeśli opis > 32k tokens → retry z `chunk` strategią
🔔 Slack: Start + koniec batcha. Listę nieudanych retry-ów.
💾 Archiwum:
02_opisy_pl.csv02_batch_input.jsonl02_validation_log.json3
Liczenie Bioshi Score (Gemini multimodal)
🤖 auto
⏱ 30-45 min
📥 4 input(ów) · 📤 1 output(ów) · 📝 6 sub-kroków
▼
📝 Sub-kroki
- 3.1 Buduje multimodal prompt per produkt: tekst (nazwa, marka, parametry, opis HTML) + URL zdjęcia jako `Part.from_uri`
- 3.2 Validate URL zdjęcia (HEAD request) — jeśli 404, użyj fallback placeholder lub flag 'BRAK_ZDJECIA'
- 3.3 Submit do Vertex AI z `response_schema=BIOSHI_SCHEMA` (15 pól)
- 3.4 Gemini zwraca: `ingredients[]`, `ingredientCount`, `allergens[]`, `bio`, `local`, `nova` (1-4), `hasENumbers`, `hasHFCS`, `hasHydrogenatedFats`, `hasArtificialAdditives`, `hasProteinIsolates`, `hasSweeteners`, `packaging` (glass/paper/metal/compostable/plastic), `origin`, `productionCountry`
- 3.5 `calculate_bioshi_score()` z `pipeline/bioshi_score.py` przelicza grade A-E
- 3.6 Merge wyników z istniejącymi danymi → `03_bioshi_score.csv`
📥 Input
- `00_konsolidacja.csv` + `02_opisy_pl.csv` zmergowane
- Zdjęcia produktów (URL z dostawców)
- Schema `RESPONSE_SCHEMA` z `analyzer.py` (15 pól)
- System prompt `bioshi_extraction.txt` z Firestore
📤 Output
- `03_bioshi_score.csv` z polami: `productId`, `EAN`, `grade` (A-E), `score` (int), `color`, `label`, + 15 pól wejściowych (ingredients, nova, bio, local, etc.)
⚠️ Ryzyka
- Brak zdjęcia → score liczony bez zdjęcia (mniej dokładny, zwłaszcza dla `packaging`)
- Gemini może źle odczytać skład ze zdjęcia → operator review w M3
- Niespójność: BIO w parametrach ale brak w składzie → flag
🔔 Slack: Po zakończeniu: rozkład grade'ów A/B/C/D/E.
💾 Archiwum:
03_bioshi_score.csv03_multimodal_responses.jsonlM3
MANUAL: Review opisy + Bioshi Score
👤 manual
⏱ 15-45 min (per produkt 1-2 min)
📥 2 input(ów) · 📤 2 output(ów) · 📝 6 sub-kroków
▼
📝 Sub-kroki
- M3.1 Operator dostaje link do `/runs/{run_id}/review/3`
- M3.2 Strona pokazuje per produkt: zdjęcie, polski opis (live preview HTML), Bioshi Score widget
- M3.3 Operator może edytować opis inline (zachowując strukturę h3+table)
- M3.4 Operator może zmienić grade (uzasadnienie wymagane)
- M3.5 Bulk actions: 'Zatwierdź wszystkie z grade A', 'Flag niskiej jakości opisów'
- M3.6 Po zatwierdzeniu pipeline kontynuuje step 4
📥 Input
- `02_opisy_pl.csv` + `03_bioshi_score.csv`
- Slack DM z linkiem do strony review
📤 Output
- Zatwierdzony `02_opisy_pl_approved.csv` + `03_bioshi_score_approved.csv`
- Lista edycji operatora
⚠️ Ryzyka
- Operator zmieni grade bez powodu → audyt log zapisuje wszystkie zmiany
- Operator zatwierdzi nieprzeczytany → flag w archive
👤 Akcje operatora
- Przejrzeć każdy produkt (zdjęcie + opis + score)
- Edytować jeśli trzeba
- Zatwierdzić batch (z opcją 'Audyt: które produkty obejrzałeś')
🔔 Slack: DM po zakończeniu step 3 + przypomnienia co 1h.
💾 Archiwum:
02_opisy_pl_approved.csv03_bioshi_score_approved.csv03_operator_edits.json4
Tłumaczenia na 23 języki (Gemini Batch)
🤖 auto
⏱ 60-120 min (największy batch)
📥 2 input(ów) · 📤 2 output(ów) · 📝 6 sub-kroków
▼
📝 Sub-kroki
- 4.1 Buduje JSONL — per produkt × per lang = 23 requestów per produkt
- 4.2 Dla 100 produktów = 2300 requestów. Vertex AI Batch obsługuje async.
- 4.3 Submit + polling (jak poprzednio)
- 4.4 Validate per lang: brak polskich znaków, struktura h3 zachowana, długość zbliżona do polskiego
- 4.5 Polish leak detection: jeśli w niemieckim opisie pojawia się polskie słowo (`się`, `oraz`) → flag → retry
- 4.6 Merge wyników do jednego CSV z 23 kolumnami
📥 Input
- `02_opisy_pl_approved.csv`
- Prompt `translation_base.txt` + per-lang prompty (`bulgarski.txt`, `niemiecki.txt`, etc.)
📤 Output
- `04_translations.csv` z 23 kolumnami `desc_<lang>` + `name_<lang>` + `meta_<lang>`
- Per-lang: `04_translations_<lang>.csv` osobno
⚠️ Ryzyka
- Polish leak w niemieckim — najczęstszy problem (~5% bez retry)
- Truncation dla bardzo długich opisów → retry z 32k tokens
- Brak h3 struktury → flag, retry
- Dla 5 unsup langs (lav/ltz/mlt/por/slv) Gemini bywa słabszy → ekstra validation
🔔 Slack: Start + procent done co 30 min (dla operatora context awareness)
💾 Archiwum:
04_translations.csv04_translations_per_lang/*.csv04_polish_leak_report.csv5
Składanie widgetu Bioshi Score per język
🤖 auto
⏱ ~5 min (deterministyczne)
📥 2 input(ów) · 📤 1 output(ów) · 📝 5 sub-kroków
▼
📝 Sub-kroki
- 5.1 Per produkt × per lang renderuje widget HTML z `WIDGET_TRANSLATIONS[lang]` + grade z step 3
- 5.2 Widget = Sora,Inter font, @import Google Fonts, kolorowy badge A-E, 4 filary (Skład/Pochodzenie/Przetworzenie/Opakowanie)
- 5.3 Składanie: `widget_lang + '<!-- Opis produktu -->' + body_lang`
- 5.4 Validate HTML balance (otwarte vs zamknięte taggi)
- 5.5 Output 24 kolumn × N produktów
📥 Input
- `04_translations.csv` + `03_bioshi_score_approved.csv`
- Dict `WIDGET_TRANSLATIONS` (23 langs)
📤 Output
- `05_final_widgets.csv` z 24 kolumnami `desc_<lang>_full` (widget + body)
⚠️ Ryzyka
- Niezbalansowany HTML → auto-fix przez `balance_html()` (z lekcji 13k uploadu)
🔔 Slack: Tylko log koniec.
💾 Archiwum:
05_final_widgets.csvM4
MANUAL: Final approval przed uploadem
👤 manual
⏱ 5-20 min
📥 1 input(ów) · 📤 2 output(ów) · 📝 4 sub-kroków
▼
📝 Sub-kroki
- M4.1 Operator dostaje Slack 'Final approval needed'
- M4.2 Strona pokazuje preview 5 losowych produktów per język (24 panele × 5 = 120 podglądów)
- M4.3 Operator może zaznaczyć: 'Upload tylko lang-default' albo 'Upload + per-shop overrides'
- M4.4 Po kliknięciu 'Zatwierdź upload' bot startuje step 6
📥 Input
- `05_final_widgets.csv` + losowe sample 5 produktów po pełnym renderze
📤 Output
- Aprobata operatora w UI
- Lista shopów do uploadu (default: wszystkie 22)
⚠️ Ryzyka
- Operator wciśnie upload bez review — flag w archive
👤 Akcje operatora
- Otworzyć preview 5 produktów
- Sprawdzić wybrane języki (np. ger, eng, ita, rum, fin)
- Wybrać scope (lang-default vs per-shop)
- Zatwierdzić
🔔 Slack: DM 'Final approval needed' + przypomnienia.
💾 Archiwum:
M4_approval_log.json6
Upload do IdoSell (REST API)
🤖 auto
⏱ 10-30 min (zależy od liczby produktów × shopów)
📥 3 input(ów) · 📤 2 output(ów) · 📝 6 sub-kroków
▼
📝 Sub-kroki
- 6.1 Per produkt: batch PUT do `/api/admin/v8/products/products`
- 6.2 Struktura: `productLongDescriptions.productLongDescriptionsLangData[i]` z `shopId` jeśli per-shop override
- 6.3 Batch 30 produktów per request (z lekcji 13k)
- 6.4 Rate limit: max 10 concurrent, sleep 0.5s
- 6.5 Verify: po uploadzie, dla losowych 100 produktów refetchuje z IdoSell i porównuje (z normalizacją: usuń `<!--StartFragment-->`, normalize quote style)
- 6.6 Final report — sukces ratio per lang per shop
📥 Input
- `05_final_widgets.csv`
- Lista shopów do uploadu
- IdoSell API key z Secret Manager
📤 Output
- Log uploadu `06_idosell_upload_log.csv` (HTTP statusy per produkt × per lang × per shop)
- Verify report `06_verify_report.csv` (sample 100 produktów, re-fetch i compare)
⚠️ Ryzyka
- Shop override blokuje lang-default → trzeba uploadować obu (z lekcji audytu 44531)
- IdoSell rate limit (429) → exponential backoff
- Empty string + shopId usuwa override ALE może też usunąć lang-default w niektórych przypadkach (z lekcji)
🔔 Slack: Start + progress co 5 min + final report z stats
💾 Archiwum:
06_idosell_upload_log.csv06_verify_report.csv06_final_stats.json✓
Zakończenie i raport
🤖 auto
⏱ ~1 min
📥 1 input(ów) · 📤 3 output(ów) · 📝 5 sub-kroków
▼
📝 Sub-kroki
- ✓.1 Pakowanie wszystkich plików etap'ów do ZIP
- ✓.2 Upload ZIP do Google Drive `Bioshi Products/Nowe produkty/{run_id}/`
- ✓.3 Aktualizacja Firestore: `runs/{run_id}` status `done`, `finished_at`, `download_url`
- ✓.4 Wpis w timeline collection
- ✓.5 Slack message z linkami: timeline event, Drive ZIP, IdoSell admin
📥 Input
- Wszystkie pliki z runu
📤 Output
- ZIP archive `run_{run_id}_complete.zip` na Google Drive
- Wpis w timeline `evt-{timestamp}`
- Slack final message
🔔 Slack: Final notification z linkami.
💾 Archiwum:
run_{run_id}_complete.zip
💡
Tryb manualny ma cooldown 1h przed pierwszym przypomnieniem, potem co 1h. W godzinach ciszy (18:00-08:00) bot czeka do rana, chyba że włączony Tryb Boguś Pracoholik w Ustawieniach.
Diagram przepływu danych
DFDflowchart LR
M([Merkury]):::supplier
BP([BioPlanet]):::supplier
EW([EkoWital]):::supplier
OP([Operator]):::user
GS[(GCS archives)]:::store
GEM([Gemini Batch/Multimodal]):::ext
ID[(IdoSell)]:::store
SL([Slack]):::ext
M --> P0((Konsolidacja))
BP --> P0
EW --> P0
P0 --> GS
OP -.->|edytuj drzewko| P0
GS --> P1((Klasyfikacja))
P1 --> GEM
GEM --> P1
P1 --> GS
OP -.->|review| P1
GS --> P2((Opisy PL))
P2 --> GEM
GEM --> P2
P2 --> P3((Bioshi Score multimodal))
P3 --> GEM
P3 --> P4((Translate 23 langs))
P4 --> GEM
P4 --> P5((Widget × 24))
P5 --> P6((Upload IdoSell))
OP -.->|approval| P6
P6 --> ID
P6 --> SL
SL -.-> OP
classDef supplier fill:#fef3c7,stroke:#f59e0b,color:#78350f;
classDef user fill:#dbeafe,stroke:#3b82f6,color:#1e3a8a;
classDef ext fill:#ede9fe,stroke:#8b5cf6,color:#5b21b6;
classDef store fill:#f1f5f9,stroke:#94a3b8,color:#334155;
⏳
Ładuję diagram...
Legenda:
External
Datastore
Proces
⌨️ scroll = zoom · click&drag = pan · folder Nowe produkty
Materiał wideo
▶ YOUTUBE
YouTube ID: CF-1I_IZEu8 · kliknij obraz żeby odtworzyć
Historia tego trybu
📦
Jeszcze nie używałeś tego trybu