Przejdź do treści

03 — Agents Specification

Insurance Intelligence System


Zasady ogólne agentów

  • Każdy agent ma jedną odpowiedzialność (Single Responsibility Principle)
  • Agenci komunikują się przez bazę danych — nie bezpośrednio między sobą
  • Każdy agent loguje swoją pracę — co zebrał, co przetworzymy, ile kosztowało
  • Agenci są idempotentni — uruchomienie dwa razy nie duplikuje danych
  • Kod każdego agenta mieszka w osobnym folderze na VPS
  • Zasada wydzielania: każda nowa funkcjonalność jest weryfikowana pod kątem czy nie powinna być osobnym agentem. Kryterium: czy zadanie ma inny trigger, inne zasoby i inną odpowiedzialność niż istniejący agent → jeśli tak, wydzielić.

Orkiestracja — n8n

Wszystkie agenty są orkiestrowane przez n8n działające na VPS (Docker, port 5678, za nginx/Traefik).

n8n zapewnia: - Harmonogramy (cron) dla każdego agenta niezależnie - Wyzwalacze zdarzeniowe (nowy plik w source_documents → uruchom Extractor) - Retry przy awarii (3 próby z backoffem) - Powiadomienia o błędach (email/webhook) - Widoczność pipeline'u — wizualny widok co i kiedy się wykonało - Logowanie czasu wykonania i statusu każdego kroku

Lokalizacja: /opt/n8n/ — Docker Compose, dane w wolumenie URL: https://n8n.intelligence.srv1298625.hstgr.cloud (Traefik HTTPS)

Główne workflow n8n

[Cron 06:00 UTC] → Agent 1 Collector (HTTP POST /collect)
                          │ po zakończeniu (webhook)
                          ▼
                   Agent 2 Extractor (HTTP POST /extract)
                          │ po zakończeniu (webhook)
                          ▼
                   Agent 3 Classifier (HTTP POST /classify)
                          │ po zakończeniu (webhook)
                          ▼
              [Cron poniedziałek 07:00 UTC]
                          │
                          ▼
                   Agent 4 Analyst (HTTP POST /analyze)
                          │ po zakończeniu (webhook)
                          ▼
                   Agent 5 Reporter (odświeżenie dashboardu)

Każdy agent zgłasza zakończenie przez webhook do n8n — n8n decyduje o uruchomieniu kolejnego.


Pipeline danych — przepływ między agentami

Źródła zewnętrzne (YouTube, KNF, PIU, NBP, portale, podcasty)
          │
          ▼ Agent 1 — Collector
    raw_signals          — metadane, tytuły, linki, checksum
    source_documents     — surowe pliki (BYTEA): XLSX, PDF, MP3
    transcriptions       — tekst z YouTube i Whisper
          │
          ▼ Agent 2 — Extractor
    extracted_signals    — znormalizowany tekst + metryki ze wszystkich formatów
    w1_market_position   — metryki finansowe (GWP, udziały rynkowe)
    w2_financials        — wskaźniki rentowności (COR, loss ratio)
    ...w3-w7             — pozostałe warstwy analityczne
          │
          ▼ Agent 3 — Classifier
    classified_signals   — segment, relevance_score, is_noise, TU, tematy
          │
          ▼ Agent 4 — Analyst
    insights             — trendy, anomalie, ruchy konkurencji, regulatory_change
          │
          ▼ Agent 5 — Reporter
    reports              — HTML dashboard + PDF raporty

Agent 1 — Collector

Odpowiedzialność: Zbieranie surowych danych ze wszystkich źródeł zewnętrznych. Collector nie przetwarza treści — tylko pobiera i zapisuje.

Lokalizacja: /opt/youtube-mcp/ (aktywny), /opt/regulatory-mcp/ (aktywny), kolejne źródła w /opt/[source]-mcp/

Uruchomienie: n8n cron, codziennie o 06:00 UTC

Źródła (kolejność wdrożenia): 1. ✅ YouTube Data API v3 — wywiady, konferencje, podcasty 2. ✅ KNF (knf.gov.pl) — komunikaty, raporty kwartalne (biuletyny XLSX) 3. 🔲 PIU (piu.org.pl) — statystyki rynku, raporty kwartalne 4. 🔲 NBP (nbp.pl) — raporty o stabilności finansowej 5. 🔲 RSS podcastów — metadane odcinków + pobieranie MP3 6. 🔲 Portale branżowe (gu.com.pl, nowości ubezpieczeniowe) — scraping artykułów 7. 🔲 Relacje inwestorskie TU — raporty roczne PDF

Szczegółowe zadania:

DLA KAŻDEGO ŹRÓDŁA:
1. Pobierz listę aktywnych fraz / URL-i z tabeli query_config
2. Wywołaj API lub scraper źródła
3. Dla każdego wyniku → oblicz checksum(source_id)
4. Jeśli checksum nie istnieje w raw_signals → INSERT do raw_signals
5. Jeśli wynik zawiera plik (PDF/XLSX) → pobierz i zapisz do source_documents (BYTEA)
6. Jeśli wynik zawiera transkrypcję (YouTube) → zapisz do transcriptions
7. Wyciągnij URL-e z opisów → zapisz do raw_signals.description_urls
8. Jeśli istnieje → pomiń (deduplikacja przez checksum)
9. Wyślij webhook do n8n: {status: done, new_records: N, files: M}
10. Zapisz log operacji

Deduplikacja: SHA256 z source_id — zapobiega podwójnemu pobraniu tego samego zasobu.

Limity API: - YouTube Data API v3: 10 000 jednostek/dzień (search = 100 jedn.) — przy 38 frazach × 100 = 3800 jedn./dzień - KNF: scraping HTML bez limitu, z opóźnieniem 2s między żądaniami

Output: - Tabela raw_signals — metadane wszystkich zebranych pozycji - Tabela source_documents — pliki binarne (XLSX, PDF, MP3) - Tabela transcriptions — tekst z YouTube i Whisper


Agent 2 — Extractor

Odpowiedzialność: Konwersja surowych danych z każdego formatu na ustrukturyzowany tekst i metryki. Extractor nie ocenia wartości danych — tylko normalizuje format i wyciąga liczby/sygnały.

Lokalizacja: /opt/insurance-extractor/

Uruchomienie: n8n webhook po zakończeniu Collectora (event-driven, nie cron)

Obsługiwane formaty:

Format Źródło Narzędzie parsowania Output
XLSX KNF, PIU, NBP pandas metryki liczbowe → w1-w7
PDF Raporty roczne TU, KNF pdfplumber tekst → extracted_signals
HTML / artykuł Portale branżowe BeautifulSoup tekst → extracted_signals
Transkrypcja YT tabela transcriptions bezpośrednio tekst sygnały → extracted_signals
Transkrypcja audio Whisper output bezpośrednio tekst sygnały → extracted_signals
RSS / metadane Podcasty XML parser tekst → extracted_signals

Szczegółowe zadania:

DLA KAŻDEGO NOWEGO REKORDU W source_documents / transcriptions:
1. Sprawdź extraction_status = 'pending'
2. Rozpoznaj typ dokumentu (xlsx / pdf / html / transcript / rss)
3. Uruchom odpowiedni normalizer:
   - XLSX → pandas → tabela z kolumnami i wartościami
   - PDF → pdfplumber → czysty tekst + tabele
   - HTML → BeautifulSoup → czysty tekst (bez tagów, reklam, nawigacji)
   - Transkrypcja → tekst już gotowy, chunking na segmenty 2000 znaków
4. Dobierz prompt ekstrakcji do typu dokumentu (Prompt Router)
5. Wyślij do Claude API z wymuszonym JSON output
6. Zapisz wynik:
   - Metryki liczbowe → tabele w1-w7 (dla XLSX/PDF finansowych)
   - Sygnały tekstowe → tabela extracted_signals
7. Zaktualizuj extraction_status = 'done' lub 'failed'
8. Wyślij webhook do n8n: {status: done, extracted: N, failed: M}

Prompt Router — typy promptów:

xlsx_knf_financial  → "Wyciągnij metryki finansowe TU w JSON:
                        {insurer_name, metric_name, value, unit,
                         period_quarter, period_year, confidence}"

pdf_annual_report   → "Wyciągnij z raportu rocznego: wyniki finansowe,
                        strategię, kluczowe decyzje zarządu w JSON"

html_article        → "Wyciągnij sygnały: które TU wymienione, jaka rola,
                        jaki temat, jaki sentiment, parafraza kluczowej treści"

transcript_youtube  → "Wyciągnij ze stenogramu: kluczowe stanowiska,
                        strategiczne deklaracje, metryki wymienione przez mówcę.
                        Zawsze parafraza — nie cytaty 1:1"

Zasada prawna: Każdy tekst zapisany do extracted_signals musi być parafrazą (własnymi słowami Claude), nigdy cytatem 1:1 z oryginału. Ochrona prawna + standard rynkowy (Wisers, Lexalytics).

Output: - Tabela extracted_signals — znormalizowane sygnały tekstowe gotowe do klasyfikacji - Tabele w1w7 — metryki liczbowe z dokumentów finansowych - Status ekstrakcji zaktualizowany w source_documents


Agent 3 — Classifier

Odpowiedzialność: Ocena wartości i kategoryzacja wyekstrahowanych sygnałów — strategiczne vs szum, przypisanie segmentu. Classifier pracuje wyłącznie na danych z extracted_signals — nigdy na surowych plikach.

Lokalizacja: Claude.ai / Claude Code (licencja Accenture) — klasyfikacja zużywa tokeny, działa poza VPS

Uruchomienie: n8n webhook po zakończeniu Extractora

Architektura hybrydowa (Warstwa 1 → Warstwa 2):

Warstwa 1 — Reguły (classification_method = 'rules')

Szybka klasyfikacja oparta na słownikach. Bez kosztów API. Obsługuje ~80% przypadków.

SIGNAL_HIGH = [
    "wyniki", "strategia", "wywiad", "raport", "konferencja",
    "prezes", "wiceprezes", "dyrektor", "członek zarządu",
    "CFO", "CEO", "CTO", "CIO", "CRO",
    "dyrektor sprzedaży", "dyrektor bancassurance",
    "dyrektor likwidacji szkód", "dyrektor transformacji",
    "składka przypisana", "szkodowość", "wynik techniczny",
    "results", "strategy", "interview", "annual report"
]

SIGNAL_NOISE = [
    "kup", "porównaj", "najtańsze", "tanie", "kalkulator",
    "oferta", "promocja", "zniżka", "jak wybrać", "poradnik",
    "dla klientów", "ranking OC"
]

SEGMENT_RULES = {
    "ceo_cfo_tu":     ["prezes", "wiceprezes", "CEO", "CFO", "członek zarządu", "zarząd"],
    "financials_tu":  ["wyniki", "składka", "szkodowość", "wynik techniczny", "raport roczny"],
    "regulators":     ["KNF", "PIU", "NBP", "UFG", "Rzecznik Finansowy", "regulacje"],
    "conferences":    ["konferencja", "kongres", "forum", "panel", "Insurance Congress"],
    "bancassurance":  ["bancassurance", "bank", "PKO BP", "Santander", "Credit Agricole"],
    "intermediaries": ["multiagencja", "broker", "OFWCA", "agent ubezpieczeniowy", "Unilink"],
    "expert_analysis":["podcast", "analiza", "ekspert", "insurtech", "McKinsey", "Deloitte"],
    "eu_groups":      ["Allianz Group", "AXA Group", "Generali Group", "VIG", "Talanx", "CEE"]
}

Wynik Warstwy 1: - relevance_score 0–100 (liczba trafień sygnałów wysokiej wartości) - segment (pierwsza pasująca reguła segmentu) - is_noise (True jeśli trafień SIGNAL_NOISE > SIGNAL_HIGH)

Warstwa 2 — Claude API (classification_method = 'claude_api')

Uruchamiana tylko gdy Warstwa 1 zwraca relevance_score między 30–60 (przypadki graniczne). Obsługuje ~20% rekordów.

Prompt do Claude API:
"Jesteś ekspertem rynku ubezpieczeń w Polsce.
Oceń czy poniższy sygnał zawiera wartościowe informacje
strategiczne dla competitive intelligence.

Typ źródła: {source_type}
Tytuł / nagłówek: {title}
Treść (parafraza, max 500 znaków): {content_preview}

Odpowiedz TYLKO w JSON:
{
  'is_noise': true/false,
  'segment': '[nazwa segmentu]',
  'relevance_score': [0-100],
  'reason': '[max 100 znaków uzasadnienia]',
  'companies_mentioned': ['PZU', 'Warta'],
  'roles_mentioned': ['prezes', 'dyrektor sprzedaży'],
  'topics': ['wyniki finansowe', 'strategia']
}"

Szczegółowe zadania:

DLA KAŻDEGO REKORDU W extracted_signals (status = 'pending_classification'):
1. Uruchom Warstwę 1 (reguły słownikowe)
2. Jeśli relevance_score < 30 → is_noise = TRUE, zapisz, pomiń Warstwę 2
3. Jeśli relevance_score > 60 → zapisz jako wartościowy, pomiń Warstwę 2
4. Jeśli relevance_score 30-60 → uruchom Warstwę 2 (Claude API)
5. Zapisz wynik do classified_signals
6. Zaktualizuj known_sources.quality_score jeśli źródło konsekwentnie wartościowe
7. Wyślij webhook do n8n: {status: done, classified: N, noise: M, api_calls: K}

Ekonomia: Claude API wywoływane tylko dla ~20% rekordów (przypadki graniczne).

Output: Tabela classified_signals — każdy sygnał z segmentem, oceną i metadanymi


Agent 4 — Analyst

Odpowiedzialność: Analiza sklasyfikowanych sygnałów i metryk, wykrywanie wzorców, trendów i anomalii, generowanie insightów łączących dane z wielu źródeł.

Lokalizacja: Claude.ai / Claude Code (licencja Accenture) — analiza zużywa tokeny

Uruchomienie: n8n cron, raz w tygodniu (poniedziałek 07:00 UTC) + na żądanie przez dashboard

Szczegółowe zadania:

ANALIZA TYGODNIOWA:
1. Pobierz wszystkie classified_signals z ostatnich 7 dni
   gdzie is_noise = FALSE i relevance_score >= 60
2. Pobierz nowe metryki z tabel w1-w7 (dodane w ostatnim tygodniu)
3. Grupuj sygnały po: segment, companies_mentioned, topics
4. Wykryj trendy: tematy powtarzające się ≥3 razy w różnych źródłach
5. Wykryj anomalie: nagły wzrost sygnałów o danym TU (>2x tygodniowa średnia)
6. Wykryj ruchy konkurencji: konkretne deklaracje strategiczne TU
7. Wykryj zmiany regulacyjne: nowe komunikaty KNF/PIU z wysokim relevance_score
8. Dla każdego wykrytego wzorca → generuj insight (parafraza, nie cytaty)
9. Generuj embedding (VECTOR) dla każdego insightu → zapis do insights.embedding
10. Zapisz do tabeli insights
11. Wyślij webhook do n8n: {status: done, insights_generated: N}

ANALIZA NA ŻĄDANIE:
- Benchmark wybranych TU (porównanie metryk w1-w7)
- Deep-dive na konkretny temat / TU / okres
- Odpowiedź na pytanie semantyczne przez pgvector

Typy insights: - trend — temat powtarzający się ≥3 razy w tygodniu w różnych źródłach - anomaly — nagły wzrost sygnałów o danym TU (>2x średnia tygodniowa) - competitor_move — konkretna zmiana strategiczna TU (deklaracja, produkt, kanał) - regulatory_change — nowe regulacje lub komunikaty KNF/PIU - benchmark_delta — istotna zmiana metryki TU względem poprzedniego okresu

Zasada intelligence: Insight musi odpowiadać na "dlaczego" — nie tylko "co". Sam COR=89% dla PZU to dana. Wartość zaczyna się gdy Analyst odpowiada "dlaczego COR spadł, co to znaczy dla strategii, jak się ma do konkurencji".

Output: - Tabela insights — insighty z embeddingiem VECTOR (pgvector) - Gotowe do wyświetlenia w dashboardzie i do raportów


Agent 5 — Reporter

Odpowiedzialność: Prezentacja danych — dynamiczny dashboard HTML oraz generowanie raportów PDF na żądanie. Reporter nie analizuje — tylko wizualizuje gotowe dane z insights, classified_signals i w1-w7.

Lokalizacja: /opt/insurance-dashboard/ (nginx serwuje dashboard)

Uruchomienie: Dashboard — ciągły (nginx). Raporty PDF — na żądanie przez UI.

Szczegółowe zadania:

DASHBOARD (ciągły):
1. REST API odczytuje dane z PostgreSQL (insights, classified_signals, w1-w7)
2. Widoki dashboardu odświeżane automatycznie po każdym cyklu Agenta 4
3. Filtrowanie po: TU, segment, okres, typ insightu
4. Wyszukiwanie semantyczne (pgvector) po naturalnym języku

RAPORTY PDF (na żądanie):
1. Właściciel projektu wybiera parametry: TU, okres, typ raportu
2. Reporter pobiera dane z insights + classified_signals + w1-w7
3. Generuje raport przez Claude API (formatowanie, narracja)
4. Zapisuje do tabeli reports + plik PDF
5. Udostępnia do pobrania przez dashboard

TYGODNIOWY DIGEST (automatyczny, poniedziałek 08:00 UTC):
1. Pobierz insights z ostatniego tygodnia (importance >= 3)
2. Wygeneruj HTML digest
3. Wyślij email / webhook (konfigurowany przez właściciela)

Typy raportów: - weekly — automatyczny digest tygodniowy (top insights, alerty) - monthly — automatyczny, pierwszy poniedziałek miesiąca - benchmark — porównanie wybranych TU na podstawie metryk w1-w7 - adhoc — dowolny zakres i temat, na żądanie przez dashboard

Dashboard — widoki: 1. Przegląd tygodnia — top insights, alerty, nowości regulacyjne 2. Mapa TU — aktywność i metryki każdego towarzystwa w danym okresie 3. Segmenty — filtrowanie po: regulacje / zarządy / bancassurance / intermediaries 4. Wyszukiwanie semantyczne — "co mówiono o cenach OC w Q3 2025" (pgvector) 5. Źródła — jakość i aktywność poszczególnych kanałów i stron 6. Raporty — historia wygenerowanych raportów, generowanie nowych

Output: HTML dashboard serwowany przez nginx + PDF raporty w tabeli reports


Feedback loop (uczenie systemu)

Właściciel projektu przez dashboard może: - Oznaczyć sygnał jako szum → manually_reviewed = TRUE, is_noise = TRUE - Oznaczyć kanał jako wartościowy → known_sources.quality_score += 10 - Oznaczyć kanał jako szum → known_sources.is_monitored = FALSE - Skorygować klasyfikację segmentu → classification_method = 'manual'

Te korekty wracają jako dane treningowe dla Warstwy 1 Agenta 3 (Classifier) przy kolejnej iteracji konfiguracji reguł. n8n co miesiąc generuje raport jakości klasyfikacji (precision/recall na podstawie korekt manualnych).