Jak ograniczać koszty w aplikacjach RAG bez pogarszania jakości

Kwiecień 21, 2026 | #ai , #architecture , #costs , #llm , #rag

000000
000001
100000
010010

Nie od dziś wiadomo, że dobrą implementację poprzedza dobry plan. Nie inaczej jest w przypadku aplikacji RAG, której wartość dla klienta jest wynikową rozważań o pieniądzach i jakości, gdzie cięcie kosztów nie tylko wpływa pozytywnie na zawartość portfela, ale paradoksalnie może też podnieść jakość osiąganych wyników. Dobór właściwego modelu to tylko pierwszy krok, a jak pokazuje praktyka, wcale nie najważniejszy.

Czym właściwie jest RAG

Dla lepszego zrozumienia omawianych zagadnień wyjaśnijmy na początek, czym jest RAG. RAG (ang. Retrieval-Augmented Generation) to podejście, w którym model językowy nie odpowiada wyłącznie na podstawie własnej wiedzy, ale dostaje dodatkowy kontekst pobrany z dokumentów, bazy wiedzy albo innego zbioru danych. W praktyce taki mechanizm działa zwykle tak:

  1. użytkownik zadaje pytanie,
  2. system wyszukuje najbardziej pasujące fragmenty dokumentów,
  3. dopiero potem model LLM (Large Language Model) generuje odpowiedź na podstawie pytania i tych fragmentów.

RAG dobrze sprawdza się tam, gdzie model musi korzystać z konkretnej, aktualnej lub prywatnej wiedzy. Nie oznacza to jednak, że sam RAG rozwiązuje problem złożonego rozumowania. Pomaga dostarczyć właściwe dane, ale nie gwarantuje jeszcze poprawnego wnioskowania.

Do czego nadaje się RAG

  • FAQ, helpdesk, chatboty firmowe
  • Praca na dokumentach (PDF, instrukcje, regulaminy)
  • Wyszukiwanie wiedzy (semantic search)
  • Integracja z wewnętrznymi danymi (CRM, bazy wiedzy)
  • Odpowiedzi wymagające aktualnych danych niedostępnych w trakcie trenowania modelu

Do czego sam RAG zwykle nie wystarcza

  • Zadania wymagające głębokiego rozumowania / logiki
  • Złożone decyzje biznesowe lub diagnostyka ekspercka bez dodatkowych zabezpieczeń
  • Przetwarzanie danych wymagające spójności i precyzji (np. księgowość), jeśli wynik ma być użyty automatycznie
  • Gdy wiedza musi być w 100% poprawna i gwarantowana
  • Zadania bez potrzeby zewnętrznej wiedzy (tu często lepszy jest czysty LLM albo klasyczna logika aplikacyjna)

Zrozumienie do czego nadaje się RAG oraz jak działa jest pierwszą i podstawową kwestią potrafiącą zaoszczędzić masę pieniędzy.

W jednym z projektów, w których brałem udział, postanowiono użyć tej technologii do „sterowania” na stronie internetowej sortowaniem i filtrowaniem tabeli z rozbudowanymi i dość złożonymi danymi. Rozwiązanie działało, ale model zbyt często się mylił. Najgorsze było jednak to, że wpisanie lub podyktowanie promptu na tyle precyzyjnego, aby dawał zadowalające wyniki, było zwyczajnie trudniejsze i trwało dłużej niż kliknięcie w parę opcji i zatwierdzenie formularza.

Chwilę nam zajęło, zanim zrozumieliśmy, że w naszym przypadku prędkość uzyskiwanych wyników jest czynnikiem, który dyskwalifikuje nasz proof of concept, ale zdążyliśmy się nauczyć m.in. tego, że zmiana modelu na większy, dokładanie dokumentów do kontekstu i wydłużanie promptu często prowadzi jedynie do podniesienia kosztów bez poprawy wyników.

Śmieci na wejściu oznaczają śmieci na wyjściu

Wejdźmy trochę bardziej w technikalia. W aplikacji RAG dołączenie dokumentu do promptu nie odbywa się wprost. To nie jest tak, że do zapytania, które przesłał użytkownik, dodaje się w całości treść jakiegoś PDF-a albo nawet kilkudziesięciu dokumentów i tak skonstruowany prompt przesyła się do modelu językowego. Każdy dokument, który ma zostać użyty w kontekście aplikacji RAG, jest najpierw przygotowywany. Wygląda to tak:

  1. Odczytanie dokumentu (np. PDF, baza wiedzy)
  2. Podział treści na małe fragmenty (chunki)
  3. Utworzenie embeddingów dla każdego fragmentu
  4. Zapisanie embeddingów w bazie wektorowej

Embedding to matematyczna reprezentacja znaczenia tekstu umożliwiająca porównanie semantyczne.

W trakcie korzystania z aplikacji RAG zapytanie użytkownika jest w locie embedowane, czyli zamieniane w matematyczną reprezentację, co pozwala odpytać bazę wektorową, która porównuje je z zapisanymi w niej fragmentami i zwraca te najbardziej pasujące. Dzięki temu do kontekstu promptu nie jest dołączana cała „baza dokumentów”, tylko najlepiej dobrane chunki. Im trafniejsze dopasowanie i bardziej adekwatne kawałki, tym lepsza odpowiedź.

Śmieci na wejściu do modelu LLM oznaczają kiepski prompt albo dodanie do kontekstu prompta nieoptymalnych fragmentów dokumentów.

Limit dokumentów

Jednym z najczęstszych błędów jest wrzucanie do promptu zbyt wielu fragmentów dokumentów. Zwykle dzieje się tak wtedy, gdy zespół nie ufa wyszukiwaniu i próbuje „na wszelki wypadek” dać modelowi bardzo dużo materiału. Problem polega na tym, że większy kontekst prawie zawsze zwiększa koszt, ale nie gwarantuje proporcjonalnej poprawy jakości odpowiedzi. To zjawisko dobrze pokazuje praca Lost in the Middle: How Language Models Use Long Contexts, która opisuje ograniczenia modeli w wykorzystywaniu informacji ukrytych w długim kontekście.

Adaptive Context Windows to technika dostosowywania liczby dołączanych do kontekstu fragmentów w zależności od pytania. Pytania mają różny stopień złożoności:

  • proste faktograficzne - zwykle wystarczą 1–2 fragmenty,
  • średnie - kilka fragmentów,
  • złożone porównania lub wyjaśnienia - mogą wymagać więcej fragmentów.

To jedna z najważniejszych optymalizacji kosztu, bo ogranicza tokeny wejściowe przy każdym zapytaniu.

Optymalny podział na chunki

Wbrew pozorom podział na fragmenty nie jest zadaniem trywialnym. Można podzielić treść na zbyt obszerne kawałki, co podnosi liczbę marnowanych tokenów i powoduje przesłanie do modelu wielu zbędnych informacji, wprowadzających jedynie szum. Zbyt małe fragmenty mogą z kolei spowodować ucięcie ważnych informacji. Jeśli system odpowiada poprawnie, ale tylko dlatego, że model jest w stanie „domyślić się” braków w dostarczonym kontekście, to takie rozwiązanie jest kruche.

Istotne jest też dobranie odpowiedniego overlap, czyli „zakładki”. Chodzi o to, że sąsiadujące ze sobą fragmenty zachodzą na siebie, aby nie urywać kontekstu w złym miejscu i aby model był w stanie rozpoznać, że jeden chunk jest kontynuacją drugiego, a nie zupełnie innym fragmentem dokumentu.

Nie ma złotego podziału. Powszechnie praktykuje się podział na chunki w blokach od 128 do 512 tokenów. Dla zakładki (overlap) przyjmuje się zwykle 10-20% treści. Od tego można zacząć, ale to dopiero początek.

Microsoft Learn w materiale o zaawansowanych systemach RAG zwraca uwagę, że strategia fragmentowania powinna zależeć od typu danych, sposobu ich aktualizacji i oczekiwanej jakości wyszukiwania. Optymalny rozmiar bloku wcale nie oznacza stałej wartości dla wszystkich fragmentóœ. Instrukcje techniczne wymagają innego podziału na części niż FAQ, a podział według struktury może okazać się ważniejszy niż sam rozmiar chunka. I tak dokumenty Markdown można dzielić na sekcje wyznaczone nagłówkami h1 (#), h2 (##), h3 (###), a listingi kodu według funkcji. Ideą jest zachowanie nie tylko całych zdań, ale całych, logicznie spójnych sekcji.

Selekcja danych

Retrieval, czyli etap wyszukiwania fragmentów dokumentów, które mają pomóc odpowiedzieć na pytanie użytkownika, to bardzo złożony proces, który musi sobie poradzić z szeregiem wyzwań, jak np. wieloznaczność terminów. Stosuje się tu różnego rodzaju algorytmy, a także techniki typu:

Wyszukiwanie hybrydowe - łączy klasyczne wyszukiwanie pełnotekstowe po słowach kluczowych (BM25 / keyword search) z wyszukiwaniem semantycznym. W zależności od rozwiązania oba sygnały mogą być liczone równolegle albo w kilku etapach, a ich połączenie zwykle poprawia trafność.

Filtrowanie po metadanych - oznacza po prostu zawężenie wyników jeszcze przed wyszukiwaniem semantycznym, na przykład do konkretnego typu dokumentu, działu, produktu albo daty.

Selekcja i deduplikacja tematyczna - Select Diverse - wybiera z listy znalezionych fragmentów takie fragmenty, które pokrywają różne aspekty tematu starając się unikać powtórzeń. Przy pytaniu złożonym 5 bardzo podobnych chunków bywa gorsze niż 2 bardzo trafne + 3 uzupełniające.

Kompresja kontekstu - oznacza próbę skrócenia znalezionych fragmentów do tych części, które są naprawdę istotne dla pytania.

Wszystkie wymienione techniki sprowadzają się do możliwie taniego odnalezienia najbardziej trafnych fragmentów i zawężenia listy wyników do tych, które naprawdę warto dołączyć do kontekstu. To prowadzi do ograniczenia liczby tokenów, obniżenia kosztów i zazwycza poprawia trafność odpowiedzi.

Hierarchiczne wyszukiwanie, w którym wyszukuje się fragmenty, ale zwraca np. pełny dokument, aby dostarczyć cały kontekst, jest niejako odwrotnością wyżej wymienionych metod. Świadome zastosowanie tej techniki może wydłużyć prompt, ale jednocześnie zapobiec obcięciu istotnych treści i konieczności ponowienia pytania. To z kolei nie oznacza jeszcze, że lepsza jakość musi kosztować więcej, bo unika się dodatkowych żądań.

Abstrahując od wyjątków, mniejszy, ale precyzyjniejszy prompt zwykle znaczy lepiej. To ważne również dziś, gdy modele obsługują coraz większy kontekst. Duży kontekst nie usuwa problemu wyboru właściwych informacji. Może go tylko ukryć wyższym kosztem.

Weryfikacja jakości odpowiedzi retrievala

Modele językowe potrafią generować odpowiedzi brzmiące wiarygodnie nawet wtedy, gdy nie mają wystarczających podstaw w danych wejściowych. W praktyce w aplikacjach RAG dobrym pomysłem jest ocena jakości wyników zwróconych z bazy wektorowej. Jeśli retrieval nie daje wystarczająco dobrych lub jednoznacznych rezultatów, lepiej przerwać generację albo przejść do fallbacku niż płacić za odpowiedź niskiej jakości. Zależność między relewantnością dostarczonej wiedzy a jakością odpowiedzi dobrze opisuje praca Do You Know What You Are Talking About? Characterizing Query-Knowledge Relevance For Reliable Retrieval Augmented Generation.

Jakość wyników retrievala można wstępnie ocenić tanim kosztem na podstawie similarity score / distance. To jednak raczej heurystyka niż twardy dowód jakości, dlatego warto dodać do niej dodatkowe sprawdzenia, jeśli przypadek tego wymaga, np. sprawdzić, czy zachodzi spójność pytania i odpowiedzi. Gdy pytanie dotyczy produktu X, a wyniki pochodzą z dokumentów produktu Y, to raczej nie można się spodziewać zadowalającej odpowiedzi. Potrzebę osobnego oceniania retrievalu i generacji podkreślają też prace RAGAs oraz ARES.

Ostatecznie warto poinstruować model, aby w przypadku nieznalezienia odpowiedzi na zadane pytanie zwrócił krótkie "Nie wiem" zamiast rozbudowanej odpowiedzi bez oparcia w danych, ograniczając tym samym liczbę zmarnowanych tokenów.

Model Routing

Różne pytania mają różny stopień skomplikowania, a co za tym idzie, czasami odpowiedź jest wręcz trywialna, a niekiedy wymaga „przemyślenia”. Wychodząc z tego założenia, warto rozpatrzyć użycie różnych modeli LLM do rozpatrywania odmiennych kwestii.

Mamy narzędzia, które potrafią mierzyć różne metryki promptu, między innymi:

  • długość,
  • liczbę unikalnych terminów,
  • „gęstość techniczną”,
  • liczbę bloków kodu

Jeśli jesteśmy więc w stanie na podstawie tych metryk pogrupować pytania według stopnia złożoności, np. na proste faktograficzne, złożone techniczne, wymagające kreatywności itd., to do każdej z tych grup możemy przypisać inny model LLM. Odpowiedzi na najprostsze pytania możemy uzyskać wręcz od modelu uruchomionego lokalnie, np. z użyciem Ollama, a pozostałe kierować do modeli w chmurze poprzez API. Tu również mamy wybór co do ceny i szybkości działania. Routing może ograniczać koszt RAG, bo pozwala dobierać tańszą ścieżkę lub lżejszy model do prostszych zapytań zamiast uruchamiać zawsze najdroższy wariant jak w case studies: Reducing Inference Cost by 25–60% with Model Routing + Token Budgets (Quality Held Steady.

Infrastruktura też kosztuje

Zarówno modele embeddingowe, jak i LLM możemy uruchomić na infrastrukturze własnej. Czasem jest to kwestia wymagań - jeśli dotyczy danych wrażliwych - a niekiedy oszczędności. Zakup kart graficznych, pamięci RAM, dysków, procesorów itd. to wydatek nietrywialny, ale w dłuższej perspektywie może się opłacić. Tym bardziej, że przecież zazwyczaj - przynajmniej część - tego kosztu i tak ponosimy, musząc gdzieś hostować choćby samą aplikację RAG. Niekiedy więc jest to tylko kwestia rozbudowy już istniejącego rozwiązania.

Przygotowując się do napisania tego artykułu, natrafiłem na studium przypadku Building Cost-Effective RAG: From $3K to $150/month, w którym autor opisuje, jakie konkretnie kroki podjął, aby ograniczyć koszty działania aplikacji RAG. Jedna z optymalizacji polegała na zastąpieniu zewnętrznej, chmurowej usługi Pinecone doinstalowaniem rozszerzenia pgvector do funkcjonującej już w ramach stacku bazy danych PostgreSQL. Oprócz wyeliminowania dodatkowej usługi autor wspomina także o korzyściach takich jak ACID - zapewniający spójność danych, pełna kontrola nad backupami, migracjami, replikami oraz pełnoprawny SQL.

Współczesne modele językowe mają wbudowane dodatkowe narzędzia, które pozwalają im realizować niektóre zadania szybciej, bez angażowania procesów głębokiego myślenia. Na przykład operacje matematyczne, często po sprowadzeniu ich do wyrażonego formalnie równania, są przekierowywane do kalkulatora i sam wynik jest już obliczany bez użycia modelu językowego. To samo można zrobić po stronie aplikacji, obsługując niektóre pytania alternatywnymi narzędziami wyszukiwania. I w ten scenariusz świetnie wpisuje się wspomniany wyżej PostgreSQL.

Cache i ograniczenie pracy powtórnej

Prostym i powszechnie stosowanym sposobem poprawy wydajności oraz oszczędności jest zapobieganie wykonywaniu tej samej pracy wielokrotnie. W aplikacjach RAG cache'owanie odbywa się na kilku etapach:

Unikanie wielokrotnego embedowania

Dodawanie dokumentów do bazy wektorowej wykonuje się raz. Embedowanie wymaga użycia specjalnego modelu embeddingowego. To inny typ modelu niż LLM, zoptymalizowany do reprezentowania znaczenia jako wektory, a nie do generowania tekstu. Jego użycie też kosztuje, więc jeśli dokument się nie zmienił, nie ma potrzeby ponownego przeliczania go na reprezentację numeryczną. Potrzeba taka może zajść, jeśli zmienimy model embeddingowy, ponieważ - jak już wspomniano - zapytanie użytkownika embeduje się w locie, a wygenerowane w ten sposób wektory muszą pasować do przestrzeni wektorowych dokumentów zapisanych w bazie wektorowej. Wątek pracy przyrostowej i aktualizowania tylko zmienionych danych pojawia się m.in. w dokumentacji Microsoft Learn dotyczącej aktualizowania indeksu.

Na marginesie dodam, że oszczędności wynikające z wyboru tańszego modelu do embeddingu mogą się okazać strzałem w stopę. Większa liczba wymiarów sama w sobie nie gwarantuje lepszej jakości, ale w praktyce lepsze modele embeddingowe często dają trafniejsze dopasowania. To jednak zależy od konkretnego modelu, domeny i charakteru danych.

Cachowanie wyników

Nie każde pytanie wymaga pełnego uruchomienia całego RAG. Część zapytań może się powtarzać, np. „Jak zresetować hasło”, albo być semantycznie zbliżona, jak „Jak zmienić hasło”. Takie przypadki da się obsłużyć bez ponownego odpytywania bazy wektorowej - cache'owanie wyników retrievala - lub nawet bez ponownego wywoływania modelu LLM - cache'owanie odpowiedzi modelu.

Porównywanie promptów może być dosłowne - exact query cache - lub też semantyczne - semantic cache - polegające na porównaniu embeddingów pytań. Jeśli podobieństwo między pytaniami nie schodzi - załóżmy - poniżej progu 0,95, można przyjąć, że jest to pytanie o to samo co wcześniej. W projektach typu FAQ po zastosowaniu normalizacji (lowercase, usuwanie zbędnych znaków, standaryzacja formy pytania itp.) może się to sprawdzić i przynieść realne korzyści.

Prompt-caching, context-caching

Wyobraź sobie, że w Twojej aplikacji RAG konstruujesz prompt, który wygląda tak:

[SYSTEM PROMPT: 2000 tokenów]
[CONTEXT: 5000 tokenów]
[USER QUESTION: 20 tokenów]

Jeśli jedyne, co się zmienia, to USER QUESTION, to wysyłając za każdym razem całość, zużywasz ponad 7 tys. tokenów przy każdym żądaniu. Prompt-caching to mechanizm, który w części modeli i API pozwala wysłać całość tylko za pierwszym razem, a następnie przez czas życia danych w cache (TTL) przesyłać jedynie to, co się zmienia. Jest to pamięć podręczna realizowana po stronie modelu, ale jej dostępność i skuteczność zależą od dostawcy oraz konkretnego wzorca użycia. W modelach rozliczanych tokenowo taki mechanizm ma bezpośredni wpływ na koszt wejścia, co widać także w oficjalnym cenniku API OpenAI.

Ręka na pulsie

Jeśli nie mierzysz input tokens, output tokens, cache hit rate i kosztu per request, to nie wiesz, które decyzje naprawdę przyniosły oszczędność. Optymalizacja bez pomiaru łatwo staje się tylko intuicją. Obserwowalność daje jednak jeszcze jedną korzyść, która, ignorowana, może doprowadzić do katastrofy, a mianowicie pozwala szybko zorientować się, że coś jest nie tak.

W aplikacjach RAG łatwo skupić się na jakości odpowiedzi i pominąć prosty fakt: publiczny albo darmowy dostęp bez ograniczeń zachęca do nadużyć (abuse). Nie musi to nawet wyglądać jak klasyczny atak. Wystarczy, że ktoś zacznie wysyłać dużo kosztownych zapytań, używać systemu do innych celów niż planowane albo zautomatyzuje ruch przez skrypt. Dla właściciela systemu efekt jest ten sam: koszt rośnie (cost explosion), a wartość biznesowa nie. Tego typu problem dobrze opisuje OWASP API4:2023 Unrestricted Resource Consumption, gdzie brak limitów jest pokazany nie tylko jako ryzyko dostępności, ale też kosztów operacyjnych.

Dlatego obok optymalizacji architektury warto wdrożyć podstawowe mechanizmy ochronne: ograniczanie liczby żądań w czasie, limity użycia per użytkownik albo klucz API, progi odcinające nietypowo kosztowne zapytania, budżet tokenowy per request i monitoring nagłych wzrostów ruchu. Tego rodzaju praktyki są zgodne z zaleceniami NIST SP 800-204, które wprost wymienia throttling i rate limiting jako elementy ochrony systemów opartych o API.

Najwięcej kosztuje zły pipeline

Pipeline, czyli łańcuch kroków wykonywanych przez aplikację w trakcie działania, na który składają się: przygotowanie pytania, wyszukanie fragmentów dokumentów, zbudowanie promptu, wywołanie modelu i zwrócenie odpowiedzi, to szereg decyzji architektonicznych wpływających na jakość, prędkość i koszt uzyskiwanych rezultatów.

Budując aplikację RAG od podstaw lub też przystępując do „podrasowania” już istniejącego rozwiązania, warto w pierwszej kolejności podzielić koszty na podstawowe kategorie:

  • koszt przygotowania embeddingów - utworzenie bazy wektorowej,
  • koszty inferencji (inference) - czyli użycie modelu LLM do wygenerowania wyniku na podstawie danych wejściowych,
  • koszty infrastruktury - koszty zakupu usług chmurowych typu baza wektorowa, narzędzie do monitoringu, zakup kart graficznych w celu uruchomienia modeli na infrastrukturze własnej, Następnie należy zastanowić się, które z tych pozycji mają charakter jednorazowy, które są powtarzalne i których można uniknąć. Od samego początku warto też mierzyć przynajmniej najważniejsze aspekty wpływające na stan portfela, aby w toku prac móc stwierdzić, czy wprowadzane usprawnienia przynoszą efekt. Uzbrojeni w „szkiełko i oko” będziemy mogli ocenić, czy przedstawione poniżej tezy są prawdziwe:

Dla jakości wyników kluczowe znaczenie ma odpowiednie przygotowanie embeddingów i jakość wyników zwracanych przez retrieval.

Z punktu widzenia kosztów, ale też prędkości odpowiedzi, w typowych zastosowaniach produkcyjnych bardzo ważne są metody, które pozwalają ograniczyć liczbę wywołań modeli LLM lub embeddingowych, a najlepiej wyeliminować ich część poprzez cache'owanie, alternatywne metody wyszukiwania czy unikanie powtarzalnych czynności. W drugiej kolejności znaczenie mają wszelkie techniki kierowania zapytań do tańszych modeli lub skracania długości kontekstu.

Na końcu lecz nie mniej ważne jest zadbanie o bezpieczeństwo, które wyeliminuje groźbę niespodziewanych wydatków w wyniku nadużyć lub też nieświadomego, niepoprawnego używania aplikacji.

Akceptuję Ta strona zapisuje niewielkie pliki tekstowe, nazywane ciasteczkami (ang. cookies) na Twoim urządzeniu w celu lepszego dostosowania treści oraz dla celów statystycznych. Możesz wyłączyć możliwość ich zapisu, zmieniając ustawienia Twojej przeglądarki. Korzystanie z tej strony bez zmiany ustawień oznacza zgodę na przechowywanie cookies w Twoim urządzeniu.