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 |
| 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 w1–w7 — 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).