RAG na tabelach i SQL: kiedy tekstowe chunking przegrywa z Text-to-SQL i hybrydą
Kiedy pytasz o dane w tabelach, klasyczny RAG z chunkingiem często zawodzi. Porównujemy RAG, Text-to-SQL i hybrydę, plus architekturę, walidację SQL, bezpieczeństwo i testy.
1. Wprowadzenie: dlaczego pytania o dane tabelaryczne to osobna kategoria problemu
Pytania o dane tabelaryczne różnią się od klasycznych pytań „o treść dokumentów”, bo dotyczą struktury, relacji i obliczeń, a nie tylko odnajdywania fragmentu tekstu. W przypadku opisów, polityk czy instrukcji często wystarczy znaleźć właściwy akapit i streścić go w odpowiednim kontekście. Przy tabelach użytkownik zwykle oczekuje wyniku, który jest policzony, przefiltrowany, pogrupowany albo zestawiony według reguł — a to wymaga precyzyjnej interpretacji znaczenia kolumn, kluczy i zależności.
Największa różnica polega na tym, że w danych strukturalnych „prawda” nie jest pojedynczym zdaniem, tylko wynikiem operacji na rekordach. Odpowiedź może zależeć od definicji miar (np. „przychód” vs „zysk”), okna czasowego, sposobu agregacji, obsługi braków danych czy wykluczeń. To sprawia, że samo podobieństwo semantyczne do „kawałka tekstu” bywa złudne: model może znaleźć fragment opisujący metrykę, ale bez faktycznego obliczenia nadal nie odpowie na pytanie, a próba „dopowiedzenia” liczb na podstawie kontekstu prowadzi do błędów.
Dane tabelaryczne wprowadzają też inne oczekiwania co do dokładności i weryfikowalności. Użytkownik z reguły chce odpowiedzi deterministycznej, spójnej z aktualnym stanem bazy oraz takiej, którą da się odtworzyć. W świecie dokumentów akceptowalne bywa streszczenie z pewnym marginesem interpretacji; w świecie tabel nawet drobna pomyłka w filtrze, łączeniu danych czy jednostkach może całkowicie zmienić wynik.
Do tego dochodzi problem zmienności. Dokumenty często aktualizują się rzadziej i mogą być skutecznie indeksowane jako tekst. Tabele i hurtownie danych zmieniają się ciągle: dochodzą nowe wiersze, zmieniają się statusy, korekty historyczne. Pytanie „ile było zamówień wczoraj?” wymaga pracy na bieżących danych, a nie na fragmencie tekstu z wczorajszego indeksu.
W praktyce pytania o tabele można rozpoznać po tym, że:
- odwołują się do liczb, sum, średnich, rankingów albo porównań w czasie,
- wymagają precyzyjnych filtrów (region, segment, status, okres),
- zakładają relacje między bytami (np. klient → zamówienia → pozycje),
- oczekują odpowiedzi zgodnej z danymi źródłowymi, a nie „najbardziej prawdopodobnej” narracji.
To właśnie dlatego metody oparte wyłącznie na tekstowym wyszukiwaniu i chunkingu często przegrywają w scenariuszach analitycznych: nie wystarczy znaleźć opis — trzeba wykonać poprawną operację na strukturze. Jednocześnie nie każde pytanie o dane wymaga bezpośredniego zapytania do bazy: część dotyczy definicji metryk, interpretacji pól czy zasad raportowania, które są zapisane w dokumentacji. W rezultacie zadania na tabelach tworzą osobną klasę problemów, w której kluczowe staje się dobranie podejścia odpowiedniego do rodzaju pytania, wymagań dokładności i charakteru danych.
Trzy podejścia: klasyczny RAG (chunking), Text-to-SQL, podejście hybrydowe — porównanie działania
Pytania o dane tabelaryczne można obsłużyć trzema głównymi strategiami, które różnią się tym, co model dostaje na wejściu (tekst czy wynik zapytania), jak powstaje odpowiedź (streszczenie kontekstu czy wyliczenie na danych) oraz gdzie leży „źródło prawdy” (fragmenty dokumentacji vs baza danych). Ten artykuł powstał jako rozwinięcie jednego z najczęstszych tematów poruszanych podczas szkoleń Cognity.
1) Klasyczny RAG (chunking): „najpierw wyszukaj tekst, potem odpowiedz”
W klasycznym RAG informacje o tabelach i danych są traktowane jak zwykły tekst: dokumentacja, opisy schematu, eksporty CSV, raporty, zrzuty ekranów czy komentarze analityków są dzielone na fragmenty (chunki), indeksowane i wyszukiwane semantycznie. Model dostaje wybrane fragmenty jako kontekst i generuje odpowiedź.
- Jak działa w praktyce: zapytanie użytkownika → wyszukiwanie fragmentów tekstu → odpowiedź na podstawie znalezionych opisów.
- Mocne strony: świetne do pytań definicyjnych i wyjaśniających, np. „co oznacza kolumna”, „jak liczony jest wskaźnik”, „jakie są zasady filtrowania”.
- Słabe strony: słabo radzi sobie z pytaniami wymagającymi precyzyjnych obliczeń, agregacji, filtrów i aktualnych wartości; łatwo o odpowiedź opartą na nieaktualnym opisie lub przybliżeniu, a nie na realnych danych.
2) Text-to-SQL: „zamień pytanie na zapytanie do bazy”
Text-to-SQL traktuje bazę danych jako docelowe źródło odpowiedzi. Model mapuje pytanie użytkownika na intencję analityczną i generuje zapytanie SQL, które jest wykonywane na żywych danych. Dopiero na podstawie wyników model formułuje odpowiedź w języku naturalnym.
- Jak działa w praktyce: zapytanie użytkownika → generacja SQL → wykonanie w bazie → interpretacja wyników → odpowiedź.
- Mocne strony: wysoka precyzja dla pytań liczbowych i przekrojów danych, np. „ile było zamówień w zeszłym tygodniu”, „top 10 produktów”, „średni czas realizacji per region”. Daje też naturalnie aktualne wyniki, jeśli baza jest aktualna.
- Słabe strony: wymaga dobrego zrozumienia schematu (tabele, klucze, relacje, nazewnictwo), a pytania niejednoznaczne mogą prowadzić do błędnego zapytania. Bez dodatkowych zabezpieczeń rośnie ryzyko kosztownych lub niepożądanych zapytań.
3) Podejście hybrydowe: „RAG do kontekstu i reguł, SQL do faktów”
Hybryda łączy oba światy: RAG dostarcza kontekst potrzebny do poprawnego zadania pytania bazie (np. definicje metryk, mapowanie terminów biznesowych na kolumny, reguły filtracji, opis relacji), a Text-to-SQL dostarcza twarde wyniki z danych. Model nie musi „zgadywać”, jak liczy się metrykę lub które tabele są właściwe — może to najpierw odczytać z dokumentacji, a potem policzyć na danych.
- Jak działa w praktyce: zapytanie użytkownika → wyszukanie kontekstu (definicje, słownik pojęć, opis schematu) → generacja SQL zgodna z tym kontekstem → wykonanie → odpowiedź z odwołaniem do użytych definicji i danych.
- Mocne strony: najlepsza, gdy organizacja ma złożone definicje KPI, wiele źródeł danych, niestandardowe reguły liczenia i częste nieporozumienia terminologiczne. Poprawia trafność SQL i zmniejsza ryzyko rozminięcia się z definicją biznesową.
- Słabe strony: większa złożoność przepływu i utrzymania (więcej komponentów, więcej miejsc, gdzie może dojść do rozjazdu między dokumentacją a stanem bazy). Wymaga spójnego zarządzania wiedzą i metadanymi.
Najważniejsze różnice w zastosowaniach
- RAG (chunking) dominuje, gdy pytanie dotyczy znaczenia, zasad i interpretacji, a nie wyliczeń.
- Text-to-SQL wygrywa, gdy pytanie jest w istocie prośbą o wynik analityczny z danych tabelarycznych.
- Hybryda sprawdza się, gdy do poprawnego policzenia wyniku trzeba najpierw ustalić definicje, mapowania i reguły, a dopiero potem pobrać dane z bazy.
3. Kryteria wyboru podejścia: typ pytań, aktualność danych, koszty i opóźnienia, ryzyko halucynacji i błędów
Wybór między klasycznym RAG na chunkach tekstu, Text-to-SQL oraz podejściem hybrydowym zależy głównie od tego, jakiego rodzaju pytania zadają użytkownicy, jak świeże muszą być dane, jakie są akceptowalne koszty i opóźnienia oraz jak bardzo trzeba minimalizować ryzyko halucynacji i błędnych wniosków. Poniżej zestaw kryteriów, które najczęściej przesądzają o decyzji.
Typ pytań: opisowe vs. obliczeniowe i wielotabelowe
- Pytania o definicje, interpretacje i kontekst (np. „co oznacza kolumna X?”, „jak liczony jest wskaźnik Y?”): zwykle wygrywa klasyczny RAG, bo źródłem prawdy jest dokumentacja (wiki, specyfikacje, opisy tabel), a nie same rekordy.
- Pytania o konkretne wartości, agregacje i rankingi (np. „suma sprzedaży w Q4”, „top 10 klientów wg przychodu”): naturalnym wyborem jest Text-to-SQL, bo odpowiedź wynika z obliczeń na danych, a nie z tekstu.
- Pytania wymagające łączenia wielu tabel, filtrów i warunków (joiny, grupowania, okna czasowe): zwykle Text-to-SQL lub hybryda. Chunking tekstowy ma tendencję do gubienia relacji (klucze, kardynalność, filtry), co skutkuje błędną odpowiedzią „z pamięci”.
- Pytania mieszane (np. „policz X, ale wyjaśnij, jak to jest liczone i jakie są ograniczenia”): najczęściej najlepsza jest hybryda, gdzie SQL dostarcza liczby, a RAG dostarcza definicje, reguły i caveaty.
Aktualność danych: „tu i teraz” vs. stan opisany w dokumentacji
- Wysoka zmienność danych (dashboardy operacyjne, stany magazynowe, bieżące transakcje): preferuj Text-to-SQL (lub hybrydę), bo odpowiedź musi pochodzić z aktualnego źródła danych.
- Wiedza stabilna (definicje KPI, słowniki, procesy): RAG jest wystarczający, a często najtańszy i najprostszy.
- Ryzyko rozjazdu „opis vs. rzeczywistość”: gdy dokumentacja bywa nieaktualna, hybryda pozwala zestawić to, co opisane z tym, co wynika z danych (np. odchylenia, brakujące wartości).
Koszty i opóźnienia: gdzie płacisz i na czym tracisz czas
- RAG (chunking): koszt przenosi się na embeddingi, wyszukiwanie i generację odpowiedzi. Często jest szybki w inferencji, ale może wymagać większej liczby kontekstów (tokenów), gdy odpowiedź zależy od wielu fragmentów.
- Text-to-SQL: koszt i opóźnienie wynikają z dwóch elementów: generacji SQL (LLM) oraz wykonania zapytania (baza/warehouse). Dla ciężkich agregacji to właśnie czas bazy dominuje.
- Hybryda: bywa najdroższa, bo uruchamia dwa tory (retrieval + SQL). Opłaca się, gdy redukuje liczbę dopytań i błędów, a odpowiedź musi być jednocześnie policzona i uzasadniona.
- Wąskie gardła: jeśli użytkownicy zadają wiele podobnych pytań, Text-to-SQL z dobrze ustawionym cache wyników bywa bardziej ekonomiczny niż wielokrotne generowanie długich odpowiedzi na podstawie chunków.
Ryzyko halucynacji i błędów: co może pójść nie tak
- RAG (chunking): nawet z cytatami może „dopowiedzieć” liczby lub zależności, jeśli nie ma w kontekście kompletnego fragmentu. Typowe ryzyko to: uogólnienia, pomylenie wersji definicji, brak spójności między chunkami.
- Text-to-SQL: ogranicza halucynacje liczb (bo wynik pochodzi z bazy), ale wprowadza inne klasy błędów: zły join (duplikacje), zła miara (np. COUNT vs. COUNT DISTINCT), błędny filtr czasu, nieuwzględnione warunki biznesowe.
- Hybryda: minimalizuje ryzyko „zmyślonych” wartości, ale wymaga spójności między warstwą obliczeń a warstwą wyjaśnień. Najczęstsza pułapka to sytuacja, w której opis definicji (z RAG) nie pasuje do tego, co faktycznie policzyło SQL.
Tabela decyzyjna: szybkie dopasowanie podejścia
| Kryterium | RAG (chunking) | Text-to-SQL | Hybryda |
|---|---|---|---|
| Definicje, procedury, interpretacje | Najlepszy wybór | Rzadko | Gdy trzeba dopiąć liczby do definicji |
| Agregacje, rankingi, filtry | Słabo (łatwo o konfabulacje) | Najlepszy wybór | Gdy potrzebne dodatkowe wyjaśnienie |
| Wiele tabel i relacji | Ryzykowne | Dobre (jeśli model zna schemat) | Bardzo dobre przy złożonych regułach |
| Wymagana świeżość danych | Ograniczona do aktualności dokumentów | Wysoka (dane „live”) | Wysoka + kontekst |
| Koszt/latencja | Zwykle niskie do średnich | Zależne od ciężaru zapytań | Najczęściej najwyższe |
| Główne ryzyko | Halucynacje i niespójne wnioski | Błędy logiczne w SQL (join/filtry/metryki) | Niespójność definicji i obliczeń |
Praktyczna heurystyka wyboru
- Jeśli odpowiedź da się wiarygodnie zacytować z dokumentu: wybierz RAG.
- Jeśli odpowiedź musi być policzona na aktualnych danych: wybierz Text-to-SQL.
- Jeśli trzeba jednocześnie policzyć i uzasadnić znaczenie/ograniczenia: wybierz hybrydę.
4. Architektura referencyjna end-to-end: pytanie → plan → SQL → wykonanie → odpowiedź z cytowaniem źródeł
W przypadku pytań o dane tabelaryczne najstabilniej działa architektura, która traktuje bazę danych jako źródło prawdy, a model językowy jako warstwę planowania, generowania zapytań i narracji. Kluczowe jest rozdzielenie etapów: najpierw intencja i plan, potem deterministyczne pobranie danych (SQL), a dopiero na końcu sformułowanie odpowiedzi wraz z cytowaniem tego, co faktycznie zostało odczytane. Zespół trenerski Cognity zauważa, że właśnie ten aspekt (przejrzyste rozdzielenie planu, SQL i cytowania) sprawia uczestnikom najwięcej trudności.
Przepływ danych i odpowiedzialności (pipeline)
- Pytanie użytkownika → wejście w języku naturalnym (często z niejednoznacznościami, skrótami i kontekstem domenowym).
- Plan → model formułuje, jakie dane są potrzebne, jakie filtry/agregacje zastosować oraz jaką postać ma mieć wynik.
- SQL → plan jest tłumaczony na zapytanie (lub zestaw zapytań) do konkretnego dialektu i schematu.
- Wykonanie → silnik bazy zwraca wynik; opcjonalnie wykonywane są dodatkowe zapytania pomocnicze (np. do wyjaśnień, rozkładów, kontroli zakresu).
- Odpowiedź → model generuje podsumowanie w języku naturalnym na podstawie wyników, z cytowaniem źródeł (np. tabela/kolumny, widok, timestamp, ewentualnie identyfikator zapytania).
Komponenty architektury referencyjnej
- Warstwa API (Chat/Query Service): przyjmuje pytania, zarządza sesją i kontekstem (np. poprzednie pytania), narzuca limity oraz format odpowiedzi.
- Orkiestrator: steruje kolejnymi krokami (plan → SQL → wykonanie → odpowiedź), decyduje czy potrzebna jest doprecyzowująca interakcja.
- Katalog schematu (Schema Registry): udostępnia modelowi opis tabel, relacji, miar, słowników wartości; to „kontrakt” pomiędzy LLM a bazą.
- Generator SQL: zamienia plan na SQL zgodny z dialektem i konwencjami (np. nazwy pól, wymagane filtry bezpieczeństwa).
- Wykonawca zapytań (DB Connector): uruchamia SQL, stosuje limity (czas, liczba wierszy), zwraca wyniki w ustandaryzowanej postaci.
- Warstwa cytowania (Provenance): buduje „dowody” do odpowiedzi: skąd pochodzą liczby (tabela/kolumny), jaki był zakres czasu, jaki widok, jaki identyfikator zapytania.
- Generator odpowiedzi: tworzy odpowiedź biznesową, zachowując rozdział między wnioskami a faktami z wyników.
Plan jako kontrakt: co powinno się w nim znaleźć
Plan jest pośrednią reprezentacją, która pozwala utrzymać kontrolę nad tym, co ma zostać policzone, zanim pojawi się konkretne SQL. Powinien być krótki, ale jednoznaczny, np.:
- encje i miary (co liczymy: suma, średnia, liczba unikalnych),
- wymiary (po czym grupujemy),
- filtry (zakres dat, statusy, segment),
- sortowanie i limit (top N, kolejność),
- format odpowiedzi (tabela, jedno zdanie, lista punktów),
- założenia (np. definicja „aktywny klient” lub wybór źródła, jeśli jest kilka).
Cytowanie źródeł dla danych tabelarycznych: co oznacza „source”
W RAG na dokumentach „źródłem” jest zwykle fragment tekstu. Dla SQL „źródłem” jest pochodzenie danych i ścieżka obliczeń. W praktyce cytowanie może przyjąć formę:
- tabela/widok i użyte kolumny (np. sales.orders.amount),
- zakres czasu i kluczowe filtry (np. WHERE order_date BETWEEN …),
- identyfikator zapytania lub skrót SQL (dla audytu i powtarzalności),
- timestamp odczytu (kiedy wynik został policzony).
To pozwala odróżnić odpowiedź „z danych” od odpowiedzi „z pamięci modelu” i ułatwia weryfikację.
Warianty architektury: Text-to-SQL, hybryda, a gdzie wchodzi RAG
W referencyjnym pipeline dominują dwa warianty, a klasyczny chunking dokumentów pojawia się jako wsparcie, nie jako główny kanał prawdy:
- Text-to-SQL (rdzeń): model generuje SQL na podstawie schematu i pytania; odpowiedź jest budowana z wyników zapytania.
- Hybryda: SQL daje liczby/fakty, a RAG z dokumentów/definicji dostarcza kontekst (np. definicje KPI, opisy pól, zasady biznesowe) do etapu planu i narracji.
- Chunking „czysto tekstowy”: sensowny głównie wtedy, gdy pytanie dotyczy opisów, procedur, definicji, a nie wyliczeń na tabelach.
Minimalny szkic implementacji (schemat kroków)
// 1) Interpretacja i plan
plan = LLM.makePlan(question, schema_context, business_definitions)
// 2) Generowanie SQL
sql = LLM.toSQL(plan, schema_context)
// 3) Wykonanie
result = db.execute(sql, limits)
// 4) Budowa dowodów/cytowań
sources = provenance.from(sql, result.metadata)
// 5) Odpowiedź
answer = LLM.writeAnswer(question, result.rows, sources, format=plan.output_format)
Co ta architektura daje w praktyce
- Rozdział odpowiedzialności: model nie „wymyśla” liczb — liczby pochodzą z zapytań.
- Powtarzalność: ten sam plan/SQL można uruchomić ponownie i porównać wyniki.
- Lepsza obserwowalność: łatwiej logować decyzje (plan), zapytania (SQL) i źródła (provenance).
- Skalowalność funkcjonalna: dołożenie nowej tabeli lub KPI to rozszerzenie katalogu schematu i definicji, a nie „dosypywanie chunków”.
5. Walidacja i niezawodność SQL: generowanie planu, guardrails, sprawdzanie schematu, sanity checks i ograniczenia zapytań
Gdy odpowiedź ma pochodzić z bazy danych, jakość systemu zależy nie tylko od tego, czy model „umie napisać SQL”, ale czy potrafi wygenerować zapytanie bezpieczne, poprawne względem schematu i stabilne kosztowo. W praktyce walidacja SQL to zestaw prostych, ale konsekwentnie stosowanych mechanizmów, które ograniczają błędy i ryzyko halucynacji do poziomu akceptowalnego w produkcji.
Generowanie planu przed SQL
Najprostszy sposób na redukcję błędów to rozdzielenie procesu na dwa kroki: plan (co trzeba policzyć i z jakich tabel) oraz SQL (jak to wykonać). Plan jest krótką specyfikacją, którą łatwiej zweryfikować automatycznie niż pełne zapytanie.
- Plan minimalny: wymagane metryki, filtry, zakres czasu, poziom agregacji, sortowanie, limit.
- Kontrakty: plan musi wskazać tabele/kolumny z dozwolonej listy oraz określić, czy wynik ma być pojedynczą liczbą, tabelą czy szeregiem czasowym.
- Wczesne wykrywanie niejednoznaczności: jeśli plan wymaga brakującej definicji (np. „aktywny klient”), system powinien wymusić doprecyzowanie zamiast zgadywać.
Guardrails: co wolno generować, a czego nie
Guardrails to zestaw ograniczeń nakładanych na generator SQL, aby zawęzić przestrzeń możliwych zapytań. Zastosowanie jest proste: mniej swobody oznacza mniej zaskakujących błędów.
- Tryb tylko-odczyt: blokada zapytań modyfikujących (INSERT/UPDATE/DELETE, DDL).
- Ograniczenie do widoków/warstwy semantycznej: model nie widzi „surowych” tabel, tylko stabilne obiekty raportowe.
- Białe listy funkcji: dopuszczenie wybranych agregacji i funkcji dat (reszta odrzucana).
- Wymuszenie LIMIT: zapobiega przypadkowym pełnym zrzutom danych.
- Zakaz SELECT *: wymusza wybór konkretnych kolumn, co zmniejsza ryzyko wycieku i koszty.
Sprawdzanie schematu: zgodność tabel, kolumn i typów
Model może „wymyślić” kolumnę lub pomylić typy. Dlatego przed wykonaniem zapytania warto przeprowadzić walidację statyczną:
- Istnienie obiektów: czy wszystkie tabele/kolumny w SQL istnieją w aktualnym schemacie.
- Zgodność typów: np. porównania dat z datami, liczb z liczbami; ostrzeżenia dla podejrzanych rzutowań.
- Relacje i klucze: wykrywanie joinów „na oko” (np. łączenie po nazwie zamiast ID) i promowanie joinów po zdefiniowanych kluczach.
W praktyce jest to kontrola na poziomie parsera/AST oraz metadanych bazy. Celem nie jest udowodnienie poprawności, tylko odcięcie typowych pomyłek.
Sanity checks: szybkie testy sensowności wyniku
Nawet poprawny syntaktycznie SQL może dać bezsensowny wynik (np. przez zły join, błędny filtr czasu, podwójne zliczenie). Sanity checks to proste reguły uruchamiane po wykonaniu zapytania lub na podstawie planu.
- Kontrole kardynalności: wykrywanie eksplozji joinów (nagły wzrost liczby wierszy).
- Zakresy wartości: wartości ujemne tam, gdzie nie powinny wystąpić; procenty > 100%.
- Spójność agregacji: czy wynik pasuje do oczekiwanego poziomu (np. jedna liczba vs wiele wierszy).
- Kontrola czasu: czy zakres dat nie jest „od zawsze”, jeśli pytanie sugeruje okres (np. „w tym kwartale”).
Jeśli sanity check zawiedzie, preferowaną reakcją jest: korekta zapytania (np. przez doprecyzowanie joinów) albo prośba o doprecyzowanie, zamiast dostarczenia odpowiedzi „na siłę”.
Ograniczenia zapytań: koszty, opóźnienia i stabilność
Produkcja wymaga kontroli kosztów i czasu. Ograniczenia zapytań to polityki wykonania, które chronią system przed drogimi lub zbyt wolnymi analizami.
- Limity czasu i zasobów: timeout, limity skanowanych danych (jeśli silnik na to pozwala), limity pamięci.
- Wymuszenie filtrów: dla dużych tabel wymagany warunek po dacie/partycji.
- Ograniczenia złożoności: maksymalna liczba joinów, CTE, podzapytań; blokada zapytań „wielopoziomowych”, jeśli nie są potrzebne.
- Cache i powtarzalność: preferowanie zapytań, które można buforować (stabilne parametry, deterministyczne wyniki).
Minimalny pipeline walidacyjny (przykładowy szkic)
1) Utwórz plan (metryka, filtry, tabele, agregacja, limit)
2) Zweryfikuj plan względem polityk (dozwolone źródła, wymagane filtry)
3) Wygeneruj SQL
4) Parsuj SQL do AST i sprawdź: tylko-odczyt, brak SELECT *, białe listy funkcji
5) Sprawdź zgodność ze schematem (tabele/kolumny/typy, joiny po kluczach)
6) Uruchom z limitami wykonania (timeout, limit danych)
7) Sanity checks na wyniku; w razie problemu: popraw/ponów albo poproś o doprecyzowanie
Co daje największy efekt przy najmniejszym koszcie
| Mechanizm | Redukuje | Najczęstsze zastosowanie |
|---|---|---|
| Plan → SQL | złe założenia i niejednoznaczności | pytania biznesowe wymagające definicji/filtrów |
| Guardrails (read-only, whitelist, LIMIT) | ryzyko i koszt wykonania | produkcyjne endpointy LLM, self-service analytics |
| Schema check (AST + metadane) | halucynacje kolumn/tabel | często zmieniające się schematy, wiele źródeł danych |
| Sanity checks | „poprawny SQL, zły wynik” | joiny, agregacje, raporty KPI |
| Limity wykonania | timeouts i nieprzewidywalne koszty | duże tabele faktów, partycje czasowe |
Zebrane razem, te elementy tworzą praktyczną „siatkę bezpieczeństwa” dla Text-to-SQL i podejść hybrydowych: model może się mylić, ale system nie powinien pozwolić, by błąd zamienił się w kosztowną awarię lub błędną odpowiedź bez sygnału ostrzegawczego.
6. Bezpieczeństwo danych: uprawnienia, row-level security, maskowanie pól, audyt i ochrona przed prompt injection
Bezpieczeństwo w systemach typu RAG na tabelach i Text-to-SQL różni się od pracy na zwykłych dokumentach: tu LLM może pośrednio uruchamiać dostęp do danych produkcyjnych, a wynik zapytania bywa na tyle „konkretny”, że pojedyncza pomyłka w uprawnieniach lub filtrach może ujawnić dane wrażliwe. Dlatego kontrola dostępu i redukcja ekspozycji powinny działać na poziomie bazy i warstwy wykonawczej, a nie tylko w promptach.
Uprawnienia: najmniejsze możliwe, rozdzielone na warstwy
Podstawowa zasada to least privilege: model (a dokładniej konto techniczne używane do wykonywania SQL) powinien widzieć wyłącznie te tabele, widoki i kolumny, które są niezbędne do odpowiedzi na typowe pytania. W praktyce pomaga rozdzielenie ról:
- Rola aplikacyjna (serwis): minimalne uprawnienia do SELECT na przygotowanych widokach, bez dostępu do surowych tabel.
- Rola użytkownika końcowego: mapowana na kontekst zapytania (np. dział/region), aby wynik był zgodny z uprawnieniami biznesowymi.
- Rola administracyjna: poza ścieżką LLM (nigdy nieużywana przez model).
W wielu wdrożeniach bezpieczniejsze jest udostępnianie modelowi warstwy semantycznej (widoki, materializacje, tabele „gold”), zamiast pełnego schematu analitycznego.
Row-Level Security (RLS): polityki w bazie, nie w promptach
RLS ogranicza wiersze widoczne dla danego użytkownika/roli zgodnie z polityką (np. tylko dane własnego regionu). To kluczowe, bo model może wygenerować zapytanie, które „przypadkiem” pomija filtr — RLS nadal zadziała. Typowe zastosowania:
- Izolacja najemców (multi-tenant): każdy klient widzi tylko swoje rekordy.
- Segmentacja organizacyjna: oddziały, zespoły, portfele, projekty.
- Dane osobowe: dostęp do rekordów ograniczony do uprawnionych ról.
W praktyce warto traktować RLS jako twardą barierę, a wszelkie filtry generowane przez LLM jako wygodę, nie jako mechanizm bezpieczeństwa.
Maskowanie pól i ograniczanie kolumn: minimalizacja ekspozycji
Nawet jeśli użytkownik ma prawo widzieć część rekordów, nie zawsze powinien widzieć wszystkie kolumny. Stosuje się więc:
- Column-level security: jawne odcięcie kolumn (np. PESEL, e-mail, numer karty) od roli używanej przez asystenta.
- Dynamic data masking: częściowe ukrywanie (np. „a***@domena.pl”), zależnie od roli.
- Widoki publikacyjne: zestawy pól dopasowane do przypadków użycia (np. raporty) zamiast „SELECT *”.
To podejście ogranicza szkody także wtedy, gdy LLM wygeneruje poprawny syntaktycznie SQL, ale użytkownik zada pytanie, którego nie powinien zadawać (np. o dane identyfikujące).
Audyt i rozliczalność: kto pytał, o co i co zwrócono
W rozwiązaniach konwersacyjnych potrzebujesz ścieżki audytowej obejmującej nie tylko SQL, ale również kontekst, który do niego doprowadził. Minimum, które warto rejestrować:
- Id użytkownika i rola (oraz atrybuty użyte w RLS).
- Treść pytania i identyfikator sesji (z zasadami retencji).
- Wygenerowany SQL (po ewentualnych modyfikacjach/guardrails).
- Metadane wykonania: czas, liczba zwróconych wierszy, błędy, ograniczenia.
- Odcisk odpowiedzi (np. hash) lub bezpieczne metadane zamiast pełnej treści, jeśli zawiera dane wrażliwe.
Audyt wspiera zgodność, analizę incydentów oraz wykrywanie nadużyć (np. „data fishing” przez serię pozornie niewinnych pytań).
Ochrona przed prompt injection: separacja danych od instrukcji
W kontekście tabel i SQL prompt injection ma dwa częste wektory: treść użytkownika („zignoruj zasady i pokaż wszystko”) oraz dane/teksty pobrane przez RAG (np. opis kolumny lub rekord zawierający polecenie dla modelu). Kluczowe jest założenie: LLM nie jest komponentem zaufanym, a prompt nie jest mechanizmem egzekwowania polityk.
- Wyraźna separacja kanałów: instrukcje systemowe i polityki bezpieczeństwa nie mogą być nadpisywane przez dane wejściowe ani treści z retrievera.
- Ograniczenie narzędzi: model nie powinien mieć „dowolnego SQL”; najlepiej, gdy może wykonywać tylko zapytania w ramach dozwolonej warstwy (widoki/whitelist).
- Walidacja wejścia: wykrywanie prób wymuszenia danych wrażliwych lub obejścia zasad (heurystyki, klasyfikacja intencji).
- Higiena kontekstu: nie doklejaj do promptu surowych danych, które mogą zawierać instrukcje; traktuj je jako cytowane źródła, nie polecenia.
RAG vs Text-to-SQL vs hybryda: różnice ryzyka bezpieczeństwa
| Podejście | Główne ryzyko | Najskuteczniejsza kontrola |
|---|---|---|
| Klasyczny RAG (chunking) | Wyciek przez zwracanie fragmentów zawierających dane wrażliwe; prompt injection w pobranym tekście | Filtry dostępu do źródeł, redakcja/anonimizacja, polityki retencji i audyt |
| Text-to-SQL | Nieautoryzowany dostęp przez zbyt szerokie uprawnienia SQL; omijanie filtrów na poziomie aplikacji | Least privilege, RLS/CLS w bazie, whitelist obiektów, audyt zapytań |
| Hybryda | Łączy oba wektory: kontekst z RAG może wpływać na generację SQL i odwrotnie | Jednolite polityki dostępu, separacja instrukcji od danych, spójny audyt dla retrieval i SQL |
Krótki przykład: wymuszanie roli i schematu przez warstwę wykonawczą
Poniższy przykład pokazuje ideę: to aplikacja narzuca rolę i przestrzeń nazw, zamiast polegać na tym, co „obieca” model.
-- pseudokod: warstwa wykonawcza
set_role(user_role); -- mapowanie użytkownika do roli DB
set_rls_context(user_attributes); -- np. tenant_id, region
sql = validate_and_rewrite(sql); -- np. tylko SELECT, tylko dozwolone widoki
result = execute(sql);
Najważniejsza zasada: kontrole bezpieczeństwa muszą być egzekwowalne niezależnie od treści promptu i jakości generacji. W systemach opartych o SQL oznacza to przeniesienie ciężaru na uprawnienia, RLS/CLS, maskowanie oraz audyt — a dopiero potem na zachowanie modelu.
7. Testy i utrzymanie: testy regresji, zestawy pytań kontrolnych, metryki jakości, monitoring i fallbacki
Systemy odpowiadające na pytania o dane tabelaryczne starzeją się szybciej niż klasyczne chatboty oparte o dokumenty. Zmienia się schemat, definicje metryk, zakres uprawnień, a nawet „sens” danych biznesowych. Dlatego utrzymanie RAG na tabelach, Text-to-SQL i podejścia hybrydowego wymaga cyklicznych testów regresji, stałych metryk jakości oraz mechanizmów awaryjnych, które ograniczają skutki błędnych zapytań lub niepewnych odpowiedzi.
Testy regresji: co psuje się najczęściej
Regresje w systemach tabelarycznych zwykle wynikają nie z modelu, lecz ze środowiska danych. W praktyce warto zakładać, że każda zmiana w hurtowni lub warstwie semantycznej może zmienić odpowiedź, nawet jeśli pytanie pozostaje identyczne.
- Zmiany schematu: nowe kolumny, zmienione typy, usunięte pola, inne nazwy tabel lub widoków.
- Zmiany definicji miar: np. „przychód” liczony inaczej niż wcześniej, inna definicja „aktywnych użytkowników”.
- Dryf danych: sezonowość, korekty historyczne, opóźnienia ładowań, duplikaty.
- Zmiany uprawnień: użytkownik nagle traci dostęp do części danych, co może skutkować inną odpowiedzią lub brakiem wyniku.
- Zmiany promptów i ustawień: nawet drobna modyfikacja instrukcji potrafi zmienić strategię generowania zapytania lub interpretację pytania.
W regresji dla podejścia hybrydowego dochodzi jeszcze ryzyko rozjazdu między „opisem” (tekst) a „prawdą” (SQL): dokumentacja może nie nadążać za danymi, więc testy powinny wykrywać niespójność w obie strony.
Zestawy pytań kontrolnych: jak je budować, żeby były użyteczne
Najlepsze zestawy kontrolne nie są zbiorem przypadkowych pytań, tylko pokrywają typowe tryby analizy oraz wrażliwe obszary logiki danych. Powinny też odzwierciedlać rzeczywiste intencje użytkowników, a nie wyłącznie „ładne” przykłady.
- Pytania faktograficzne: wartości liczbowe, agregacje, rankingi, porównania okresów.
- Pytania z filtrami: segmenty, regiony, kanały, produkty, ograniczenia czasowe, warunki złożone.
- Pytania graniczne: puste wyniki, bardzo małe próbki, brak danych w okresie, nietypowe wartości.
- Pytania niejednoznaczne: takie, które powinny wymusić doprecyzowanie (np. brak wskazania miary lub horyzontu czasu).
- Pytania „bezpiecznikowe”: sprawdzające, czy system odmawia lub ogranicza odpowiedź, gdy ryzyko jest wysokie (np. prośba o dane wrażliwe).
Warto rozdzielać zestawy na: smoke tests (kilkanaście najważniejszych pytań uruchamianych często), zestaw regresyjny (szerszy, uruchamiany cyklicznie) oraz testy incydentowe (dodawane po każdej awarii, aby nie wróciła).
Metryki jakości: co mierzyć w praktyce
W systemach opartych o SQL sama „poprawność językowa” odpowiedzi jest drugorzędna. Kluczowa jest zgodność wyniku z intencją, stabilność oraz to, czy użytkownik może zaufać liczbie i jej pochodzeniu.
- Wskaźniki poprawności: odsetek odpowiedzi zgodnych z oczekiwanym wynikiem lub mieszczących się w dopuszczalnej tolerancji.
- Wskaźniki wykonalności: odsetek zapytań, które wykonują się bez błędu oraz bez przekroczeń limitów.
- Wskaźniki uściślania: jak często system prosi o doprecyzowanie i czy robi to w sytuacjach rzeczywiście niejednoznacznych.
- Wskaźniki halucynacji: przypadki, gdy odpowiedź zawiera liczby lub twierdzenia bez pokrycia w wykonanym zapytaniu albo w źródłach.
- Latency i koszt: czas do odpowiedzi oraz zużycie zasobów w typowych scenariuszach; osobno dla ścieżek RAG/SQL/hybrydy.
- Satysfakcja użytkownika: proste sygnały typu „pomogło/nie pomogło”, uzupełnione o kategorię powodu (brak danych, zła definicja, zły filtr).
Istotne jest też mierzenie stabilności: czy przy tym samym pytaniu i zbliżonym stanie danych system zwraca porównywalne wyniki i uzasadnienie. W środowiskach produkcyjnych to często ważniejsze niż marginalne zyski w „średniej” dokładności.
Monitoring: co obserwować, żeby wykrywać problemy zanim zrobią to użytkownicy
Monitoring powinien obejmować zarówno warstwę modelu, jak i dane oraz wykonanie zapytań. W przeciwieństwie do czysto tekstowych zastosowań, tutaj awaria często objawia się nagłym skokiem błędów zapytań, anomaliami w wynikach lub wzrostem kosztu wykonania.
- Błędy wykonania: wzrost odsetka błędów składniowych, brakujących kolumn/tabel, problemów z uprawnieniami.
- Parametry wydajności: czasy wykonania, liczba skanowanych wierszy, przekroczenia limitów, kolejki w hurtowni.
- Anomalie w wynikach: nagłe skoki lub spadki miar, nietypowe zera, duże rozjazdy względem poprzednich dni dla tych samych pytań.
- Wzorce zapytań: częste pytania, nowe typy intencji, fragmenty, na których system się myli lub prosi o doprecyzowanie.
- Jakość uzasadnień: czy odpowiedź odnosi się do faktycznie użytych źródeł i czy podaje wystarczający kontekst (np. zakres dat, filtry).
Warto też utrzymywać „czarną listę” symptomów, które automatycznie podnoszą priorytet incydentu: gwałtowny wzrost kosztów, powtarzające się zapytania o dane wrażliwe, skoki błędów po wdrożeniu zmian w schemacie.
Fallbacki: jak bezpiecznie degradować działanie
Fallback nie jest oznaką porażki, tylko elementem odpowiedzialnego projektu. W systemach tabelarycznych lepiej udzielić odpowiedzi częściowej lub poprosić o doprecyzowanie niż zwrócić pewnie brzmiącą, ale błędną liczbę.
- Fallback do doprecyzowania: gdy brakuje kluczowych parametrów (miara, okres, jednostka, filtr), system powinien zadać krótkie pytania uzupełniające.
- Fallback do zakresu bezpiecznego: ograniczenie do agregatów, krótszego zakresu czasu lub mniejszej liczby wymiarów, gdy ryzyko kosztu lub błędu jest wysokie.
- Fallback do odpowiedzi opisowej: wyjaśnienie, dlaczego nie można odpowiedzieć liczbowo (np. brak dostępu, brak danych, niejednoznaczność), wraz z podaniem możliwych kroków.
- Fallback między strategiami: przełączenie z Text-to-SQL na hybrydę (lub odwrotnie) w zależności od typu pytania, dostępności danych i historii sukcesów dla podobnych intencji.
- Fallback operacyjny: eskalacja do człowieka lub do procesu zgłoszeniowego, gdy pytanie dotyczy definicji biznesowej lub gdy wynik ma krytyczne konsekwencje.
Utrzymanie jakości w dłuższym horyzoncie sprowadza się do pętli: obserwować błędy, zamieniać je w testy, mierzyć wpływ zmian i mieć zaplanowaną degradację działania. W przypadku danych tabelarycznych to właśnie ta pętla decyduje, czy rozwiązanie będzie użyteczne po pierwszym wdrożeniu.
8. Przykłady: prompty dla RAG/Text-to-SQL/hybrydy oraz przykładowe odpowiedzi z cytowaniem
Poniższe przykłady pokazują, jak różni się sposób zadawania pytań i formułowania odpowiedzi w trzech podejściach: klasyczny RAG na tekstowych fragmentach, Text-to-SQL oraz hybryda. Dla czytelności „źródła” mają formę odwołań do konkretnych elementów danych (np. tabela/kolumny, wynik zapytania, fragment definicji). Każdy przykład zawiera: prompt oraz przykładową odpowiedź z cytowaniem.
8.1. Klasyczny RAG (chunking): gdy kontekst jest opisowy, a liczby są wtórne
Typowe zastosowanie: pytania o definicje metryk, reguły biznesowe, opisy pól, interpretację raportów, procedury liczenia. Dane tabelaryczne pojawiają się raczej jako kontekst niż materiał do obliczeń.
Prompt (RAG):
„Wyjaśnij, co oznacza metryka ‘aktywny użytkownik 30D’ w naszej analityce i jakie są wykluczenia. Odpowiedz krótko i zacytuj źródła.”
Przykładowa odpowiedź z cytowaniem:
Metryka aktywny użytkownik 30D oznacza użytkownika, który wykonał co najmniej jedną kwalifikowaną akcję w ciągu ostatnich 30 dni. Do kwalifikowanych akcji zaliczają się m.in. logowanie oraz wykonanie zdarzeń produktowych, natomiast wykluczane są działania systemowe oraz testowe konta. Źródła: definicja metryki w dokumentacji analitycznej (sekcja „Aktywność 30D”), lista wykluczeń (sekcja „Filtry i wyłączenia”), opis kwalifikowanych zdarzeń (sekcja „Zdarzenia produktowe”).
Kontrast: to podejście dobrze odpowiada na „co to znaczy” i „jak liczymy”, ale gorzej na „ile wynosi teraz” (zwłaszcza gdy liczba zależy od aktualnych danych lub wymaga agregacji).
8.2. Text-to-SQL: gdy pytanie wymaga policzenia, filtrowania i agregacji na żywych danych
Typowe zastosowanie: pytania o wyniki liczbowe, rankingi, trendy, segmentację, porównania okresów, wszystko co wymaga obliczeń na aktualnych rekordach.
Prompt (Text-to-SQL):
„Policz przychód (suma kwoty) za ostatnie 7 dni oraz porównaj do poprzednich 7 dni. Zwróć różnicę i % zmiany. Podaj źródła w postaci: użyte tabele/kolumny oraz zakres dat.”
Przykładowa odpowiedź z cytowaniem:
Przychód za ostatnie 7 dni wyniósł 1 240 000, a za poprzednie 7 dni 1 180 000. Zmiana to +60 000, czyli +5,1%. Źródła: wynik agregacji na tabeli transakcji (kolumny: kwota, data_transakcji), zakres dat: ostatnie 7 dni vs wcześniejsze 7 dni, filtr: transakcje zakończone (status = „completed”).
Kontrast: tu odpowiedź powinna wynikać bezpośrednio z wykonania zapytania na tabelach; RAG na opisach mógłby co najwyżej wyjaśnić definicję przychodu, ale nie poda wiarygodnej aktualnej wartości.
8.3. Hybryda: definicje i reguły z RAG + obliczenia z SQL
Typowe zastosowanie: pytania, które łączą semantykę biznesową (definicje, filtry, mapowania) z twardym wyliczeniem (SQL). Model najpierw „rozumie” metrykę z dokumentacji, a potem liczy ją na danych.
Prompt (Hybryda):
„Ilu mamy aktywnych użytkowników 30D w podziale na kanał pozyskania? Zastosuj definicję ‘aktywny użytkownik 30D’ z naszej dokumentacji, a w odpowiedzi podaj wyniki i cytowania: (1) fragment definicji, (2) tabele/kolumny użyte do wyliczeń.”
Przykładowa odpowiedź z cytowaniem:
W ostatnich 30 dniach aktywni użytkownicy 30D według kanału pozyskania to: organic 120 540, paid 88 210, referral 34 905. Źródła: definicja „aktywny użytkownik 30D” oraz wykluczenia (dokumentacja analityczna: sekcje „Aktywność 30D” i „Filtry i wyłączenia”); wyliczenie na danych zdarzeń użytkowników (tabele: zdarzenia_użytkowników oraz atrybucja_kanału; kolumny: user_id, timestamp_zdarzenia, typ_zdarzenia, kanał_pozyskania), okno czasowe: ostatnie 30 dni.
Kontrast: hybryda minimalizuje ryzyko „liczenia nie tego co trzeba” (bo definicja metryki jest cytowana) i jednocześnie nie opiera się na statycznych fragmentach tekstu przy obliczaniu aktualnych wartości.
8.4. Przykład, w którym RAG na chunkach najczęściej przegrywa
Prompt (źle dopasowany do RAG):
„Jaki był średni czas realizacji zamówienia w zeszłym miesiącu, z wyłączeniem zamówień anulowanych i z podziałem na magazyn?”
Co może pójść nie tak w RAG:
- Model przywoła opis wskaźnika lub przykładowe liczby z raportu, ale nie wykona poprawnej agregacji dla „zeszłego miesiąca”.
- Jeśli w materiałach są stare wyniki, odpowiedź może wyglądać wiarygodnie, ale będzie nieaktualna.
- Cytowanie „fragmentów raportu” nie jest równoznaczne z cytowaniem wyniku obliczeń na danych źródłowych.
Jak brzmi poprawna odpowiedź w podejściu SQL/hybrydowym (przykład formy):
„Średni czas realizacji w zeszłym miesiącu wyniósł 2,4 dnia (magazyn A: 2,1; magazyn B: 2,8). Źródła: obliczenie na tabeli zamówień i statusów (kolumny: data_utworzenia, data_realizacji, status, magazyn), filtr status != ‘canceled’, zakres: zeszły miesiąc kalendarzowy.”
8.5. Przykład, w którym Text-to-SQL bez RAG bywa ryzykowny
Prompt (ryzykowny bez definicji):
„Policz churn w ostatnim kwartale i porównaj do poprzedniego.”
Dlaczego to bywa pułapką:
- „Churn” może oznaczać rezygnację subskrypcji, brak aktywności, niewznowienie po okresie próbnym albo spadek przychodu (revenue churn).
- Bez przywołania definicji model może wygenerować SQL, który policzy „jakiś churn”, ale nie ten uzgodniony w organizacji.
Przykładowa odpowiedź hybrydowa z cytowaniem (forma):
„Churn w ostatnim kwartale wyniósł 3,2% vs 2,9% w poprzednim kwartale (+0,3 p.p.). Źródła: definicja churn (dokumentacja metryk: sekcja ‘Churn — definicja i okno obserwacji’); wyliczenie na tabelach subskrypcji (kolumny: customer_id, data_start, data_end, status) z podziałem na kwartały kalendarzowe.”
8.6. Minimalne wzorce promptów (bez kodu) dla trzech podejść
- RAG (chunking): „Odpowiedz na podstawie dostarczonych źródeł. Jeśli w źródłach nie ma jednoznacznej odpowiedzi, powiedz wprost, czego brakuje. Cytuj fragmenty, na których się opierasz.”
- Text-to-SQL: „Wygeneruj zapytanie do bazy zgodne ze schematem. Odpowiedź liczbową oprzyj wyłącznie o wynik wykonania zapytania. Zacytuj: tabele/kolumny i zakres filtrów.”
- Hybryda: „Najpierw ustal definicje/filtry z dokumentacji i zacytuj je. Następnie policz wynik na danych i zacytuj tabele/kolumny oraz parametry (okno czasu, statusy, segmenty).”
W praktyce „cytowanie” w RAG dotyczy treści opisowych (definicji i zasad), a w Text-to-SQL i hybrydzie powinno obejmować także to, na jakich danych i w jakim zakresie wykonano obliczenia — tak, by odbiorca mógł wynik zweryfikować. Jeśli chcesz poznać więcej takich przykładów, zapraszamy na szkolenia Cognity, gdzie rozwijamy ten temat w praktyce.