Vector DB и RAG-инфраструктура
В RAG-приложении векторная БД — отдельный single-point-of-failure, который почти никто не мониторит: поиск может вернуть пустой массив, и пользователь просто получит «модель сегодня туповатая», без видимой ошибки.
Notifly закрывает это тремя слоями:
1. Активный TCP/HTTP-мониторинг векторной БД
Заголовок раздела «1. Активный TCP/HTTP-мониторинг векторной БД»Большинство популярных 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 в мониторах.
2. End-to-end проверка реального запроса
Заголовок раздела «2. End-to-end проверка реального запроса»/health зелёный, а коллекция повреждена / квота на запросы выбрана?
Маленькая cloud-функция с timer-trigger, которая делает
реальный поиск и сравнивает количество результатов:
import os, requests, timefrom 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).
3. Heartbeat от индексационного пайплайна
Заголовок раздела «3. Heartbeat от индексационного пайплайна»Индексация (краулинг → embeddings → upsert) обычно идёт по cron. Если она упала ночью — днём качество поиска уже плохое, но никто этого не заметит.
Создайте heartbeat с intervalSec чуть больше периода
вашего cron-job. Пингуйте его в самом конце успешного прогона:
*/30 * * * * /usr/local/bin/reindex.sh && curl -fsS "$REINDEX_PING_URL" -o /dev/nullЛюбой пропуск — и в течение intervalSec + graceSec приходит push
«индексация не доехала».
4. Алёрт на drift количества чанков
Заголовок раздела «4. Алёрт на drift количества чанков»Полезный сигнал: коллекция «съёжилась» в 10 раз — почти наверняка кто-то переиндексировал в неправильную коллекцию. Раз в час сверяем:
prev = int(open("/tmp/qdrant-count.txt").read() or 0) if os.path.exists("/tmp/qdrant-count.txt") else 0cur = 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.).