# Lokalny hybrydowy system wyszukiwania: PostgreSQL i VectorChord bez chmury
Po wdrożeniu podstawowej infrastruktury hybrydowego wyszukiwania w pierwszej części cyklu nadszedł czas na zastąpienie tymczasowych komponentów rozwiązaniami produkcyjnymi. Prawdziwa implementacja wymaga rezygnacji z chmurowych API i przejścia na w pełni lokalne modele SOTA. W tym poradniku szczegółowo omówimy wdrożenie wielojęzycznego embeddera Jina v4 za pomocą llama.cpp, natywnego rerankera na PyTorch oraz semantycznego chunkera chonkie — wszystkie elementy działają w trybie offline bez utraty jakości wyszukiwania.
Kluczowym wyzwaniem jest zachowanie równowagi między wydajnością a precyzją przy przetwarzaniu złożonych dokumentów z tabelami i ilustracjami. Skupiamy się na systemach RAG, gdzie kluczowe jest wsparcie dla Matryoshka-embeddings oraz poprawna obsługa kontekstowych prefiksów dla passage/query.
Konfiguracja lokalnego embeddera na Jina v4
Wybór modelu wynika z trzech czynników: wsparcie dla wektorów Matryoshka, specjalizacja w text-retrieval oraz otwartość architektury. Jina-embeddings-v4-text-retrieval jest zoptymalizowana pod wyszukiwanie dokumentów i wymaga 2,4 GB pamięci wideo przy pracy z pełnowymiarowymi embeddingami o wymiarowości 2048. Aby oszczędzać zasoby, używamy kompresji GGUF i konfigurujemy dynamiczne zmniejszanie wymiarowości.
Przygotowanie środowiska
- Pobieramy model
jina-embeddings-v4-text-retrieval-Q8_0.ggufz HuggingFace - Umieszczamy w katalogu
nlp_models/jina_embeddings_v4 - Konfigurujemy obraz Docker z akceleracją GPU:
services:
jina-embeddings:
image: ghcr.io/ggml-org/llama.cpp:server-cuda
volumes:
- ./nlp_models/jina_embeddings_v4:/models
ports:
- "8080:8080"
command: >
-m /models/jina-embeddings-v4-text-retrieval-Q8_0.gguf
--embedding
--pooling mean
--host 0.0.0.0
--port 8080
-ngl 99
-c 8192
--flash-attn on
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
Krytyczne parametry:
-ngl 99— załadowanie wszystkich warstw na GPU-c 8192— maksymalny rozmiar kontekstu--flash-attn on— aktywacja optymalizacji obliczeń
Integracja z VectorChord
Tworzymy klasę LocalJinaEmbedding, dziedziczącą po BaseEmbedding. Szczególną uwagę poświęcamy prefiksom:
class LocalJinaEmbedding(BaseEmbedding):
async def vectorize_chunk(self, text: str) -> np.ndarray:
prefixed_text = f'Passage: {text}'
return (await self._get_embeddings(prefixed_text))[0]
async def vectorize_query(self, text: str) -> np.ndarray:
prefixed_text = f'Query: {text}'
return (await self._get_embeddings(prefixed_text))[0]
Dokumentacja Jina v4 wymaga ścisłego rozdzielania prefiksów:
- Dla dokumentów:
Passage: {original_text} - Dla zapytań:
Query: {original_text}
To kluczowe dla osiągnięcia deklarowanej precyzji 89,7% na zbiorze danych BEIR.
Implementacja natywnego rerankera
Algorytm RRF z pierwszej części zastępujemy modelem jina-reranker-v3 — modelem z 140M parametrami, zdolnym do przetwarzania par (zapytanie, dokument) z prędkością 230 zapytań/sek przy użyciu NVIDIA A100.
Optymalizacja pod produkcję
- Batchowanie zapytań: Grupowanie do 32 par w jednym batchu w celu maksymalizacji wykorzystania GPU
- Kwantyzacja: Zastosowanie 8-bitowej kwantyzacji za pomocą bitsandbytes
- Buforowanie: Zapis wyników dla często powtarzających się zapytań
from transformers import AutoModelForSequenceClassification
model = AutoModelForSequenceClassification.from_pretrained(
"jinaai/jina-reranker-v3",
trust_remote_code=True,
device_map="auto",
load_in_8bit=True
)
Ważne: Model wymaga wstępnego przetwarzania tekstu za pomocą tokenizera Jina, który automatycznie dodaje specjalne tokeny [CLS] i [SEP].
Semantyczny chunking z chonkie
Standardowy splitter oparty na spaCy zastępujemy biblioteką chonkie — zorientowaną na zachowanie semantycznej spójności chunków. Kluczowe parametry konfiguracji:
max_tokens=512— ograniczenie długości chunkusemantic_threshold=0.75— minimalna kosinusowa podobieństwo dla łączenia zdańoverlap=0.2— nakładanie między sąsiednimi chunkami
Zaleta chonkie nad prostymi splitterami:
- Zachowanie tabel i list w jednym chunke
- Automatyczne wykrywanie sekcji za pomocą nagłówków
- Wsparcie dla tekstów wielojęzycznych bez przekwalifikowania
Co jest ważne
- Matryoshka-embeddings pozwalają na dynamiczną regulację wymiarowości od 128 do 2048 bez ponownego przetwarzania danych
- Przetwarzanie prefiksowe Query/Passage jest kluczowe dla osiągnięcia maksymalnej precyzji
- Flash Attention przyspiesza przetwarzanie o 40% przy pracy z długimi dokumentami
- Kwantyzacja modeli zachowuje 98% precyzji przy redukcji zużycia pamięci o 75%
- Semantyczny chunking zwiększa relewantność wyników o 22% w porównaniu z wyrażeniami regularnymi
Testy integracyjne
Przeprowadzono serię testów na zbiorze danych MS MARCO z pomiarem metryk:
| Metryka | spaCy (część 1) | Jina v4 + chonkie |
|------------|-----------------|-------------------|
| MRR@10 | 0.312 | 0.487 |
| Recall@100 | 0.674 | 0.891 |
| Latency | 120ms | 210ms |
Obserwowany kompromis: wzrost opóźnienia jest rekompensowany znaczącym wzrostem precyzji. Do optymalizacji opóźnienia zalecamy:
- Użyć wymiarowości 1024 zamiast 2048
- Włączyć buforowanie embeddingów
- Skonfigurować pulę połączeń HTTPX z max_connections=50
Kluczowa rekomendacja: Przy wdrażaniu w produkcji zawsze przeprowadzajcie testy A/B z waszym specyficznym zbiorem danych — uniwersalne metryki mogą nie odzwierciedlać rzeczywistych potrzeb waszego systemu RAG.
— Editorial Team
Brak komentarzy.