Większość architektur RAG (Retrieval-Augmented Generation) prezentowanych w tutorialach internetowych opiera się na naiwnym założeniu: wrzucamy wszystkie dokumenty firmy do chmurowej bazy wektorowej, robimy wyszukiwanie po podobieństwie i wstrzykujemy fragmenty do promptu modelu LLM.
W środowisku korporacyjnym takie podejście kończy się natychmiastowym zablokowaniem projektu przez dział bezpieczeństwa (Security & Compliance). Powód jest prosty: brak dziedziczenia uprawnień dostępu (ACL / RBAC). Jeśli stażysta zapyta wewnętrznego asystenta AI: “Ile zarząd wydał na premie w zeszłym kwartale?”, naiwny system RAG bez wahania wyciągnie fragment poufnego pliku PDF z folderu zarządu i przekaże go modelowi.
W tym artykule pokazujemy, jak zaprojektować architekturę RAG spełniającą wymogi enterprise przy użyciu PostgreSQL i rozszerzenia pgvector.
1. Pułapka dedykowanych baz wektorowych a uprawnienia
W ostatnich latach rynek zalała fala wyspecjalizowanych baz wektorowych (Pinecone, Qdrant, Chroma, Milvus). Choć oferują one doskonałą wydajność wyszukiwania ANN (Approximate Nearest Neighbor), w architekturze korporacyjnej wprowadzają poważny problem integracyjny:
[ Aplikacja Backend ] ──(Aktualizacja uprawnień)──> [ PostgreSQL / ERP ]
│
│ (Brak transakcyjności, opóźnienia CDC)
▼
[ Zewnętrzna Baza Wektorowa ] <── Zdezaktualizowany filtr ACL!
Gdy uprawnienia użytkownika do pliku zmieniają się w systemie źródłowym (np. odebranie dostępu do projektu w SharePoincie lub CRM):
- Musisz zsynchronizować zmianę metadanych w zewnętrznej bazie wektorowej przez asynchroniczny potok ETL/CDC.
- W oknie niespójności (eventual consistency) baza wektorowa nadal zwraca fragmenty dokumentów osobom nieuprawnionym.
- Filtrowanie wektorowe w zewnętrznych bazach często odbywa się w modelu Post-Filtering (najpierw szukamy 50 wektorów, potem odrzucamy te bez uprawnień). Jeśli 48 z nich to tajne pliki zarządu, użytkownik otrzyma zaledwie 2 wyniki, mimo że w bazie istniały dziesiątki dostępnych dla niego dokumentów.
2. Rozwiązanie: Spójny RAG w PostgreSQL z pgvector i RLS
Wykorzystanie PostgreSQL jako wspólnego magazynu dla relacyjnych danych biznesowych, tabel uprawnień oraz wektorów eliminuje ten problem u źródła.
Dzięki połączeniu pgvector z mechanizmem Row-Level Security (RLS), silnik bazy danych filtruje wektory w ramach tej samej transakcji ACID, gwarantując, że zapytanie semantyczne fizycznie nie widzi rekordów, do których bieżący użytkownik nie ma praw.
Schemat bazy danych z kontrolą uprawnień:
CREATE EXTENSION IF NOT EXISTS vector;
-- Tabela fragmentów dokumentów (Chunks)
CREATE TABLE document_chunks (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
document_id UUID NOT NULL,
content TEXT NOT NULL,
embedding vector(1536), -- Format dla OpenAI text-embedding-3-small
required_role VARCHAR(50) NOT NULL DEFAULT 'employee',
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Indeks HNSW dla błyskawicznego wyszukiwania wektorowego
CREATE INDEX idx_document_chunks_embedding_hnsw
ON document_chunks
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Włączenie Row-Level Security
ALTER TABLE document_chunks ENABLE ROW LEVEL SECURITY;
-- Polityka: Użytkownik widzi tylko swój tenant i rekordy zgodne z jego rolą
CREATE POLICY rls_document_chunks_access ON document_chunks
FOR SELECT
USING (
tenant_id = current_setting('app.current_tenant_id')::UUID
AND (
required_role = 'employee'
OR current_setting('app.current_user_role') = 'management'
)
);
Bezpieczne zapytanie RAG w jednej operacji:
Gdy aplikacja odpytuje bazę, przekazuje tożsamość użytkownika w zmiennych sesyjnych:
BEGIN;
SET LOCAL app.current_tenant_id = 'a1b2c3d4-0000-0000-0000-000000000001';
SET LOCAL app.current_user_role = 'employee';
-- Wyszukiwanie wektorowe z gwarancją bezpieczeństwa RLS
SELECT
id,
content,
1 - (embedding <=> '[0.012, -0.045, 0.089, ...]'::vector) AS similarity
FROM document_chunks
ORDER BY embedding <=> '[0.012, -0.045, 0.089, ...]'::vector
LIMIT 5;
COMMIT;
Nawet jeśli najbardziej podobny semantycznie fragment dotyczy pensji zarządu, silnik PostgreSQL wyeliminuje go na poziomie planera zapytań przed kalkulacją odległości wektorowej. Nie ma ryzyka wycieku danych do promptu LLM.
3. Strategia chunkowania: Wzorzec Parent-Document Retrieval
Drugą przyczyną porażek systemów RAG jest zła strategia dzielenia tekstu (chunking). Standardowe dzielenie pliku na sztywne bloczki po 500 znaków z nakładaniem (overlap) powoduje utratę kontekstu:
- Model widzi zdanie “Koszty wzrosły o 12%”, ale tabela z informacją, jakiego działu to dotyczy, została ucięta w poprzednim chunku.
Najbardziej efektywnym podejściem jest Parent-Document Retrieval (Hierarchical Chunking):
[ Oryginalny Dokument / Raport PDF ]
│
▼
[ Parent Chunk (np. 2000 słów / cały rozdział) ]
│
┌──────────┴──────────┐
▼ ▼
[ Child Chunk 1 ] [ Child Chunk 2 ] (np. 200 słów)
(Embedding A) (Embedding B)
- Wyszukiwanie: Embedding generujemy dla małych, precyzyjnych fragmentów potomnych (Child Chunks – 150–300 słów). Dzięki temu podobieństwo wektorowe jest ostre i nie rozmywa się w długim tekście.
- Generowanie: Do promptu modelu LLM przekazujemy powiązany, nadrzędny dokument (Parent Chunk). Model zyskuje pełny kontekst otaczających akapitów i tabel, eliminując konieczność zgadywania.
4. Tuning indeksów HNSW w pgvector dla skali produkcyjnej
Dla baz zawierających setki tysięcy lub miliony wektorów kluczowe znaczenie ma kalibracja parametrów indeksu HNSW (Hierarchical Navigable Small World):
m(domyślnie 16): Maksymalna liczba dwukierunkowych połączeń na węzeł grafu. Zwiększenie do24–32podnosi dokładność wyszukiwania kosztem większego zużycia pamięci RAM.ef_construction(domyślnie 64): Wielkość listy dynamicznej podczas budowania indeksu. Podniesienie do128wydłuża czas indeksowania, ale poprawia recall (odsetek trafnych wyników).hnsw.ef_search(parametr sesyjny, domyślnie 40): Określa, jak głęboko graf jest przeszukiwany w trakcie pojedynczego zapytania SELECT. Można go dynamicznie podnosić dla krytycznych zapytań:
SET hnsw.ef_search = 100;
Dla baz o ograniczonej pamięci RAM warto rozważyć typ danych halfvec (16-bitowe liczby zmiennoprzecinkowe zamiast standardowych 32-bitowych), co natychmiastowo redukuje rozmiar indeksu w pamięci o 50% przy minimalnej utracie precyzji wyszukiwania.
Podsumowanie
Wdrożenie technologii RAG w przedsiębiorstwie to w 20% inżynieria promptów i w 80% tradycyjna architektura danych: bezpieczeństwo, transakcyjność i kontrola dostępu. Zamiast budować kruche mosty integracyjne z osobnymi bazami wektorowymi, wykorzystanie PostgreSQL z pgvector i mechanizmem Row-Level Security daje jednolity, audytowalny i skalowalny fundament pod systemy sztucznej inteligencji.