feat(embeddings): добавить облачный бэкенд (DeepInfra, та же bge-m3)
All checks were successful
Deploy / test (push) Successful in 4m29s
Deploy / deploy (push) Successful in 59s

EMBED_BACKEND=cloud — эмбеддинги через DeepInfra (OpenAI-совместимый API),
модель та же BAAI/bge-m3, что уже льётся на VM109 — переключение не рвёт
совместимость с уже пересчитанным FAISS-индексом (тот же вектор 1024).
Не активно по умолчанию (EMBED_BACKEND всё ещё "ollama").
This commit is contained in:
jze9
2026-08-26 17:18:11 +05:00
parent 96a4530a93
commit c156fb2b78
2 changed files with 35 additions and 2 deletions

View File

@@ -3,6 +3,7 @@
Бэкенд переключается через EMBED_BACKEND:
- "ollama" — HTTP-вызов к Ollama /api/embed (GGUF-модель через Vulkan, работает на AMD GPU)
- "sentence_transformers" — локальная загрузка через sentence-transformers (CUDA/CPU)
- "cloud" — облачный инференс той же модели (DeepInfra, OpenAI-совместимый формат)
Модель (в случае sentence_transformers) загружается один раз при первом обращении
и кэшируется в памяти GPU.
@@ -67,6 +68,8 @@ class ModelManager:
if settings.EMBED_BACKEND == "ollama":
return cls._encode_ollama(texts)
if settings.EMBED_BACKEND == "cloud":
return cls._encode_cloud(texts)
model = cls.get_model()
vectors = model.encode(
@@ -98,6 +101,30 @@ class ModelManager:
norms[norms == 0] = 1.0
return vectors / norms # Нормализация для cosine через inner product
@classmethod
def _encode_cloud(cls, texts: list[str]) -> np.ndarray:
"""Закодировать тексты через облачный инференс (DeepInfra, OpenAI-формат)."""
all_vectors: list[list[float]] = []
batch_size = settings.EMBED_BATCH_SIZE
for i in range(0, len(texts), batch_size):
batch = texts[i : i + batch_size]
response = httpx.post(
settings.CLOUD_EMBED_URL,
json={"model": settings.CLOUD_EMBED_MODEL, "input": batch},
headers={"Authorization": f"Bearer {settings.CLOUD_EMBED_API_KEY}"},
timeout=120.0,
)
response.raise_for_status()
data = response.json()["data"]
# OpenAI-формат может вернуть элементы не по порядку — сортируем по index
data.sort(key=lambda d: d["index"])
all_vectors.extend(d["embedding"] for d in data)
vectors = np.array(all_vectors, dtype=np.float32)
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
norms[norms == 0] = 1.0
return vectors / norms # Нормализация для cosine через inner product
@classmethod
def encode_single(cls, text: str) -> np.ndarray:
"""Закодировать один текст. Удобный метод."""