NordaGPT Identity, Memory & Performance — Design Spec
Data: 2026-03-28
Zgłaszający: Jakub Pornowski (prędkość, tożsamość), Maciej Pienczyn
Status: Draft
Cel
Przekształcić NordaGPT z anonimowego chatbota w spersonalizowanego asystenta, który:
- Wie kim jest użytkownik i personalizuje odpowiedzi
- Pamięta poprzednie rozmowy i buduje profil użytkownika
- Odpowiada 3-5x szybciej dzięki smart routingowi i streamingowi
- Kosztuje 70-80% mniej przy skali 3,000 zapytań/dzień
Skala docelowa
- 100-150 aktywnych użytkowników dziennie
- 10-20 pytań na użytkownika
- 1,500-3,000 zapytań/dzień
- Budżet: sponsorowany przez INPI, w przyszłości przeniesiony na użytkowników
Filar 1: Tożsamość użytkownika
Dane wstrzykiwane do promptu
# AKTUALNY UŻYTKOWNIK
Rozmawiasz z: {user.name}
Email: {user.email}
Firma: {company.name} (ID {company.id}) — kategoria: {company.category}
Rola w firmie: {user.company_role}
Członek Izby: {tak/nie}
Rola w Izbie: {user.chamber_role lub "—"}
Powiązane firmy: {lista z UserCompanyPermissions}
Na portalu od: {user.created_at}
Zmiany w API
send_message() — nowy parametr user_context: dict (zamiast samego user_id: int)
- Route
chat_send_message() buduje user_context z current_user + current_user.company + UserCompanyPermissions
Zachowanie AI
- Pierwsza wiadomość w konwersacji: "Cześć {imię}, w czym mogę pomóc?"
- Na "co wiesz o mnie?": wypisuje dane z profilu + powiązania firmowe + fakty z pamięci
- Kontekstowe odpowiedzi uwzględniające firmę i rolę użytkownika
Filar 2: Pamięć użytkownika
Nowe tabele
ai_user_memory
| Kolumna |
Typ |
Opis |
| id |
SERIAL PK |
|
| user_id |
FK users.id (NOT NULL) |
Właściciel pamięci |
| fact |
TEXT (NOT NULL) |
Treść faktu |
| category |
VARCHAR(50) |
interests / needs / contacts / insights |
| source_conversation_id |
FK ai_chat_conversations.id |
Z której rozmowy |
| confidence |
FLOAT DEFAULT 1.0 |
Maleje z czasem |
| created_at |
TIMESTAMP DEFAULT NOW() |
|
| expires_at |
TIMESTAMP |
created_at + 12 miesięcy |
| is_active |
BOOLEAN DEFAULT TRUE |
Użytkownik może dezaktywować |
Indeksy: (user_id, is_active, confidence DESC), (expires_at) dla cleanup crona.
ai_conversation_summary
| Kolumna |
Typ |
Opis |
| id |
SERIAL PK |
|
| conversation_id |
FK UNIQUE ai_chat_conversations.id |
1:1 z konwersacją |
| user_id |
FK users.id |
|
| summary |
TEXT |
Podsumowanie 1-3 zdania |
| key_topics |
JSONB |
["budowlane", "TERMO", "PEJ"] |
| created_at |
TIMESTAMP DEFAULT NOW() |
|
| updated_at |
TIMESTAMP |
|
Generowanie pamięci (asynchroniczne)
- Po wysłaniu odpowiedzi AI → Flash-Lite analizuje rozmowę w tle (nie blokuje response)
- Wyciąga nowe fakty → INSERT do
ai_user_memory (deduplikacja po treści)
- Co 5 wiadomości w konwersacji → aktualizuje
ai_conversation_summary
- Przy opuszczeniu konwersacji → finalne podsumowanie
Prompt do ekstrakcji faktów (Flash-Lite)
Na podstawie tej rozmowy, wyciągnij kluczowe fakty o użytkowniku.
Zwróć JSON array: [{"fact": "...", "category": "interests|needs|contacts|insights"}]
Zasady:
- Tylko nowe, nietrywialne fakty (nie "zapytał o firmę X")
- Fakty przydatne w przyszłych rozmowach
- Max 3 fakty na rozmowę
- Nie duplikuj istniejących faktów: {existing_facts}
Wstrzykiwanie do promptu
- Top 10 najświeższych aktywnych faktów (~500 tokenów)
- Podsumowania ostatnich 5 konwersacji (~750 tokenów)
- Razem: ~1,250 tokenów
UI
- Ustawienia czatu → "Co NordaGPT o mnie wie"
- Lista faktów z datą i źródłem (link do konwersacji)
- Przycisk "Usuń" przy każdym fakcie (soft delete: is_active=False)
- Lista podsumowań konwersacji
RODO
- Pamięć prywatna — dostępna TYLKO dla właściciela (filtr user_id na każdym query)
- Użytkownik ma pełną kontrolę: podgląd, usuwanie
- Przy usunięciu konta → CASCADE DELETE pamięci
- Fakty nie zawierają danych wrażliwych (PESEL, konta bankowe) — ten sam filtr RODO co na wiadomościach
Filar 3: Smart Router
Przepływ zapytania
Użytkownik pisze pytanie
↓
[1] Smart Router (3.1 Flash-Lite, ~1-2s)
Input: pytanie + tożsamość + pamięć + lista kategorii danych
Output JSON: {
"complexity": "simple|medium|complex",
"data_needed": ["companies:IT", "events", "user_memory"],
"model": "flash-lite|flash|flash-high"
}
↓
[2] Context Builder
Ładuje TYLKO potrzebne dane z bazy
↓
[3] Main Model (dobrany przez Router)
Prompt: tożsamość + pamięć + wybrane dane + historia (~15-25k tokenów)
↓
[4] Streamed response → użytkownik
↓
[5] Memory Extractor (Flash-Lite, async, w tle)
Prompt routera
Jesteś routerem zapytań NordaGPT. Przeanalizuj pytanie użytkownika i zdecyduj:
Użytkownik: {name} z firmy {company}
Pamięć: {facts_summary}
Pytanie: {user_message}
Zwróć JSON:
{
"complexity": "simple|medium|complex",
"data_needed": ["categories from list below"],
"reasoning": "one sentence why"
}
Dostępne kategorie danych:
- companies_all: wszystkie 150 firm (30k tokenów) — porównania, przeglądy
- companies_filtered:{category}: firmy z danej kategorii (2-5k)
- companies_single:{slug}: jedna firma (0.5k)
- events: nadchodzące wydarzenia (2k)
- news: aktualności i PEJ (3k)
- classifieds: ogłoszenia B2B (2k)
- forum: tematy forum (5k)
- company_people: zarząd/KRS (5k)
- registered_users: użytkownicy portalu (3k)
- social_media: profile social media firm (2k)
- audits: SEO/GBP wyniki (2k)
ZAWSZE dodawane (nie musisz wybierać): tożsamość, pamięć, historia rozmowy.
Wybierz MINIMUM potrzebnych kategorii. Jeśli nie jesteś pewien, dodaj więcej.
Kategorie danych i triggerowanie
| Kategoria |
Triggery (słowa kluczowe) |
~Tokenów |
| companies_all |
"firmy", "porównaj", "wszystkie", "ile firm" |
~30k |
| companies_filtered |
nazwa kategorii, "budowlane", "IT" |
~2-5k |
| companies_single |
nazwa firmy, slug |
~0.5k |
| events |
"spotkanie", "wydarzenie", "kalendarz" |
~2k |
| news |
"aktualności", "nowości", "PEJ", "atom" |
~3k |
| classifieds |
"ogłoszenie", "B2B", "zlecenie", "oferta" |
~2k |
| forum |
"forum", "dyskusja", "temat", "wątek" |
~5k |
| company_people |
"zarząd", "KRS", "właściciel", "udziały" |
~5k |
| registered_users |
"kto jest", "użytkownicy", "profil" |
~3k |
Fallback
Jeśli router zwróci błąd lub timeout → ładuj wszystkie dane (obecne zachowanie). Bezpieczne, wolniejsze.
Dobór modelu
| Complexity |
Model |
Thinking |
Oczekiwany czas |
| simple |
3.1 Flash-Lite |
minimal |
2-3s |
| medium |
3 Flash |
low |
4-6s |
| complex |
3 Flash |
high |
8-12s |
Filar 4: Streaming + UI
Backend
- Nowy endpoint SSE:
POST /api/chat/<id>/message/stream
- Używa
gemini_service.generate_text(stream=True)
- Zwraca
text/event-stream z chunkami: data: {"type": "token", "content": "..."}\n\n
- Events:
token (tekst), thinking (model myśli), done (koniec + metadata), error
- Stary endpoint
/api/chat/<id>/message pozostaje jako fallback (non-streaming)
Frontend
fetch() z ReadableStream (szersza kompatybilność niż EventSource dla POST)
- Tekst pojawia się słowo po słowie w bańce czatu
- Animacja "myślenia" gdy model przetwarza (pulsujące kropki)
- Po
done → zapisz pełną odpowiedź do DOM, pokaż metadata (czas, model)
Wskaźniki w UI
- Przy prostych pytaniach: brak wskaźnika myślenia, odpowiedź natychmiastowa
- Przy złożonych: "NordaGPT analizuje..." z animacją (2-3s), potem streaming tekstu
Szacunek kosztów (3,000 zapytań/dzień)
| Składnik |
Koszt/zapytanie |
Dziennie |
Miesięcznie |
| Router (Flash-Lite) |
~$0.001 |
$3 |
$90 |
| Main model (mix) |
~$0.01-0.03 |
$30-90 |
$900-2,700 |
| Memory extraction (async) |
~$0.001 |
$3 |
$90 |
| Suma |
|
$36-96 |
$1,080-2,880 |
vs. obecne podejście bez optymalizacji przy tej skali: ~$9,000-13,500/mies.
Oszczędność: 70-80%
Szacunek prędkości po zmianach
| Metryka |
Obecna |
Po zmianach |
| Średni czas odpowiedzi |
20.5s |
3-6s |
| Perceived latency (streaming) |
20.5s |
1-2s |
| P95 |
34.5s |
8-12s |
| Najwolniejsze (complex) |
46s |
12-15s |
Migracje SQL
091_create_ai_user_memory.sql — tabela + indeksy
092_create_ai_conversation_summary.sql — tabela + indeksy
Pliki do zmiany
| Plik |
Zmiana |
database.py |
Nowe modele: AIUserMemory, AIConversationSummary |
nordabiz_chat.py |
Smart Router, Context Builder, Memory Extractor, user_context |
gemini_service.py |
Streaming support w generate_text (już częściowo jest) |
blueprints/chat/routes.py |
Nowy endpoint streaming, user_context budowanie |
templates/chat.html |
Streaming UI, animacja myślenia |
static/js/chat.js lub inline |
ReadableStream handler |
Nowy: smart_router.py |
Logika routera (prompt, parsing, fallback) |
Nowy: memory_service.py |
Ekstrakcja faktów, podsumowania, CRUD pamięci |
Nowy: context_builder.py |
Selektywne ładowanie danych na podstawie decyzji routera |
Kolejność wdrażania
- Tożsamość użytkownika — najprostsza, natychmiastowy efekt "wow"
- Smart Router + Context Builder — redukcja kosztów i poprawa prędkości
- Streaming — perceived latency drop
- Pamięć użytkownika — wymaga nowych tabel, async pipeline, UI