Перейти к содержимому

Vector DB и RAG-инфраструктура

В RAG-приложении векторная БД — отдельный single-point-of-failure, который почти никто не мониторит: поиск может вернуть пустой массив, и пользователь просто получит «модель сегодня туповатая», без видимой ошибки.

Notifly закрывает это тремя слоями:

Большинство популярных vector-DB отдают /health или хотя бы открытый порт. Заведите активный монитор:

// Qdrant Cloud
{
"kind": "http",
"target": "https://your-cluster.qdrant.tech:6333/healthz",
"intervalSec": 60,
"alertMessage": "Qdrant не отвечает — RAG будет возвращать пустые ответы"
}
// Self-hosted Weaviate
{
"kind": "http",
"target": "https://weaviate.example.com/v1/.well-known/ready",
"intervalSec": 60,
"consecutiveFails": 3
}
// Pinecone (status API)
{
"kind": "http",
"target": "https://status.pinecone.io/api/v2/status.json",
"intervalSec": 120
}
// Self-hosted с Postgres+pgvector
{
"kind": "postgres",
"target": "vec.example.com:5432",
"intervalSec": 60
}

См. полный список поддерживаемых kind в мониторах.

/health зелёный, а коллекция повреждена / квота на запросы выбрана? Маленькая cloud-функция с timer-trigger, которая делает реальный поиск и сравнивает количество результатов:

import os, requests, time
from qdrant_client import QdrantClient
q = QdrantClient(url=os.environ["QDRANT_URL"], api_key=os.environ["QDRANT_KEY"])
CANARY_VEC = [0.0] * 1536 # любой стабильный вектор
EXPECTED_MIN = 5 # мы знаем, что в коллекции > 5 чанков
def handler(event, context):
t0 = time.time()
try:
hits = q.search(collection_name="docs", query_vector=CANARY_VEC, limit=10)
except Exception as e:
notify("❌ Qdrant search", f"{type(e).__name__}: {e}", 9)
return {"statusCode": 200}
ms = int((time.time() - t0) * 1000)
if len(hits) < EXPECTED_MIN:
notify("⚠️ Qdrant пустые результаты",
f"Получено {len(hits)} (ожидалось ≥ {EXPECTED_MIN}) за {ms} мс",
priority=8)
elif ms > 1500:
notify("⏱️ Qdrant медленно",
f"Поиск занял {ms} мс. Проверьте размер индекса и шарды.",
priority=6)
return {"statusCode": 200}
def notify(t, m, prio):
requests.post(f"{os.environ['NOTIFLY_URL']}/message",
params={"token": os.environ["NOTIFLY_TOKEN"]},
json={"title": t, "message": m, "priority": prio},
timeout=5)

Тот же подход для Weaviate (client.query.get(...)), Pinecone (index.query(...)) и pgvector (SELECT ... <-> $1 LIMIT 10).

Индексация (краулинг → embeddings → upsert) обычно идёт по cron. Если она упала ночью — днём качество поиска уже плохое, но никто этого не заметит.

Создайте heartbeat с intervalSec чуть больше периода вашего cron-job. Пингуйте его в самом конце успешного прогона:

Окно терминала
*/30 * * * * /usr/local/bin/reindex.sh && curl -fsS "$REINDEX_PING_URL" -o /dev/null

Любой пропуск — и в течение intervalSec + graceSec приходит push «индексация не доехала».

Полезный сигнал: коллекция «съёжилась» в 10 раз — почти наверняка кто-то переиндексировал в неправильную коллекцию. Раз в час сверяем:

prev = int(open("/tmp/qdrant-count.txt").read() or 0) if os.path.exists("/tmp/qdrant-count.txt") else 0
cur = q.get_collection("docs").points_count
if prev and abs(cur - prev) / max(prev, 1) > 0.5:
notify("⚠️ Qdrant: drift",
f"Размер коллекции: {prev}{cur}", priority=8)
open("/tmp/qdrant-count.txt", "w").write(str(cur))
  • название коллекции / индекса;
  • последняя успешная индексация (timestamp);
  • размер до/после, latency, error class;
  • ссылка на dashboard вашего vector-DB (Qdrant Web UI, Pinecone Console, etc.).