Если вы собираете свой RAG и ищете открытую embedding-модель, которая одинаково хорошо работает с русским и английским и тянет длинные документы — с большой вероятностью вам нужна именно BAAI/bge-m3. Это дефолтный выбор для self-hosted сценариев, и здесь собрано всё, что стоит знать, прежде чем ставить её на свой сервер.

Что это

BGE-M3 (BAAI General Embedding — M3) — embedding-модель от Beijing Academy of Artificial Intelligence, опубликованная в начале 2024 года.

M3 в названии — это три «мульти», которыми она отличается от предыдущих поколений:

  • Multi-Linguality — больше 100 языков, включая русский. Главное — сильный cross-lingual: запрос на русском находит релевантный фрагмент на английском и наоборот.
  • Multi-Functionality — dense, sparse и multi-vector retrieval внутри одной модели. Не нужно держать несколько эмбеддеров.
  • Multi-Granularity — контекст до 8192 токенов. Можно эмбеддить не «кусочки», а целые статьи.

Под капотом — XLM-RoBERTa-large, дообученная на больших объёмах парных и триплетных данных. На момент релиза модель была одной из лучших открытых на бенчмарке MIRACL (мультиязычный retrieval), и с тех пор её так и не скинули с пьедестала для двуязычных задач.

**Термин: **embedding — числовой вектор, в который модель превращает текст. У близких по смыслу текстов векторы тоже близкие. Благодаря этому можно искать по смыслу, а не по ключевым словам.

Зачем нужно

  • Поднять поиск по своей базе знаний без зависимости от облачных провайдеров — предсказуемая стоимость и контроль над данными.
  • Сделать RAG на двуязычных (RU/EN) документах так, чтобы запрос на любом языке находил релевантный фрагмент независимо от языка источника.
  • Эмбеддить длинные чанки и целые статьи без жёсткой нарезки по 512 токенов — контекст до 8192 токенов покрывает большинство реальных кейсов.
  • Собрать гибридный поиск dense + sparse + multi-vector из одной модели — не нужно подбирать отдельный BM25 и отдельный dense-эмбеддер.
  • Использовать в коммерческих проектах без юридических рисков — MIT-лицензия снимает вопросы, в отличие от jina-v3 с её CC-BY-NC.

Как устроено

Ключевые технические характеристики модели:

ПараметрЗначение
РазработчикBAAI (Beijing Academy of Artificial Intelligence)
ЛицензияMIT — коммерческое использование без ограничений
АрхитектураXLM-RoBERTa-large, дообученная
Размер весов~2.27 ГБ
RAM при инференсе~0.9–1.2 ГБ
Размерность dense-вектора1024
Максимальный контекст8192 токенов
Языки100+, включая русский, английский, китайский
Типы retrievalDense, Sparse (lexical), Multi-vector (ColBERT-style)
Скорость на CPU (Ryzen 5 3600)~120 мс на чанк

Главная фишка M3 — одна и та же модель отдаёт три разных типа представлений. Можно брать по одному, можно комбинировать в гибридный поиск.

РежимЧто выдаётКогда использовать
DenseОдин вектор 1024 на текстКлассический семантический поиск, обычный RAG
Sparse / LexicalВеса токенов (как BM25, но обученные)Точные совпадения терминов, имён, кодов
Multi-vector (ColBERT)Отдельный вектор на каждый токенРе-ранжирование top-N для максимальной точности

**Компромисс: **sparse и multi-vector дают качество, но жрут память в индексе. На старте берите dense. Остальные режимы подключайте, только когда метрики реально проседают — а не «на всякий случай».

Когда использовать

СитуацияПодходитПочему
Двуязычная (RU/EN) база знанийДаСильный cross-lingual retrieval, запрос на любом языке
Длинные документы, чанки 1000+ токеновДаКонтекст 8192 токенов, не нужно мельчить
Self-hosted RAG без внешнего APIДаMIT-лицензия, работает на CPU
Гибридный поиск dense + sparseДаОба режима в одной модели
Коммерческий продукт с юридическими требованиямиДаMIT — без ограничений на коммерцию
Чисто английский корпус с максимальным качествомСкорее нетtext-embedding-3-large / Voyage-3 часто выигрывают
Минимальная задержка на коротких запросах без GPUСкорее нетbge-small-en / gte-small дают 10–30 мс против 120 мс
Критична экономия на векторной БДСкорее нет1024 измерения против 384 у малых — в 2.6 раза больше места
Эмбеддинги для кодаНетНужны специализированные модели (jina-embeddings-v2-code, voyage-code-2)

Пример

Минимальные требования

  • CPU: любой современный x86_64. В примерах ниже — Ryzen 5 3600.
  • RAM: от 4 ГБ свободной. Сама модель ~1 ГБ, остальное уходит на батчи и overhead.
  • Диск: ~3 ГБ под веса и зависимости.
  • Python 3.10+.
  • GPU не обязателен. Но если он есть — инференс ускоряется в 10–30 раз.

Ставим зависимости

python3 -m venv .venv
source .venv/bin/activate
pip install -U FlagEmbedding fastapi uvicorn pydantic

FlagEmbedding — официальная библиотека от BAAI. В ней уже собраны оптимальные пресеты для всех трёх режимов, не нужно ничего угадывать.

Эталонный FastAPI-сервер (OpenAI-compatible)

Рабочий пример: кладёте в один файл server.py, запускаете, получаете embedding-сервис с API в формате OpenAI /v1/embeddings. Это значит, что вы можете вставить его как drop-in replacement в LiteLLM, LangChain, LlamaIndex — ничего не переписывая.

# server.py — OpenAI-compatible embeddings server на BGE-M3
from fastapi import FastAPI
from pydantic import BaseModel
from FlagEmbedding import BGEM3FlagModel
from typing import List, Union
import time, uuid

# use_fp16=False на CPU; на GPU ставьте True для 2x ускорения
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=False)

app = FastAPI()

class EmbeddingRequest(BaseModel):
    input: Union[str, List[str]]
    model: str = "bge-m3"

@app.post("/v1/embeddings")
def embeddings(req: EmbeddingRequest):
    texts = [req.input] if isinstance(req.input, str) else req.input
    # dense-вектор — основной режим для RAG
    vectors = model.encode(texts, batch_size=8, max_length=8192)['dense_vecs']
    return {
        "object": "list",
        "model": req.model,
        "data": [
            {"object": "embedding", "index": i, "embedding": v.tolist()}
            for i, v in enumerate(vectors)
        ],
        "usage": {"prompt_tokens": 0, "total_tokens": 0},
    }

@app.get("/health")
def health():
    return {"status": "ok", "model": "BAAI/bge-m3"}

Запуск:

uvicorn server:app --host 127.0.0.1 --port 8080

Systemd-юнит для автозапуска

# /etc/systemd/system/bge-m3.service
[Unit]
Description=BGE-M3 Embeddings Server
After=network.target

[Service]
Type=simple
User=embeddings
WorkingDirectory=/opt/bge-m3
Environment="PATH=/opt/bge-m3/.venv/bin"
ExecStart=/opt/bge-m3/.venv/bin/uvicorn server:app --host 127.0.0.1 --port 8080
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Активация:

sudo systemctl daemon-reload
sudo systemctl enable --now bge-m3
sudo systemctl status bge-m3

**Совет: **биндите сервис на 127.0.0.1 или на интерфейс Tailscale, а не на 0.0.0.0. Авторизации у модели из коробки нет. Открытый порт в интернет — приглашение чужим ботам молотить ваш CPU за ваш же счёт.

Проверяем, что всё живо

curl -X POST http://127.0.0.1:8080/v1/embeddings \
  -H "Content-Type: application/json" \
  -d '{"input": ["Привет, мир", "Hello, world"], "model": "bge-m3"}' | jq '.data | length'
# ожидаемо: 2

Чтобы проверить cross-lingual качество на глаз, посчитайте cosine similarity между русской и английской версией одной и той же фразы. Для нормальных переводов должно быть 0.85+. Если получаете 0.70 или ниже — где-то накосячили: не тот pooling, обрезан контекст, неправильно собран батч.

Практические сценарии

RAG по двуязычной базе знаний

  • Индексируете документы на русском и английском одной моделью.
  • Запрос может быть на любом языке — релевантный фрагмент найдётся независимо от языка источника.
  • Работает из коробки, без отдельного переводчика в пайплайне.

Гибридный поиск dense + sparse

  • Dense ловит синонимы и переформулировки.
  • Sparse ловит точные термины, имена, артикулы, номера версий.
  • Финальный скор — взвешенная сумма, обычно 0.7 × dense + 0.3 × sparse. Веса стоит подкрутить под свой корпус.

Семантический дедуп

  • Эмбеддите входящие сообщения, тикеты или карточки.
  • Порог cosine > 0.92 — почти наверняка дубль.
  • 0.80–0.92 — похожая тема, кандидат на объединение, но лучше глазами.

Кластеризация идей и заметок

  • Эмбеддите все заметки, прогоняете через UMAP + HDBSCAN.
  • Получаете тематические группы без заранее заданных категорий. Удобно для «а что у меня вообще в голове творится».

Ограничения

Ограничения

Что учитывать

Короткое вводное предложение про специфику BGE-M3.

use_fp16=True на CPU — замедляет инференс в 2–3 раза, а не ускоряет.

Флаг имеет смысл только на GPU с tensor cores.

Cold start — первый запрос после старта 30+ секунд:

загрузка весов и прогрев. Решается фиктивным запросом при старте сервиса.

Размер вектора — 1024 измерения, в 2.6 раза больше места в индексе, чем у 384-мерных моделей.

Учитывайте в расчётах стоимости хранения.

Скорость на CPU — ~120 мс на чанк.

Для высокой нагрузки нужен GPU или маленькая модель.

OOM на больших батчах

снижайте batch_size до 4–8 на CPU и 16–32 на GPU.

Ответ сервера LiteLLM — должен строго соответствовать OpenAI-схеме {object, model, data:

[{embedding, index}], usage}, иначе 422.

Безопасность

авторизации у модели из коробки нет, биндите на 127.0.0.1 или Tailscale, не на 0.0.0.0.

Антипаттерны

Антипаттерны

Чего не делать

Короткое вводное предложение.

Не делать:

эмбеддить весь документ целиком. Даже при контексте 8192 качество retrieval проседает. Режьте на чанки по 500–1500 токенов с overlap.

Не делать:

выставлять сервис в публичный интернет без auth. Бесплатный compute для чужих ботов за ваше электричество — сомнительное удовольствие.

Не делать:

мешать эмбеддинги от разных моделей в одном индексе. Векторы несопоставимы. Поиск сломается тихо и незаметно — это худший вариант.

Не делать:

считать cosine similarity до нормализации. FlagEmbedding возвращает уже нормализованные векторы, но если собираете руками — нормализуйте, иначе скор будет искажён.

Не делать:

игнорировать prompt-инструкции для запросов. Для retrieval-режима BGE рекомендует префикс к query — посмотрите карточку модели на HuggingFace.

Не делать:

включать use_fp16=True на CPU. Замедляет в 2–3 раза, не ускоряет. Этот флаг только для GPU с tensor cores.

Чеклист

Чеклист

Проверка перед запуском

Короткое вводное предложение.

Железо

от 4 ГБ RAM, ~3 ГБ диска под веса и зависимости, Python 3.10+.

Зависимости

FlagEmbedding + FastAPI + uvicorn + pydantic установлены в виртуальное окружение.

Сервер — отдаёт ровно OpenAI-формат:

{object, model, data: [{embedding, index}], usage}, иначе LiteLLM вернёт 422.

Авторизация

сервис слушает только 127.0.0.1 или Tailscale-интерфейс, не 0.0.0.0.

fp16-флаг

use_fp16=False на CPU, use_fp16=True только на GPU с tensor cores.

Контекст

явно указан max_length=8192, иначе обрезка по 512 и просадка cross-lingual.

Batch — размер подобран под RAM:

4–8 на CPU, 16–32 на GPU.

Systemd

юнит создан, Restart=on-failure, прогрев фиктивным запросом при старте.

Cross-lingual smoke-test — cosine между RU/EN версиями одной фразы ≥ 0.85.

Если ниже 0.75 — где-то не тот pooling или обрезан контекст.

Ссылки

Ссылки