Если вы собираете свой 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+, включая русский, английский, китайский |
| Типы retrieval | Dense, 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 или обрезан контекст.
Ссылки
Ссылки
- Модель на HuggingFace: BAAI/bge-m3
- Репозиторий FlagEmbedding: github.com/FlagOpen/FlagEmbedding
- Статья на arXiv (2402.03216): arxiv.org/abs/2402.03216
- Бенчмарк MIRACL: github.com/project-miracl/miracl
- Лидерборд MTEB: huggingface.co/spaces/mteb/leaderboard