docs: NordaGPT identity, memory & performance design spec
Four pillars: user identity awareness, persistent memory per user, smart router for cost/speed optimization, streaming responses. Target: 3,000 queries/day, 70-80% cost reduction, 3-5x faster responses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
13284ea005
commit
92780fe4b6
@ -0,0 +1,286 @@
|
||||
# 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:
|
||||
1. Wie kim jest użytkownik i personalizuje odpowiedzi
|
||||
2. Pamięta poprzednie rozmowy i buduje profil użytkownika
|
||||
3. Odpowiada 3-5x szybciej dzięki smart routingowi i streamingowi
|
||||
4. 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)
|
||||
|
||||
1. Po wysłaniu odpowiedzi AI → Flash-Lite analizuje rozmowę w tle (nie blokuje response)
|
||||
2. Wyciąga nowe fakty → INSERT do `ai_user_memory` (deduplikacja po treści)
|
||||
3. Co 5 wiadomości w konwersacji → aktualizuje `ai_conversation_summary`
|
||||
4. 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
|
||||
|
||||
1. `091_create_ai_user_memory.sql` — tabela + indeksy
|
||||
2. `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
|
||||
|
||||
1. **Tożsamość użytkownika** — najprostsza, natychmiastowy efekt "wow"
|
||||
2. **Smart Router + Context Builder** — redukcja kosztów i poprawa prędkości
|
||||
3. **Streaming** — perceived latency drop
|
||||
4. **Pamięć użytkownika** — wymaga nowych tabel, async pipeline, UI
|
||||
Loading…
Reference in New Issue
Block a user