Strona główna / Artykuły

Kasowanie semantyczne za pomocą Redis w celu wyszukiwania wektorowego

Zmniejsz wydatki na API embeddingów dzięki semantycznemu cache’owaniu w Redis: dwupoziomowy cache, który ponownie wykorzystuje wyniki wyszukiwania wektorowego, gdy zapytania oznaczają to samo, w Node.js.

Wydawca: REACTAPP.TOP4 min czytania1033 słów

Źródła: Semantic Caching with Redis: Slashing API Costs in Vector Search

Budowa produktu opartego na sztucznej inteligencji często zaskakuje zespoły nie tyle jakością modelu, co kosztami faktury. Bot rozmówczy, który wydaje się tani przy kilkuset użytkownikach, może nagle generować ogromne wydatki za każdą wyszukiwanie, rekomendację lub pytanie, które powoduje kolejne wywołanie modelu embeddingowego.

Wiele z tych wywołań jest zbędnych. Kasowanie semantyczne umożliwia ponowne użycie wcześniejszych wyników, gdy nowe zapytanie oznacza niemal to samo. W Redis taka metoda może zmniejszyć opóźnienia i koszty infrastruktury bez konieczności przepisywania logiki biznesowej aplikacji.

Typowy proces generacji wzbogaconej o wyniki wyszukiwania (RAG) wygląda następująco:

User Query
     ↓
Embedding API
     ↓
Vector Database
     ↓
Retrieved Documents
     ↓
LLM

Każde nowe zapytanie zazwyczaj oznacza:

  • wywołanie modelu embeddingowego
  • płacenie za opóźnienia sieciowe
  • wykonanie wyszukiwania wektorowego
  • zwrot wyników

Wywołania modeli embeddingowych kosztują pieniądze, a podróże tam i z powrotem – czas.

Rozważmy dwa zapytania zadane z odstępem dwóch minut:

  • „najlepszy laptop do gier w niskim budżecie”
  • „tanie laptopy do gier”

Sformułowanie się różni; intencja prawie nie. A mimo to wiele systemów oblicza oba zapytania od nowa. Wśród tysięcy użytkowników platforma wielokrotnie płaci za praktycznie tę samą pracę.

Czym jest kierowanie semantyczne?

Klasyczne cache przechowują klucze w postaci dokładnych ciągów znaków. Jeśli zapisano „czerwone buty”, trafia tylko identyczny wyrażenie. Wyrażenie „buty, które są czerwone” nie zostanie odnalezione.

Kierowanie semantyczne porównuje znaczenie. Każde zapytanie przekształca się w wektor reprezentujący jego treść. Gdy dwa wektory są wystarczająco bliskie, można ponownie wykorzystać zapisaną odpowiedź.

Na przykład:

Query A
"best beginner Python book"
↓
Embedding A

Później:

Query B
"good Python books for beginners"

Jeśli podobieństwo kosinowe przekroczy wybraną granicę – powiedzmy 0,95 – system zwraca zapisaną odpowiedź. Nie ma potrzeby ponownego generowania wektora, nowego wyszukiwania wektorowego ani dodatkowej pozycji w rachunku.

Budowa dwupoziomowej pamięci cache

W projektach produkcyjnych często stosuje się dwie pamięci cache.

Poziom 1: pamięć cache embeddingów

Najszybszy poziom. Identyczny ciąg zapytań zwraca przechowywany embedding zamiast ponownego wywoływania modelu.

Query
   ↓
Redis
   ↓
Embedding

Samo to eliminuje jeden z najczęściej występujących płatnych wywołań w aplikacjach AI.

Poziom 2: pamięć cache wyników semantycznych

Drugi poziom przechowuje ostateczne odpowiedzi z wyszukiwania. Porównanie odbywa się na podstawie sąsiedztwa embeddingów, a nie surowego tekstu.

Embedding
      ↓
Redis Vector Search
      ↓
Cached Search Results

Bliższy sąsiad oznacza pominięcie bazy danych wektorowej i natychmiastową dostawę odpowiedzi z pamięci cache.

Dlaczego Redis?

Redis to szybki magazyn w pamięci operacyjnej. Dzięki Redis Stack i RediSearch może również przeprowadzać wyszukiwanie podobieństwa wektorów.

To połączenie umożliwia Redis:

  • przechowywanie embeddingów
  • porównywanie wektorów
  • zwracanie najbliższych sąsiadów
  • wygaszanie wpisów według TTL
  • dostarczanie wyników w ciągu kilku milisekund
  • Cache przestaje być prostym kontenerem klucz-wartość i staje się warstwą semantycznego wyszukiwania.

    Wybór odpowiedniego progu podobieństwa

    Wybór progu ma decydujące znaczenie dla jakości. Przy 0,99 dopasowują się tylko niemal identyczne dane — wysoka precyzja, mniej wyników. Przy 0,85 wzrasta liczba trafień, ale mogą występować zbieżności między niepowiązanymi zapytaniami (buty do biegania a buty do koszykówki).

    Tolerancja zależy od dziedziny. E-commerce często może akceptować bardziej luźne progi; wyszukiwanie medyczne zazwyczaj nie. Niektóre systemy ustalają nawet różne progi dla poszczególnych kolekcji.

    Utrzymywanie cache w aktualności

    Odpowiedzi z cache są przydatne tylko dopóki są prawidłowe. „Najnowsze ceny iPhone’ów” mogą stać się przestarzałe już w ciągu nocy bez planu odświeżania. Dwa powszechne podejścia rozwiązują większość przypadków.

    Wygaszanie oparte na czasie

    Ustaw wartość TTL, aby Redis automatycznie usuwał wpisy — to przydatne w przypadku katalogów, które często się zmieniają.

    Nieważność oparta na zdarzeniach

    Gdy aplikacja wie o zmianie — aktualizacji zapasów, edycji cen, dodaniu nowych dokumentów lub ich usunięciu — natychmiast usuwa dotknięte klucze, zamiast czekać na upływ TTL.

    Jak Redis wyszukuje embeddingi

    Współczesny Redis przechowuje embeddingi jako wektory zmiennoprzecinkowe i indeksuje je za pomocą struktur takich jak:

    • FLAT
    • HNSW

    Nowe zapytanie nie przeszukuje wszystkich wektorów metodą siłowej; indeksy szybko znajdują sąsiednie elementy, dzięki czemu wyszukiwania semantyczne zazwyczaj trwają zaledwie kilka milisekund.

    Łączenie wszystkiego razem

    Ścieżka żądania od początku do końca wygląda następująco:

    User Query
          ↓
    Embedding Cache
          ↓
    Hit?
          ↓
    Yes → Use Cached Embedding
    No
     ↓
    Embedding API
     ↓
    Redis Semantic Cache
     ↓
    Hit?
     ↓
    Yes → Return Cached Results
    No
     ↓
    Vector Database
     ↓
    Store Results in Redis
     ↓
    Return Results
    

    Droga obejmująca obliczanie embeddingu i wyszukiwanie jest wykonywana tylko w przypadku nieudanych prób znalezienia danych. Wszystko inne pozostaje szybkie.

    Jakie ulepszenia można się spodziewać?

    Liczby różnią się w zależności od produktu, ale wiele raportów produkcyjnych wygląda następująco:

    • zwykłe wyszukiwanie semantyczne często osiąga wskaźnik trafień na poziomie **od jednej piątej do jednej trzeciej**
    • wyszukiwanie automatyczne oraz powtarzane zapytania mogą stanowić **mniej więcej połowę** ruchu
    • odpowiedzi z pamięci cache przychodzą zazwyczaj w ciągu **kilku milisekund**
    • próba wyszukania z treścią wplecioną, przeprowadzana od początku do końca, może zająć **kilkaset milisekund**

    Każde trafienie oznacza jedną mniej wywołaną funkcję API do generowania embeddingów. Przy tysiącach zapytań daje to znaczące oszczędności pod względem kosztów i opóźnień.

    Częste pułapki

    Zatrucie pamięci cache

    Zła odpowiedź, która trafi do pamięci cache, może zatruć podobne przyszłe zapytania. Monitorowanie w połączeniu z rozsądnym okresem ważności zmniejsza zakres wpływu tego problemu.

    Rozrost pamięci

    Embeddingi są duże: każdy składa się z setek lub tysięcy liczb zmiennoprzecinkowych. Zbiory te szybko rosną. Przed uruchomieniem należy ustalić limity pamięci oraz zasady usuwania danych.

    Początkowe fazy działania

    Każde nowe wdrożenie zaczyna się od pustej bazy danych, więc początkowy ruch generuje pełne koszty. Popularne zapytania z czasem ogrzewają pamięć cache; niektóre zespoły wcześniej ładowają najczęściej wyszukiwane zapytania, aby skrócić ten proces.

    Kiedy katalogowanie semantyczne nie jest odpowiednim rozwiązaniem

    To podejście sprawdza się, gdy wielu użytkowników zadaje podobne pytania. Słabnie natomiast, gdy odpowiedzi są silnie spersonalizowane lub bardzo aktualne:

    • „Restauracje w pobliżu”
    • spersonalizowane rekomendacje
    • panel sterowania dostosowany do konkretnego użytkownika
    • aktualny stan zapasów
    • szybko zmieniające się ceny

    W takich przypadkach użycie zapisanej w cache semantycznej odpowiedzi może być po prostu błędne.

    Podsumowanie

    W miarę wzrostu ruchu generowanego przez sztuczną inteligencję, koszty infrastruktury często konkurują z jakością modeli o uwagę użytkowników. Semantyczne cacheowanie to praktyczne rozwiązanie: umożliwia rozpoznawanie wspólnych intencji, omijając w ten sposób powtarzające się operacje embedowania i wyszukiwania. Redis ułatwia to dzięki połączeniu szybkości pracy w pamięci operacyjnej z wbudowanym wyszukiwaniem wektorowym — szybsze odpowiedzi, mniej płatnych wywołań funkcji embedowania oraz bardziej efektywne skalowanie. Czasami najlepszą optymalizacją nie jest lepszy model, ale pominięcie operacji, które w ogóle nie musiały być wykonywane.