Przejdź do treści

Szablon: Imiona psów (Dog name extraction → Pupprint)

Gotowy scenariusz „Dog name extraction (cards)" wyciąga imię psa z zamówienia personalizowanego (mail z Allegro / Erli / sklepu) i dodaje wpis do kolejki Pupprint — modułu generującego naklejki A4 z imionami psów.

Skąd biorą się te zamówienia

Klient zamawia spersonalizowaną kartę z imieniem psa. W mailu-zgłoszeniu z platformy podaje konkretne imię („Burek", „Reksio", lub kilka imion oddzielonych przecinkiem). Personalizowane SKU mają w katalogu Convex tag PERSONALIZACJA*. Scenariusz parsuje HTML maila (regex), liczy ile naklejek trzeba wydrukować, prosi LLM o wyciągnięcie imion i kolejkuje wpisy w puppy_names. Gdy uzbiera się 6 imion (rozmiar big) lub 9 (small), Pupprint generuje arkusz A4.


Co dostarcza szablon

Aktualnie operator robi to tak:

  1. Otwiera maila zamówienia.
  2. Czyta SKU produktów (z HTMLa, format <p id="sku">2 x KIN130/105CZE</p>).
  3. Liczy, ile imion ma wpisać.
  4. Czyta imię psa (<p id="name">Burek</p>).
  5. Wpisuje do panelu Pupprint: imię + rozmiar (big/small) + ilość kart.
  6. Czeka, aż uzbiera się arkusz, klika „Generuj".

Szablon automatyzuje 1–5. Punkt 6 (generacja arkusza) chodzi sam — Pupprint ma cron flush_expired_batches, który zamyka kolejki po określonym czasie.


Graf scenariusza

graph TD
    src[source<br/><i>email</i>] --> filt[filter_dog<br/>Subject ma 'IMIĘ PSA']
    filt -->|raise_on_skip<br/>kończy cicho| stop1((filtered_out))
    filt --> parse[parse_cards<br/>Parse SKU × tag]
    parse --> llm[llm_dog_name<br/>LLM: rozmiar + imiona]
    llm --> append[append_card<br/>Pupprint queue]
    append --> mark[mark_read<br/>Gmail: Seen]
    mark --> done[mark_resolved<br/>Status: resolved]

7 węzłów + 6 krawędzi. Source (korzeń) → FilterParse personalization cardsLLM extractAppend puppy nameMark email readUpdate status.


Krok 1 — sklonuj szablon

W panelu Obsługa Klienta → Scenariusze:

  1. Znajdź kartę „Dog name extraction (cards)" (żółte tło, Starter C10).
  2. Edytuj → zmień nazwę na np. „Imiona psów — produkcja".
  3. Zapisz bez aktywacji.

Krok 2 — pasek triggera

  • Źródło: Email (Gmail / Mailcow).
  • Konto: konkretna skrzynka powiązana z platformą sprzedaży (np. konto Gmail, do którego Allegro / Erli wysyłają zgłoszenia o nowych zamówieniach).
  • Gdy nastąpi: Nowe zgłoszenie (new_issue).

Konto MUSI być wybrane

Inaczej scenariusz odpali na każdej skrzynce, w tym Twojej osobistej. Wpisanie konkretnego konta to też dodatkowa warstwa bezpieczeństwa — Twoje prywatne maile do support@ zostaną pominięte.


Krok 3 — filtr filter_dog

Domyślne ustawienia:

  • Pole: subject
  • Operator: contains
  • Wartość: IMIĘ PSA
  • Uwzględniaj wielkość liter: false
  • Zgłoś sentinel przy skip: true
  • Powód skip: Subject does not contain 'IMIĘ PSA'

Co warto zmienić:

  • raise_on_skip = true to kluczowe — gdy temat nie pasuje, scenariusz kończy cicho (status=filtered_out), nie zaśmieca DLQ. Nie zmieniaj tego.
  • Wartość — dostosuj do tego, jak Twoja platforma oznacza zamówienia z personalizacją. Allegro mailowe mają w temacie IMIĘ PSA (wielkimi literami) — wynika z regexu w parse_personalization_cards. Erli ma podobne, ale sprawdź.

Krok 4 — parsowanie kart parse_cards

Węzeł parse_personalization_cards nie ma żadnych pól — robi swoje deterministycznie:

  1. Z HTMLa wszystkich wiadomości w wątku wycina paragrafy <p id="...name"> (imiona) i <p id="...sku"> (SKU + ilość).
  2. Sprawdza w katalogu Convex (marketplace/products), czy SKU ma tag pasujący do PERSONALIZACJA* (case-insensitive).
  3. Sumuje ilości tych spersonalizowanych SKU → expected_cards.

Wynik (do wglądu po teście węzła):

{
  "structured": true,
  "expected_cards": 4,
  "candidate_names": ["Burek", "Reksio", "Łatka", "Mruczek"],
  "personalized_skus": [
    {"sku": "KIN130/105CZE", "count": 2, "name": "Karta zwykła czerwona DUŻA"},
    {"sku": "KIN130/106NIE", "count": 2, "name": "Karta zwykła niebieska DUŻA"}
  ],
  "non_personalized_skus": [],
  "unknown_skus": []
}

structured = true znaczy: HTML miał rozpoznane paragrafy. false znaczy: maile bez tej struktury (zwykła wiadomość od klienta) — downstream LLM dostaje pusty expected_cards i pracuje w trybie unstructured (jedno imię, koniec).


Krok 5 — LLM llm_dog_name

Domyślny prompt jest długi i precyzyjny — wynik wieloiteracyjnego dostrojenia (TASK-295). Kluczowe rzeczy:

Pracujesz z zamówieniem na spersonalizowaną kartę z imieniem psa.
Na podstawie TEMATU wiadomości ustal rozmiar kartonu:
  temat zawierający "DUŻE" → size="big"
  temat zawierający "MAŁE" → size="small"
  Bez markera → domyślnie "big".

Dane z katalogu (mogą być puste dla wiadomości bez struktury HTML):
expected_cards={{results.parse_cards.expected_cards}}
candidate_names={{results.parse_cards.candidate_names}}
personalized_skus={{results.parse_cards.personalized_skus}}

Reguły dla pola names:
- candidate_names to TEKST wyciągnięty regex-em z paragrafu <p id="name">.
  NIE jest to gwarantowane imię — klient mógł wpisać URL, zdanie, śmieci.
  Twoim zadaniem jest ocena semantyczna:
  · Pojedyncze słowo (1–3 krótkie) → ZAWSZE imię psa, choćby brzmiało dziwnie
    (GRUBY, KULKA, PUCEK, TAKO, MICA — wszystko poprawne przezwiska).
  · URL, długie zdanie, oczywisty nie-imię → wyciągnij jak najlepiej.
- expected_cards > 0 → zwróć DOKŁADNIE tyle imion (możesz powtarzać).
- expected_cards puste → wstaw imię (lub kilka połączonych w jeden string,
  np. "Burek i Reksio") jako pierwszy element tablicy.
- Zachowaj polskie znaki i emotki, jeśli klient użył.

Odpowiedz JSON-em:
{"fields": {"size": "big"|"small", "names": [...]}, "confidence": 0..1}

Czego nie zmieniaj:

  • Reguła „pojedyncze słowo = zawsze imię" — bez tego LLM zaczyna „poprawiać" („GRUBY → niepoprawne, użyję Reksio").
  • Cycle-padding (powtarzanie imion gdy expected_cards > liczba imion) — bez tego brakuje naklejek.
  • Zachowywanie emotek — klient płaci za personalizację 1:1.

Co możesz zmienić:

  • Dodatkowy kontekst (np. „klienci sklepu zoologicznego, imiona zwykle 1-2 sylaby") — pomaga przy nietypowych imionach.
  • Threshold confidence — domyślnie 0.7, raise_on_low_confidence: true. Niżej = więcej automatyzacji, więcej błędów. Wyżej = więcej eskalacji, mniej błędów.

Krok 6 — append_card

Dodaje wpisy do puppy_names (kolejki Pupprint):

  • Tablica imion: {{results.llm_dog_name.fields.names}} — placeholder.
  • Oczekiwana liczba kart: {{results.parse_cards.expected_cards}} — placeholder. Może być pusty (LLM unstructured).
  • Rozmiar karty: {{results.llm_dog_name.fields.size}}.

Logika reconciliacji (zaszyta w append_puppy_name.py):

Stan wejścia Co robi
expected_cards = null (unstructured) Każde imię = jedna karta.
expected_cards = 0 Rzuca błąd („structured + 0 personalized SKUs").
expected_cards = N, len(names) = N Pasuje 1:1.
expected_cards = N, len(names) > N Rzuca błąd („more names than cards").
expected_cards = N, 1 ≤ len(names) < N Cycle-pad: powtarza imiona aż uzbiera się N.
expected_cards = N, len(names) = 0 Rzuca błąd („zero names").

Błąd → przebieg trafia do DLQ → operator widzi w panelu Pupprint zakładkę „Wymaga uwagi".


Krok 7 — mark_read (oznacz Gmail jako przeczytane)

Brak pól. Po sukcesie scenariusza Gmail wątek dostaje flag \Seen przez IMAP. To jedyne miejsce, gdzie scenariusz pisze do platformy poza akcjami CS — patrz Source setup → Polityka read-only.

Operator może traktować Gmail jako kolejkę: wszystko nieprzeczytane = jeszcze nie obsłużone scenariuszem.


Krok 8 — test pojedynczego węzła

Krytyczne dla tego scenariusza, bo logika reconciliacji w append_card jest skomplikowana.

  1. Otwórz scenariusz w edytorze.
  2. Kliknij parse_cardsTest węzła → wybierz prawdziwe zgłoszenie z personalizacją (np. mail z Allegro o zamówieniu z KIN130). Sprawdź expected_cards i candidate_names.
  3. Kliknij „Do upstream" — wynik wstawi się do mock_results.
  4. Kliknij llm_dog_nameTest węzła → ustaw mock_issue z prawdziwym mailem, mock_results z poprzedniego kroku → uruchom. Sprawdź fields.names i fields.size.
  5. Zwróć uwagę na confidence:
    • 2 (certain) — LLM jest pewien, scenariusz pójdzie do append_card.
    • 1 (vague) — LLM zwraca, ale niepewnie. Z domyślnym raise_on_low_confidence: true przebieg trafia do failure_handler i cs_dead_letters.
    • 0 (no_name) — LLM nie znalazł imienia, eskalacja.

Krok 9 — aktywacja

  1. Zaznacz Aktywny + Zapisz.
  2. Test na żywo: zamów coś personalizowanego u siebie (lub poproś operatora) — najlepiej z dziwnym imieniem („Pucek 🐶") żeby zweryfikować kompletny flow.
  3. /marketplace/history/runs → poczekaj 5–6 min → otwórz przebieg → status success.
  4. Automatyzacje → Imiona psów → sprawdź, czy nowe wpisy są w kolejce z prawidłowym imieniem i rozmiarem.

Krok 10 — monitoring na codzień

Po aktywacji obserwuj przez kilka tygodni:

  • /marketplace/history/runs filtrowane po nazwie scenariusza — ile success, ile failed.
  • DLQ (/marketplace/history/dlq) — czyść co tydzień, zapisuj patterny błędów.
  • Pupprint → „Wymaga uwagi" — wpisy ze scenariusza, które trafiły do failure-mode (np. LLM zwrócił 0 imion). Operator decyduje ręcznie.

Najczęstsze problemy wczesnym etapie:

Symptom Przyczyna Naprawka
parse_cards zwraca unknown_skus SKU nie ma w katalogu Convex (jeszcze niezsynchronizowany) Synchronizuj BaseLinker → Convex (f/baselinker/sync_catalog).
parse_cards zwraca expected_cards: 0 mimo że produkt jest spersonalizowany Tag w katalogu nie pasuje do regexu /PERSONALIZACJA/i Sprawdź w BaseLinkerze tag — może masz Personalizacja-bluza zamiast PERSONALIZACJA-bluza?
LLM zwraca imię z dodatkowym tekstem (np. „Burek - dziękuję") Prompt potrzebuje doprecyzowania Dodaj w promcie: „NIE dodawaj komentarza, tylko sam imię".
Wszystkie scenariusze trafiają do DLQ z confidence too low Próg za wysoki Zmień confidence_threshold w polach llm_dog_name (przez API — nie ma w UI).

Adaptacja — niestandardowe rozmiary lub typy kart

Dla nowych typów produktów:

  1. Nowy rozmiar (np. medium) — wymaga zmiany zarówno schematu (extract_schema.fields.size.enum), jak i runtime'u append_puppy_name.py. To wymaga deweloperów — zgłoś przez Forgejo.
  2. Nowy typ tagu (zamiast PERSONALIZACJA, np. IMIE-WLASCICIELA) — wymaga zmiany regexu w parse_personalization_cards.py. Też przez deweloperów.
  3. Inny język imion (rumuńskie, niemieckie znaki) — to prompt-only dostosowanie, możesz zrobić sam.

Cross-linki


Dane techniczne

  • Seed: convex/convex/support/seed_templates.ts:buildDogNameExtractionGraph.
  • Tabela kolejki: puppy_names (Convex), zarządzana z Automatyzacje → Imiona psów.
  • Generator arkusza: f/automations/pupprint/generate_grid.flow/.
  • Cron flush: flush_expired_batches — zamyka niedopełnione kolejki po określonym czasie.
  • Tag personalizacji: regex /PERSONALIZACJA/i (case-insensitive) na tags[] produktu w katalogu Convex.