Skip to main content
Glama
README.md
# selti — Semantic Memory MCP Server

**selti** — высокопроизводительный MCP-сервер семантической памяти для AI-агентов. Обеспечивает векторное хранение, поиск по семантической близости и интеллектуальную дедупликацию записей на основе протокола MCP (Model Context Protocol) через SSE-транспорт.

---

> **Примечание:** Данный проект разработан с применением технологий искусственного интеллекта в рамках рабочего процесса Argenta Team. Код прошел рецензирование, тестирование и подготовлен к эксплуатации в production-среде.

---

## Стек технологий

| Компонент             | Технология                        |
|-----------------------|-----------------------------------|
| Язык                  | Python 3.12                       |
| Фреймворк             | FastAPI + FastMCP                 |
| База данных           | PostgreSQL 17 + pgvector (HNSW)   |
| Кеш                   | Redis 7                           |
| Мониторинг            | Prometheus + Grafana              |
| Контейнеризация       | Docker + Docker Compose           |

---

## Архитектура

Система построена по многослойной архитектуре с чётким разделением ответственности:

```
Client (MCP over SSE)
       |
       v
   FastAPI / FastMCP ─── Auth Middleware (опционально)
       |
       v
   ┌─────────────────────────────────────────┐
   │            MCP Tools (16)               │
   │  store / search / get / update / delete │
   │  list / forget / ingest / stats / find  │
   │  recent / archive / link / unlink       │
   │  get_relations / traverse / graph_stats │
   └──────────────────────┬──────────────────┘
                          │
                          v
   ┌─────────────────────────────────────────┐
   │           MemoryService                 │
   │        (бизнес-логика)                  │
   └──────┬──────────────────────┬───────────┘
          │                      │
          v                      v
   ┌───────────┐        ┌───────────────┐
   │ DedupEngine│◄──────►│ Embedding API │
   │exact+sem. │        │  + Redis Cache│
   └─────┬─────┘        └───────┬───────┘
         │                      │
         v                      v
   ┌─────────────────────────────────────────┐
   │          MemoryRepository               │
   │       (SQL via asyncpg)                 │
   └──────────────────┬──────────────────────┘
                      │
                      v
   ┌─────────────────────────────────────────┐
   │    PostgreSQL 17 + pgvector (HNSW)      │
   │        4096-мерные эмбеддинги           │
   │        + Relations table (граф)         │
   └─────────────────────────────────────────┘
```

**Слои архитектуры:**

- **MCP Tools** — 16 инструментов, декорированных FastMCP. Валидация namespace, трекинг метрик, обработка ошибок. Включают инструменты для графа знаний (link, unlink, get_relations, traverse, graph_stats).
- **DedupEngine** — двухуровневая дедупликация: точная (SHA256) и семантическая (cosine distance). Оптимизирован: кеширует эмбеддинги в DedupDecision.
- **MemoryService** — координатор бизнес-логики: вызов эмбеддингов, дедупликация, взаимодействие с репозиторием. Поддерживает batch-операции и Relations API.
- **Repository** — уровень доступа к данным на asyncpg; сырые SQL-запросы с параметризацией. Включает методы для графа (add_relation, traverse, get_graph_stats) и archive.
- **PostgreSQL / pgvector** — HNSW-индекс для 4096-мерных векторов, B-tree индексы для фильтрации, JSONB для метаданных. Таблица `relations` для графа знаний.
- **Redis Cache** — кеш эмбеддингов (SHA256-ключи, TTL 24 часа); снижает нагрузку на Embedding API.

---

## MCP Tools

Сервер предоставляет 16 инструментов для управления семантической памятью:

### Основные инструменты

| Tool                  | Описание                                                   | Параметры                                               |
|-----------------------|------------------------------------------------------------|---------------------------------------------------------|
| `memory_store`        | Сохранить запись с дедупликацией                           | content, user_id, metadata?, namespace?                 |
| `memory_search`       | Векторный поиск по семантической близости                  | query, user_id, limit?, threshold?, namespace?          |
| `memory_get`          | Получить запись по идентификатору                          | id                                                      |
| `memory_update`       | Обновить содержимое и/или метаданные записи                | id, content?, metadata?                                 |
| `memory_delete`       | Удалить запись по идентификатору                           | id                                                      |
| `memory_list`         | Список записей с фильтрацией и пагинацией                  | user_id?, namespace?, limit?, offset?                   |
| `memory_forget`       | Массовое удаление всех записей пользователя                | user_id, namespace?                                     |
| `memory_ingest_batch` | Массовое сохранение набора записей с дедупликацией         | entries: list[{content, metadata?, namespace?}], user_id |
| `memory_stats`        | Статистика по неймспейсам: количество записей, дата обновления | user_id                                              |
| `memory_find_similar` | Поиск семантически похожих записей без сохранения          | content, user_id, limit?, threshold?, namespace?        |
| `memory_recent`       | Последние записи по времени создания                       | namespace?, limit?, since?                              |
| `memory_archive`      | Мягкое удаление: установить is_archived = true             | id                                                      |

### Инструменты для графа знаний

| Tool                  | Описание                                                   | Параметры                                               |
|-----------------------|------------------------------------------------------------|---------------------------------------------------------|
| `memory_link`         | Добавить связь между гранулами                             | source_id, target_id, link_type?, description?, weight? |
| `memory_unlink`       | Удалить связь между гранулами                              | source_id, target_id, link_type                         |
| `memory_get_relations`| Получить связи для гранулы                                 | source_id, target_id?                                   |
| `memory_traverse`     | Обход графа от стартовой вершины                           | start_id, depth?, link_types?                           |
| `memory_graph_stats`  | Статистика графа: количество связей, сирот, средние связи  |                                                         |

Каждый инструмент инструментирован метриками: количество вызовов, длительность выполнения, статус (ok/error).

---

## Namespace-стратегия

Namespace обеспечивают логическую изоляцию данных в рамках одной базы. Передаются опционально (по умолчанию — `default`). Валидация на уровне tools; неверное значение вызывает `ValueError`.

| Namespace            | Назначение                         |
|----------------------|------------------------------------|
| `default`            | Общие записи                       |
| `user_facts`         | Факты о пользователе               |
| `code_knowledge`     | Знания из кодовой базы             |
| `dialogue_insights`  | Инсайты из диалогов                |
| `project_meta`       | Метаданные проектов                |

---

## Дедупликация

Ядро системы — `DedupEngine`, реализующий двухуровневую стратегию предотвращения дубликатов.

### Уровень 1: Exact Match

Вычисляется SHA256(content). Выполняется поиск по `content_hash` в пределах namespace:

- **user_facts** → `UPDATE` (перезапись существующей записи)
- **остальные** → `SKIP` (пропуск, возврат существующей записи)

### Уровень 2: Semantic Match

Если точное совпадение не найдено, генерируется эмбеддинг и выполняется векторный поиск. При `score >= threshold` запись считается дубликатом → `SKIP`.

### Пороги семантической дедупликации (per-namespace)

| Namespace            | Порог  |
|----------------------|--------|
| `default`            | 0.95   |
| `user_facts`         | 0.90   |
| `code_knowledge`     | 0.95   |
| `dialogue_insights`  | 0.85   |
| `project_meta`       | 0.90   |

Пороги настраиваются через переменную `DEDUP_THRESHOLDS`. Полное отключение — `DEDUP_ENABLED=false`.

---

## Быстрый старт

### Предварительные требования

- Docker 24+ и Docker Compose v2
- Python 3.12 (для миграций вне контейнера)

### Запуск

```bash
# 1. Клонировать репозиторий
git clone <repository-url>
cd selti

# 2. Скопировать шаблон окружения
cp .env.example .env

# 3. Сгенерировать пароли
python3 -c "import secrets; print(secrets.token_urlsafe(32))"

# 4. Заполнить .env:
#    PG_PASSWORD     — пароль суперпользователя PostgreSQL
#    APP_PASSWORD    — пароль пользователя приложения athena_app
#    REDIS_PASSWORD  — пароль Redis

# 5. Запустить с локальными PostgreSQL и Redis
docker compose --profile local-db up -d

# 6. Проверить здоровье сервера
curl http://localhost:8000/health

# 7. Применить миграции базы данных
python migrations/run.py
```

Для подключения к внешним PostgreSQL и Redis — запустите без профиля `--profile local-db` и укажите соответствующие URL в `.env`.

---

## Celery Setup

Система использует **Celery** для асинхронной обработки тяжёлых операций: векторного поиска, batch-вставок, дедупликации и работы с эмбеддингами. MCP-сервер только ставит задачи в очередь, а воркеры выполняют всю работу.

### Архитектура

```
MCP Server (FastMCP) ──send_task()──▶ Redis (broker) ──▶ Celery Worker (prefork)
                                                                    │
                                    ┌───────────────────────────────┤
                                    │               │               │
                                    ▼               ▼               ▼
                              PostgreSQL        Qdrant        Embedding API
                              (pgvector)       (векторы)      (эмбеддинги)
```

**Почему Celery, а не asyncio в MCP-сервере:** вся логика selti — async (asyncpg, httpx), но тяжёлые операции (embedding, векторный поиск) блокируют event loop. Celery prefork воркеры изолируют эти операции в отдельных процессах, не блокируя обработку MCP-запросов.

### Очереди

| Очередь    | Назначение                              | Таймаут (soft/hard) |
|------------|-----------------------------------------|---------------------|
| `memory`   | store, search, get, update, delete, archive, link, unlink | 240s / 300s |
| `batch`    | ingest_batch                            | 600s / 900s         |
| `hash`     | hash_upsert, hash_get, hash_list, hash_delete | 120s / 180s |

### Запуск

```bash
# Development — всё вместе
docker compose up -d

# С Flower (мониторинг воркеров)
docker compose --profile dev up -d

# Только воркер (вручную, для отладки)
celery -A memory_server.celery_app worker \
    -Q memory,batch,hash \
    -c 4 \
    --without-gossip \
    --without-mingle \
    --without-heartbeat \
    -l INFO

# Только Flower
celery -A memory_server.celery_app flower --port=5555
```

### Production-опции воркера

```bash
celery -A memory_server.celery_app worker \
    -Q memory,batch,hash \
    -c 4 \
    --without-gossip \
    --without-mingle \
    --without-heartbeat \
    --max-tasks-per-child=1000 \
    --max-memory-per-child=200000 \
    --logfile=- \
    -l INFO
```

| Флаг                              | Зачем                                      |
|-----------------------------------|--------------------------------------------|
| `--without-gossip`                | Экономит ~10% CPU, ускоряет старт          |
| `--without-mingle`                | Экономит ~10% CPU, не нужен в single-worker |
| `--without-heartbeat`             | Экономит ~10% network                      |
| `--max-tasks-per-child=1000`      | Реклайм памяти, защита от утечек           |
| `--max-memory-per-child=200000`   | OOM-защита (200 MB на процесс)             |

### Retry-политика

Задачи автоматически повторяются при ошибках сети или БД:

- **Максимум попыток:** 5
- **Стратегия:** exponential backoff + jitter
- **Базовая задержка:** 30 секунд

Validation-ошибки (ValueError, InvalidNamespace) **не ретраятся** — это баги данных, а не транзиентные сбои.

### Мониторинг

| Сервис    | URL                          | Назначение                        |
|-----------|------------------------------|-----------------------------------|
| Health    | `GET /health`                | Статус сервера + Celery worker    |
| Metrics   | `GET /metrics`               | Prometheus: 8 Celery + Redis + Qdrant метрик |
| Tasks API | `GET /tasks`                 | Активные задачи                   |
| Task info | `GET /tasks/{task_id}`       | Статус конкретной задачи          |
| Cancel    | `POST /tasks/{task_id}/cancel` | Отмена задачи (revoke)          |
| Flower    | `http://localhost:5555`      | Веб-интерфейс мониторинга (dev)   |

### Troubleshooting

| Проблема | Причина | Решение |
|----------|---------|---------|
| `ConnectionRefused` к Redis | Redis не запущен | `docker compose up -d redis` |
| `Task is stuck` / `Acknowledgement timed out` | Воркер упал во время задачи | Проверьте логи: `docker compose logs celery-worker` |
| `Worker terminated` (SIGKILL) | OOM — процесс съел >200 MB | Увеличьте `CELERY_WORKER_MAX_MEMORY_PER_CHILD` или добавьте RAM |
| `SoftTimeLimitExceeded` | Задача выполняется дольше лимита | Увеличьте таймаут или оптимизируйте запрос |
| `Retry and give up` (5 попыток) | Транзиентная ошибка не прошла | Проверьте доступность PostgreSQL / Embedding API |
| Flower не видит воркеры | `--without-gossip` блокирует discovery | В dev-режиме уберите флаг; в production используйте `celery inspect ping` |
| `Unknown task` при `inspect registered` | Воркер не подключился к broker | Проверьте `CELERY_BROKER_URL` и доступность Redis |

---

## Конфигурация

### Переменные окружения

| Переменная               | Описание                                   | По умолчанию                                              |
|--------------------------|--------------------------------------------|-----------------------------------------------------------|
| `DATABASE_URL`           | PostgreSQL connection string (asyncpg)     | `postgresql+asyncpg://athena:athena@localhost:5432/selti` |
| `REDIS_URL`              | Redis connection string                    | `redis://:@redis:6379/0`                                 |
| `EMBEDDING_API_URL`      | URL API эмбеддингов (OpenAI-совместимый)   | `http://10.0.0.21:8080/v1`                               |
| `EMBEDDING_API_KEY`      | Ключ аутентификации API эмбеддингов        | (пусто)                                                   |
| `EMBEDDING_MODEL`        | Модель эмбеддингов                         | `qwen3-embedding-8b`                                      |
| `EMBEDDING_DIMENSION`    | Размерность эмбеддинга                     | `8192`                                                    |
| `API_KEY`                | Ключ аутентификации MCP-сервера            | (пусто — аутентификация отключена)                        |
| `LOG_LEVEL`              | Уровень логирования                        | `INFO`                                                    |
| `DEDUP_ENABLED`          | Включить дедупликацию                      | `true`                                                    |
| `DEDUP_THRESHOLD`        | Глобальный порог семантической дедупликации | `0.95`                                                   |
| `SEARCH_DEFAULT_LIMIT`   | Лимит результатов поиска по умолчанию      | `10`                                                      |
| `SEARCH_DEFAULT_THRESHOLD`| Порог релевантности поиска по умолчанию    | `0.7`                                                     |
| `MCP_HOST`               | Хост сервера                               | `0.0.0.0`                                                 |
| `MCP_PORT`               | Порт сервера                               | `8000`                                                    |
| `CELERY_BROKER_URL`      | URL брокера сообщений (Redis)              | `redis://localhost:6379/0`                                 |
| `CELERY_RESULT_BACKEND`  | URL хранилища результатов (Redis)          | `redis://localhost:6379/0`                                 |
| `CELERY_WORKER_CONCURRENCY` | Количество воркер-процессов            | `4`                                                       |
| `CELERY_WORKER_MAX_MEMORY_PER_CHILD` | OOM-лимит на процесс (KB)     | `200000`                                                  |
| `CELERY_TASK_ROUTES`     | Маршрутизация задач по очередям (JSON)    | `{"memory_tasks.*":{"queue":"memory"},"hash_tasks.*":{"queue":"hash"}}` |
| `PG_USER`                | Пользователь PostgreSQL (локальный профиль)| `athena`                                                  |
| `PG_PASSWORD`            | Пароль PostgreSQL (локальный профиль)      | —                                                         |
| `REDIS_PASSWORD`         | Пароль Redis (локальный профиль)           | —                                                         |

### PostgreSQL (локальный профиль)

Для локального запуска используется образ `pgvector/pgvector:pg17` с предварительно настроенным HNSW-индексом:

- Расширение `vector`
- Таблица `memories` с колонкой `embedding vector(8192)`
- HNSW-индекс с параметрами `m = 16`, `ef_construction = 200`
- B-tree индексы: `user_id`, `namespace`, `created_at DESC`
- Триггер автообновления `updated_at`
- Уникальный индекс на `(namespace, content_hash)` для точной дедупликации

---

## Аутентификация

Опциональная защита на основе API-ключа. Включается установкой переменной `API_KEY` в `.env`.

**Механизм:**

- HTTP-middleware проверяет заголовок `Authorization: Bearer <API_KEY>` для всех эндпоинтов
- ASGI-middleware защищает `/mcp` (mount-приложение FastMCP)
- Белый список (доступ без аутентификации): `/health`, `/metrics`

При пустом значении `API_KEY` доступ открыт.

---

## Мониторинг

### Метрики Prometheus

Эндпоинт `/metrics` предоставляет 13+ метрик:

| Метрика                                   | Тип       | Описание                                    |
|-------------------------------------------|-----------|---------------------------------------------|
| `athena_http_requests_total`              | Counter   | Количество HTTP-запросов (method, endpoint, status) |
| `athena_http_request_duration_seconds`    | Histogram | Длительность HTTP-запросов                  |
| `athena_db_pool_size`                     | Gauge     | Текущий размер пула соединений              |
| `athena_db_pool_available`                | Gauge     | Доступные соединения в пуле                 |
| `athena_embedding_duration_seconds`       | Histogram | Длительность вызова API эмбеддингов         |
| `athena_search_results_count`             | Histogram | Количество результатов поиска               |
| `selti_count`                     | Gauge     | Общее количество записей (по namespace)     |
| `athena_mcp_tool_calls_total`             | Counter   | Вызовы MCP-инструментов (tool, status)      |
| `athena_mcp_tool_duration_seconds`        | Histogram | Длительность выполнения MCP-инструментов    |
| `athena_embedding_cache_hits_total`       | Counter   | Попадания в кеш эмбеддингов                 |
| `athena_embedding_cache_misses_total`     | Counter   | Промахи кеша эмбеддингов                    |
| `athena_dedup_skipped_total`              | Counter   | Пропуски дедупликации (namespace, reason)   |
| `athena_dedup_inserted_total`             | Counter   | Вставки после проверки дедупликации         |

### Healthcheck

```bash
curl http://localhost:8000/health
```

Ответ содержит версию сервера и статусы проверок конфигурации.

### Grafana Dashboard

Готовый dashboard для PostgreSQL + pgvector — `monitoring/dashboards/postgres-pgvector.json`.

### Экспортёры

Для production-развёртывания предусмотрены экспортёры (включаются через `-f monitoring/exporters/docker-compose.exporters.yml`):

- **postgres-exporter** (порт 9187) — метрики PostgreSQL
- **redis-exporter** (порт 9121) — метрики Redis

### Алерты

Правила алертинга — `monitoring/alerts/prometheus-rules.yml`:

- Доступность PostgreSQL и Redis
- Высокая загрузка соединений
- Конфликты запросов на реплике
- Долгие запросы (> 5 минут)
- Отставание WAL-архивации
- Отсутствие бэкапов
- Отсутствие HNSW-индекса при > 10k записей
- Высокое потребление памяти Redis
- Высокий процент промахов кеша Redis

---

## Миграции

Управление схемой базы данных — через встроенный migration runner.

```bash
# Применить все неприменённые миграции
python migrations/run.py

# Откатить последнюю миграцию
python migrations/run.py --down
```

Миграции находятся в `migrations/` — версионированные SQL-файлы с up/down-секциями. Система отслеживает применённые миграции в таблице `_migrations`.

**Доступные миграции:**

| Файл              | Описание                                    |
|-------------------|---------------------------------------------|
| `001_initial.sql` | Начальная схема: расширение vector, таблица memories, HNSW-индекс, триггеры, функция поиска |
| `002_dedup.sql`   | Дедупликация: content_hash, source_type, source_location, version, is_archived, уникальный индекс |
| `003_athene_memory.sql` | Расширение: embedding cache, batch operations |
| `004_infrastructure.sql` | Инфраструктурные метрики и мониторинг |
| `005_relations.sql` | Таблица relations для графа знаний: связи между гранулами, индексы, каскадное удаление |

---

## Zero-downtime Deploy

Скрипт `deploy.sh` реализует стратегию rolling-обновления:

1. Пулл нового образа из GHCR
2. Запуск нового контейнера на временном порту
3. Ожидание прохождения healthcheck
4. Переключение трафика (через reverse proxy или прямой restart)
5. Остановка и удаление старого контейнера

---

## Тестирование

Проект покрыт модульными и интеграционными тестами (142+ теста).

```bash
# Установка зависимостей для тестов
pip install -r requirements.txt

# Запуск тестов
pytest tests/ -v

# С отчётом о покрытии
pytest tests/ --cov=memory_server -v
```

---

## Разработка

Проект разработан в рамках **Argenta Team** — архитектура, разработка и сопровождение информационных систем.

Разработчик: [Dek1m](https://github.com/Dek1m)

---

**Argenta Team** — архитектура, разработка и сопровождение информационных систем.

---
Auto-deploy test: 2026-08-01T08:54:55Z