Modele embeddingów po polsku - bge-m3, multilingual-e5 czy PolDense? Test na 900 pytaniach
POWRÓT_DO_BLOGA
AI & Automatyzacja 14 min

Modele embeddingów po polsku - bge-m3, multilingual-e5 czy PolDense? Test na 900 pytaniach

Paweł Wiszniewski
Paweł Wiszniewski
Specjalista SEO & GEO · AI Engineer

Model embeddingów zamienia tekst na wektor liczb, dzięki czemu RAG, chatbot czy wyszukiwarka znajdują fragmenty podobne znaczeniowo do pytania, a nie tylko te z tymi samymi słowami. Który wybrać do polskich tekstów? Reguła z mojego testu na 900 prawdziwych pytaniach klientów: jeśli licencja Gemma Ci odpowiada, weź PolDense-150M (81,3 NDCG@10, 149 mln parametrów, działa na zwykłym CPU). Jeśli potrzebujesz licencji MIT lub Apache, weź multilingual-e5-large (80,7) albo bge-m3 (79,7, najodporniejszy na pytania bez polskich znaków i z literówkami). Do ok. 1 mln fragmentów wystarczy serwer z CPU, powyżej potrzebujesz GPU albo API. Cokolwiek wybierzesz, dodaj prefiks zapytania, którego wymaga model - bez niego PolDense traci prawie 30 punktów.

Który model embeddingów wybrać do polskiego RAG? Przetestowałem 10 modeli i BM25 na 900 prawdziwych pytaniach klientów: wyniki, odporność na brak polskich znaków i literówki, prefiks, który zabiera 30 punktów, ranking PIRB, ceny API, koszt przy dwóch skalach, licencje i kod.

/// WPIS W SKRÓCIE

Embeddingi po polsku - wyniki testu na 900 pytaniach

W tym wpisie pokazuję wyniki mojego testu 10 modeli i BM25 na polskich pytaniach klientów, co się dzieje, gdy ktoś pisze bez polskich znaków albo z literówką, jak jeden zapomniany prefiks psuje wyniki, co mówi oficjalny polski benchmark PIRB, ile kosztują modele przez API i lokalnie przy dwóch skalach, jakie licencje pozwalają na użycie w firmie i jak przetestować modele na własnych danych - z kodem.

Czy w ogóle potrzebujesz modelu embeddingów - pierwsza decyzja

Najprostsza alternatywa to wyszukiwanie po słowach kluczowych (BM25), które masz za darmo w Postgresie czy Elasticsearchu. W moim teście BM25 osiągnęło 47,7 NDCG@10, a najlepszy model embeddingów 82,7. Na pierwszym miejscu właściwy fragment był w 33% przypadków przy BM25 i w 67% przy najlepszym modelu. Powód jest prosty: klient pyta „jak oddać towar”, a w dokumentacji jest „procedura zwrotu”, a polska odmiana sprawia, że „zwrot”, „zwrotu” i „zwrocie” to dla BM25 trzy różne słowa.

SytuacjaCo wystarczy
Wyszukiwanie po numerach, kodach produktów, nazwach własnychBM25 albo filtr w bazie
Pytania w języku naturalnym do dokumentacji, FAQ, procedurModel embeddingów
Oba rodzaje pytań w jednym systemieWyszukiwanie hybrydowe: embeddingi + BM25

Mój test: 10 modeli na 900 pytaniach klientów

Jak testowałem: zbiór Allegro FAQ z konkursu PolEval 2022 - 900 prawdziwych pytań klientów i 921 fragmentów odpowiedzi (średnio 50 słów). Dla każdego pytania znane są poprawne fragmenty. Każdy model zamieniał na wektory wszystkie fragmenty i pytania, a ja sprawdzałem, czy właściwy fragment trafia na górę listy. Wszystko na 4-rdzeniowym procesorze Intel Xeon, bez GPU, z prefiksami zalecanymi przez autorów modeli (październik 2026).

ModelNDCG@10Trafienie na 1. miejscuWłaściwy fragment w top 10ParametryWymiar wektoraFragmenty na sekundę (CPU)Licencja
OPI-PIB/PolDense-400M82,767,4%96,9%395 mln10242,4Gemma
OPI-PIB/PolDense-150M81,365,0%96,5%149 mln7686,7Gemma
intfloat/multilingual-e5-large80,765,9%94,9%560 mln10244,8MIT
BAAI/bge-m379,764,8%94,1%568 mln10244,6MIT
OPI-PIB/PolDense-68M79,162,3%94,8%68 mln51215,6Gemma
sdadas/mmlw-retrieval-roberta-large76,960,9%92,3%435 mln10246,2Apache 2.0
intfloat/multilingual-e5-base76,059,8%91,5%278 mln76816,2MIT
sdadas/mmlw-retrieval-roberta-base74,658,1%90,7%124 mln76818,4Apache 2.0
Qwen/Qwen3-Embedding-0.6B72,656,2%89,2%596 mln10248,2Apache 2.0
intfloat/multilingual-e5-small72,254,8%90,8%118 mln38445,6MIT
BM25 (słowa kluczowe)47,732,6%64,7%----

Co z tego wynika:

  • Polskie modele wygrywają w swojej wadze. PolDense-68M z 68 mln parametrów wypada lepiej niż wielojęzyczne modele ośmiokrotnie większe (mmlw-large, Qwen3-0.6B) i niemal równo z bge-m3.
  • Popularny model to nie znaczy dobry po polsku. Qwen3-Embedding-0.6B, chętnie polecany w zagranicznych rankingach, zajął przedostatnie miejsce wśród modeli - na poziomie najmniejszego e5.
  • Liczba parametrów nie mówi o szybkości. PolDense-68M jest mniejszy od mmlw-base, ale na CPU wolniejszy, bo obsługuje teksty do 8192 tokenów i bez specjalnych bibliotek przyspieszających (dostępnych głównie na GPU) liczy wolniej.
  • Różnica między pierwszym a piątym modelem to 3,6 punktu. Wybór między nimi powinien wynikać z licencji, szybkości i odporności na błędy w pytaniach, a nie z samego rankingu.

Polskie znaki i literówki - test, którego nikt nie robi

Klienci często piszą „jak zwrocic towar” zamiast „jak zwrócić towar”. Sprawdziłem więc, co się dzieje, gdy z tych samych pytań usunę polskie znaki (dotyczyło to 92% pytań) albo wstawię jedną literówkę w dłuższym słowie:

ModelPytania poprawneBez polskich znakówZ literówką
PolDense-400M82,781,4 (-1,3)78,6 (-4,1)
PolDense-150M81,377,8 (-3,5)75,5 (-5,8)
multilingual-e5-large80,776,8 (-3,9)76,0 (-4,7)
bge-m379,778,5 (-1,2)77,2 (-2,5)
PolDense-68M79,176,3 (-2,8)72,8 (-6,3)
mmlw-retrieval-roberta-large76,976,0 (-0,9)70,9 (-6,0)
multilingual-e5-base76,071,4 (-4,6)69,8 (-6,2)
multilingual-e5-small72,266,2 (-6,0)67,4 (-4,8)
BM2547,738,3 (-9,4)41,8 (-5,9)

Wnioski: bge-m3 jest najbardziej odporny - przy pytaniach bez polskich znaków i z literówkami traci najmniej, więc w wyszukiwarce dla klientów indywidualnych bywa pewniejszym wyborem niż model z wyższym wynikiem na czystych danych. Najbardziej cierpi BM25 i najmniejsze modele. Nie usuwaj polskich znaków z dokumentów „na wszelki wypadek” - modele uczono na poprawnym polskim i tylko pogorszysz wyniki.

Prefiks, o którym wszyscy zapominają

Wiele modeli wymaga, żeby przed pytaniem dopisać krótki prefiks: „query: ” w e5, „zapytanie: ” w mmlw, „[query]: ” w PolDense. To nie jest kosmetyka. Policzyłem, co się dzieje, gdy go pominiesz:

ModelZ prefiksemBez prefiksuRóżnica
PolDense-150M81,351,4-29,9
PolDense-68M79,151,0-28,1
PolDense-400M82,756,0-26,7
mmlw-retrieval-roberta-base74,667,7-6,9
mmlw-retrieval-roberta-large76,971,7-5,2
multilingual-e5-large80,778,1-2,6
bge-m379,7nie wymaga-

Bez prefiksu najlepszy model w teście spada niemal do poziomu prostego wyszukiwania po słowach. Taki błąd nie wywala aplikacji ani nie pokazuje komunikatu - wyniki są po prostu gorsze i nikt nie wie dlaczego. Dlatego prefiks trzymaj w jednym miejscu w kodzie razem z nazwą modelu i dodawaj go tylko do pytań, nie do dokumentów (chyba że model wymaga też prefiksu dokumentu, jak e5 z „passage: ”).

embed_polish.py
from sentence_transformers import SentenceTransformerMODEL = "OPI-PIB/PolDense-150M"QUERY_PREFIX = "[query]: "  # zmieniasz model? zmień też prefiksmodel = SentenceTransformer(MODEL, device="cpu")def embed_documents(texts: list[str]):    return model.encode(texts, batch_size=16, normalize_embeddings=True)def embed_query(text: str):    return model.encode(QUERY_PREFIX + text, normalize_embeddings=True)docs = ["Zwrot towaru możesz zgłosić w ciągu 14 dni w zakładce Moje zakupy.",        "Fakturę VAT pobierzesz po zaksięgowaniu płatności."]D = embed_documents(docs)q = embed_query("jak oddac towar ktory mi nie pasuje")print(docs[int((D @ q).argmax())])

Ranking PIRB - szerszy obraz na 41 zadaniach

Mój test to jeden typ danych: krótkie odpowiedzi FAQ. Szerszy obraz daje Polish Information Retrieval Benchmark (PIRB), który utrzymuje Ośrodek Przetwarzania Informacji: 41 zadań, od prawa i medycyny po pytania z portali internetowych. Wybrane wyniki (średnie NDCG@10, stan na 9 października 2026):

ModelPIRB NDCG@10ParametryLicencja
PolDense-1B64,111,0 mldGemma
llama-embed-nemotron-8b63,737,5 mldtylko niekomercyjnie
bge-multilingual-gemma263,269,2 mldGemma
PolDense-400M63,21395 mlnGemma
PolDense-150M61,07149 mlnGemma
Qwen3-Embedding-8B60,937,6 mldApache 2.0
PolDense-68M59,0168 mlnGemma
mmlw-retrieval-roberta-large58,46435 mlnApache 2.0
multilingual-e5-large57,29560 mlnMIT
bge-m355,98568 mlnMIT
multilingual-e5-base53,12278 mlnMIT
Qwen3-Embedding-0.6B50,71596 mlnApache 2.0
BM2541,85--

Kolejność w PIRB jest podobna do mojej: polskie modele PolDense na górze, e5-large przed bge-m3, a Qwen3-0.6B daleko w tyle. Różnica jest w skali wyników - PIRB zawiera dużo trudniejszych zadań (np. prawo), więc średnie są niższe. Ciekawostka z PIRB: wariant hybrydowy bge-m3 (55,75) wypadł minimalnie gorzej niż sam wariant gęsty (55,98), więc „hybryda zawsze pomaga” to mit - trzeba ją sprawdzić na swoich danych.

Modele przez API - OpenAI, Gemini, Voyage

Modele przez API nie są w PIRB, a bez kluczy nie mogłem ich uczciwie przetestować, więc nie podaję dla nich wyników po polsku. Ceny (październik 2026):

ModelCena za mln tokenówUwagi
OpenAI text-embedding-3-small0,02 USD1536 wymiarów, można skracać
OpenAI text-embedding-3-large0,13 USD3072 wymiary, można skracać
Google Gemini Embedding 20,20 USD (0,10 USD w trybie wsadowym)Darmowy w planie bezpłatnym
Voyage voyage-4-lite0,02 USD200 mln tokenów za darmo
Voyage voyage-40,06 USD200 mln tokenów za darmo
Voyage voyage-4-large0,12 USD200 mln tokenów za darmo

API ma sens, gdy nie chcesz utrzymywać serwera, a dane mogą trafić do dostawcy na warunkach biznesowych. Przed wyborem przepuść przez nie ten sam test, który pokazuję niżej - koszt testu na 1 000 fragmentów to ułamki centa.

Ile to kosztuje przy dwóch skalach

Założenie: fragmenty po ok. 300 tokenów, a pytania po ok. 20 tokenów.

SkalaAPI (text-embedding-3-small)API (Gemini Embedding 2)Lokalnie na CPU (PolDense-68M)Pamięć na wektory
50 tys. fragmentów, 30 tys. pytań miesięcznieok. 0,30 USD jednorazowo + grosze miesięcznieok. 3 USD + ok. 0,12 USD miesięcznieok. 1 godzina liczenia na serwerze, który już maszok. 0,1-0,3 GB
10 mln fragmentów, 3 mln pytań miesięcznieok. 60 USD + ok. 1,2 USD miesięcznieok. 300-600 USD + ok. 12 USD miesięcznieok. 7 dni na 4 rdzeniach CPU - potrzebne GPU20-123 GB zależnie od wymiaru

Najważniejszy wniosek: samo liczenie embeddingów przez API jest tanie. Prawdziwe koszty są gdzie indziej:

  • Pamięć bazy wektorowej. 10 mln wektorów o wymiarze 3072 (text-embedding-3-large) to ok. 123 GB, o wymiarze 512 (PolDense-68M) ok. 20 GB. To różnica w rachunku za bazę co miesiąc. Więcej o kosztach baz w porównaniu baz wektorowych.
  • Zmiana modelu. Wektory z różnych modeli są niekompatybilne, więc zmiana modelu oznacza przeliczenie całego indeksu. Przechowuj teksty źródłowe i zaplanuj to z góry - piszę o tym w tekście o vendor lock-in w AI.
  • Prywatność. Model lokalny nie wysyła dokumentów nigdzie, co przy danych osobowych upraszcza zgodność z RODO.

Licencje - co wolno w firmie

LicencjaModeleUżycie komercyjne
MITmultilingual-e5, bge-m3Tak, bez dodatkowych warunków poza zachowaniem informacji o licencji
Apache 2.0mmlw-retrieval, Qwen3-EmbeddingTak, z zachowaniem informacji o licencji i zmianach
Gemma Terms of UsePolDense, bge-multilingual-gemma2Tak, ale obowiązuje polityka zakazanych zastosowań Google, a przy dystrybucji modelu trzeba przekazać warunki dalej
Niekomercyjnallama-embed-nemotron-8bNie

Jeśli dział prawny woli najprostszą licencję, wybierz e5-large albo bge-m3 - tracisz 1-2 punkty względem PolDense-150M, ale masz MIT.

Jak przetestować modele na własnych danych

Ranking to punkt wyjścia, a nie decyzja. Zbierz 50-200 prawdziwych pytań i dla każdego wskaż fragment, który powinien się znaleźć. Potem uruchom dla 2-3 kandydatów taki skrypt:

test_embeddings.py
from sentence_transformers import SentenceTransformerCANDIDATES = {"OPI-PIB/PolDense-150M": "[query]: ", "BAAI/bge-m3": "",              "intfloat/multilingual-e5-large": "query: "}DOC_PREFIX = {"intfloat/multilingual-e5-large": "passage: "}def evaluate(name, qprefix, docs, questions, correct):    m = SentenceTransformer(name, device="cpu")    D = m.encode([DOC_PREFIX.get(name, "") + d for d in docs], normalize_embeddings=True)    Q = m.encode([qprefix + q for q in questions], normalize_embeddings=True)    top10 = (Q @ D.T).argsort(axis=1)[:, ::-1][:, :10]    hit1 = sum(top[0] == c for top, c in zip(top10, correct)) / len(correct)    hit10 = sum(c in top for top, c in zip(top10, correct)) / len(correct)    return round(100 * hit1, 1), round(100 * hit10, 1)# docs: lista fragmentów, questions: lista pytań, correct: indeks właściwego fragmentu dla każdego pytania# for name, prefix in CANDIDATES.items(): print(name, evaluate(name, prefix, docs, questions, correct))

Dodaj do zestawu pytania bez polskich znaków i z literówkami - tak, jak piszą Twoi użytkownicy. Wynik „właściwy fragment w top 10” mówi, czy RAG ma szansę odpowiedzieć dobrze, bo model językowy dostaje zwykle kilka najlepszych fragmentów, a nie tylko pierwszy.

Najczęstsze błędy

  • Brak prefiksu zapytania - w moim teście do 30 punktów straty, bez żadnego komunikatu o błędzie.
  • Wybór modelu z angielskiego rankingu. MTEB i rankingi zagraniczne nie mówią, jak model radzi sobie po polsku.
  • Usuwanie polskich znaków z dokumentów. Modele uczono na poprawnym tekście, więc tylko pogarszasz wyniki.
  • Mieszanie wektorów z dwóch modeli w jednym indeksie. Po zmianie modelu przelicz wszystko od nowa.
  • Za długie fragmenty dla modeli z limitem 512 tokenów. e5 i mmlw obcinają dłuższy tekst bez ostrzeżenia, więc koniec dokumentu w ogóle nie trafia do wektora. Dziel teksty na krótsze fragmenty albo wybierz model z dłuższym limitem (bge-m3, PolDense - do 8192 tokenów).

---

Pomagam firmom budować wyszukiwanie i RAG na polskich dokumentach - od wyboru modelu embeddingów i testu na własnych danych po bazę wektorową i wdrożenie. Robię to w ramach budowy aplikacji AI i automatyzacji z AI. Napisz do mnie - zacznę od testu 2-3 modeli na próbce Twoich pytań.

Warto przeczytać:

/// AUTHOR
Paweł Wiszniewski – AI & Web Engineer

Paweł Wiszniewski

SEO & GEO Specialist & AI Engineer

Specjalista SEO/GEO (10 lat) i AI engineer (3 lata). Buduję widoczność w wyszukiwarkach, systemy AI i automatyzacje, które redukują koszty i zwiększają efektywność operacyjną firm.

Signal received?

Przerwij
Ciszę

Zainicjuj protokół. Nawiąż połączenie. Zbudujmy coś głośnego.

> OCZEKIWANIE_NA_SYGNAŁ...