MVP Итерация 1: Минимально жизнеспособная версия архитектуры долгосрочной памяти для AI в IDE.
Проект предоставляет архитектуру долгосрочной памяти для AI-ассистентов в IDE (например, Cursor IDE) через MCP (Model Context Protocol). Состоит из:
- Rails Core API: API-only приложение для управления памятью (PostgreSQL + pgvector)
- Sidekiq + Redis: Асинхронная обработка задач через Sidekiq с использованием Redis
- Ruby MCP-сервер: STDIO сервер для интеграции с IDE через MCP протокол
📘 Для проекта Insales: См. подробную инструкцию INSTALLATION_INSALES.md с пошаговыми инструкциями по установке и подключению MCP сервера в Cursor IDE.
Требуется установленный Docker и Docker Compose (плагин docker compose).
Compose-файл использует dev-слой Dockerfile, в котором собраны все зависимости (включая development/test).
- Собрать и запустить все сервисы (Rails API, PostgreSQL + pgvector, Redis):
docker compose up --build
- После первого запуска дополнительно ничего делать не нужно — контейнеры выполнят
bundle installиrails db:prepareавтоматически. - API будет доступен на
http://localhost:3101, база данных — наlocalhost:15433, Redis — наlocalhost:16380.
Полезные команды:
- Остановка окружения:
docker compose down - Повторная установка gems:
docker compose run --rm web bundle install - Запуск тестов:
docker compose run --rm web bundle exec rspec - Выполнение разовой команды Rails:
docker compose run --rm web ./bin/rails <command> - Подключиться к базе:
docker compose exec db psql -U postgres memcp_development - Подключиться к Redis:
docker compose exec redis redis-cli - Просмотр логов Sidekiq:
docker compose logs worker -f
Порты сервисов заданы через ENV и по умолчанию не пересекаются со стандартными значениями:
• API:3101(MEMCP_WEB_PORT)
• PostgreSQL:15433(MEMCP_DB_PORT)
• Redis:16380(MEMCP_REDIS_PORT)
При необходимости переопределите их при запуске, напримерMEMCP_WEB_PORT=3200 MEMCP_DB_PORT=25433 MEMCP_REDIS_PORT=26380 docker compose up.
Sidekiq worker по умолчанию не стартует. Запустить его можно так:
docker compose --profile queue upWorker использует Redis для хранения очередей задач. Конфигурация Redis находится в config/database.yml (секция redis:), отдельные базы данных используются для:
default(db: 0) — общее использование Redissidekiq(db: 1) — очереди Sidekiq
Проверка работы Redis и Sidekiq:
# Проверка Redis
docker compose exec redis redis-cli ping
# Проверка через Rails console
docker compose run --rm web bundle exec rails runner 'puts $redis.ping; puts Sidekiq.redis { |c| c.ping }'
# Постановка тестовой джобы в очередь
docker compose run --rm web bundle exec rails runner 'TestJob.perform_async("test")'bundle install
# Создайте базу данных PostgreSQL
sudo -u postgres psql -c "CREATE DATABASE memcp_development;"
sudo -u postgres psql -d memcp_development -c "CREATE EXTENSION IF NOT EXISTS vector;"
# Запустите миграции
rails db:create
rails db:migrate
# Убедитесь, что Redis запущен локально или через Docker
# Запустите Sidekiq worker (в отдельном терминале)
bundle exec sidekiq -C config/sidekiq.yml
# Заполнить embeddings для существующих записей (опционально)
rails memories:generate_embeddings
# Запуск Rails API
rails serverAPI также будет доступен на http://localhost:3001
- Провайдер по умолчанию:
MEMORY_EMBEDDING_PROVIDER=local_1024. - Модель Qwen3-Embedding-0.6B (квантизованная Q8_0) скачивается из репозитория Qwen/Qwen3-Embedding-0.6B-GGUF.
Рекомендуемый файл:Qwen3-Embedding-0.6B-Q8_0.gguf(≈1.2 ГБ). - Путь к весам задаётся переменной
MEMORY_EMBEDDING_MODEL_PATH. По умолчанию используетсяtmp/embeddings/Qwen3-Embedding-0.6B-Q8_0.gguf. - Для Matryoshka-режима установите
MEMORY_EMBEDDING_OUTPUT_DIM=1024(значение по умолчанию совпадает со схемой БД). - Endpoint локального сервиса задаётся
MEMORY_EMBEDDING_ENDPOINT(по умолчаниюhttp://127.0.0.1:8081/embed). - Для скачивания модели используйте
bin/setup_embeddings(поддерживаетHF_TOKENи проверку SHA256 черезMEMORY_EMBEDDING_MODEL_SHA256). - Требования к окружению:
python3(с поддержкойvenv),pip,cmake, компилятор (build-essential/gcc). Скриптbin/embedding_serverсоздаёт виртуальное окружениеtmp/embedding-venvи устанавливает зависимости изembeddings/requirements.txt(FastAPI, uvicorn, llama-cpp-python). - Заглушка для OpenAI (
openai_1536) оставлена для будущего расширения; для неё потребуетсяOPENAI_API_KEY.
bin/setup # устанавливает зависимости, миграции и очередь (запускает bin/dev автоматически)bin/dev запускает Rails и сервис embeddings через Procfile.dev. Для ручного запуска служб:
bin/setup_embeddings # скачивает веса
MEMORY_EMBEDDING_PORT=8081 bin/embedding_server
bin/rails server
# Sidekiq: запуск воркера в отдельном терминале
# bundle exec sidekiq -C config/sidekiq.yml
### Atlas Adapter (зеркалирование insales_atlas)
- Сервис `Atlas::SyncService` копирует документы из `insales_atlas/` в `storage/atlas/`, вычисляет SHA256 и избавляется от дубликатов (один blob на несколько источников).
- Для запуска синхронизации:
```bash
bundle exec rails atlas:sync- Метаданные доступны в
storage/atlas/index.json, контент — вstorage/atlas/blobs/. - Повторный запуск обновляет индекс и копирует только новые/изменённые файлы (по SHA).
- Каталог
storage/не версионируется: каждый разработчик наполняет его локально (см.storage/README.md). При очистке окружения содержимое можно удалять.
### File Sync Adapter (локальные документы)
- `FileSync::WatcherService` синхронизирует `documents/` в `storage/documents/`, вычисляет SHA и обновляет индекс.
- Поддерживаются текстовые расширения `.md`, `.mdc`, `.txt`, `.rb`, `.js`, `.sql`, `.json`, `.yml`, `.yaml`.
- Запуск initial sync + watcher:
```bash
bundle exec rails runner 'FileSync::WatcherService.call(params: { source_root: Rails.root.join("documents"), target_root: Rails.root.join("storage/documents") })'
(держи процесс в отдельном терминале — listen следит за изменениями).
- Метаданные лежат в
storage/documents/index.json; удаление исходного файла удаляет копию и запись в индексе. - Краткий smoke:
После проверки останови watcher комбинацией
echo "# notes" > documents/sample.md bundle exec rails runner 'service = FileSync::WatcherService.call(params: { source_root: Rails.root.join("documents"), target_root: Rails.root.join("storage/documents") }); sleep 1' cat storage/documents/sample.md cat storage/documents/index.json | jq .
Ctrl+C.
- Навыки регистрируются через
Skills::Registry; сейчас доступны:atlas_search(query:, limit: 10)— поиск в зеркалеstorage/atlas/index.json.documents_grep(pattern:, limit: 10)— текстовый поиск вstorage/documents/.
- Планировщик
Planner::SimplePlannerподбирает навык по запросу или принимаетskill_idявно. - Примеры:
result = Planner::SimplePlanner.call(params: { query: "atlas adr short link" }) puts result.result[:result][:matches] result = Planner::SimplePlanner.call(params: { skill_id: :documents_grep, skill_params: { pattern: "CartSessions" } }) puts result.result[:result][:matches]
- Ошибки навыков возвращаются в
result.result[:errors].
Observability::HubServiceпишет события планировщика и навыков вstorage/logs/observability/current.jsonl. Формат записи: JSONL со стандартными полями (trace_id,event_id,operation,status,duration_ms,payload,extra,error).- Встроенная ротация: при достижении 10 МБ файл переименовывается в
current.jsonl.<timestamp>.jsonl.gz, новый файл создаётся автоматически. - Smoke-сценарий:
# 1. Подготовить временный источник Atlas mkdir -p tmp/atlas_demo/guides cat <<'EOF' > tmp/atlas_demo/guides/intro.md # Demo Guide Demo document for observability smoke. EOF bundle exec rails runner 'Atlas::SyncService.call(params: { source_root: Rails.root.join("tmp/atlas_demo") })' # 2. Вызвать планировщик с явным навыком bundle exec rails runner 'Planner::SimplePlanner.call(params: { query: "", skill_id: :atlas_search, skill_params: { query: "demo" } })' # 3. Проверить свежие события tail -n 5 storage/logs/observability/current.jsonl # 4. Очистить временные артефакты (опционально) rm -rf tmp/atlas_demo
- При ошибках навыков
statusсменится наerror, дополнительный контекст попадёт вerrorиextra. Если запись лога не удалась, бизнес-логика продолжит выполнение (исключения подавляются).
Проверка сервиса embeddings:
curl -X POST http://127.0.0.1:8081/embed \
-H "Content-Type: application/json" \
-d '{"inputs":["embedding smoke test"]}'Ответ должен содержать массив embeddings длиной 1024.
Финальный слой Dockerfile — production. В нём только runtime-зависимости и включён режим RAILS_ENV=production.
docker build --target production -t memcp:latest .Минимальный пример запуска (потребуются переменные окружения с доступом к базе данных и ключу Rails):
docker run --rm \
-e RAILS_ENV=production \
-e RAILS_MASTER_KEY=<ваш_rails_master_key> \
-e DATABASE_URL=postgres://user:password@db:5432/memcp_production \
-p 3001:3001 \
memcp:latestВ production-окружении убедитесь, что база данных содержит расширение vector, и выполните миграции:
docker run --rm \
-e RAILS_ENV=production \
-e RAILS_MASTER_KEY=<ваш_rails_master_key> \
-e DATABASE_URL=postgres://user:password@db:5432/memcp_production \
memcp:latest \
bundle exec rails db:migrateℹ️ Для production-развёртываний убедитесь, что база данных поддерживает расширение
vector, и ключRAILS_MASTER_KEYдоступен контейнеру.
Dockerfile содержит только системные зависимости (python3, pip, cmake), поэтому перед сборкой или запуском контейнера разработчику нужно вручную подготовить модель:
# локально, до docker build
bin/setup_embeddings # скачивает веса Qwen3 в tmp/embeddings
# опционально проверить сервис
MEMORY_EMBEDDING_PORT=8081 bin/embedding_server &
curl -s http://127.0.0.1:8081/embed \
-H "Content-Type: application/json" \
-d '{"inputs":["embedding smoke test"]}' \
| jq '.embeddings[0] | length'
kill %1 # остановить серверЗатем можно собирать образ (docker build ...). Внутри контейнера модель уже лежит в tmp/embeddings тома/работающего каталога, и bin/embedding_server можно запускать аналогично.
Добавьте в конфигурацию Cursor IDE (~/.cursor/mcp.json или через настройки):
{
"mcpServers": {
"memcp": {
"command": "ruby",
"args": ["/Users/asromanychev/dev/memcp/mcp_server.rb"],
"env": {
"MEMCP_API_URL": "http://localhost:3101"
}
}
}
}Важно:
- Замените путь
/Users/asromanychev/dev/memcp/mcp_server.rbна ваш реальный путь к файлу - Убедитесь, что сервер запущен:
docker compose --profile queue up - API доступен на
http://localhost:3101(порт можно изменить черезMEMCP_WEB_PORT)
Перезапустите Cursor IDE после настройки.
app/controllers/memory_controller.rb- API контроллер с заглушкамиrecallиsaveapp/models/- МоделиProjectиMemoryRecordapp/jobs/- Sidekiq джобы (базовый классBaseSidekiqJob,GenerateEmbeddingJob,TestJob)db/migrate/- Миграции для таблицprojectsиmemory_recordsconfig/database.yml- Конфигурация PostgreSQL и Redis (секцияredis:)config/initializers/01_redis.rb- Инициализация подключения к Redis ($redis)config/initializers/sidekiq.rb- Конфигурация Sidekiq (сервер и клиент)config/sidekiq.yml- Конфигурация очередей Sidekiqmcp_server.rb- Ruby MCP STDIO сервер с инструментамиrecallиsave
POST /recall- Поиск воспоминаний (заглушка)POST /save- Сохранение воспоминаний (заглушка)
recall(query, project_path?)- Поиск воспоминаний по запросуsave(content, project_path, metadata?)- Сохранение записи памяти
- SETUP.md - подробные инструкции по установке и настройке
- docs/server_setup.md - настройка серверного ноутбука для удаленного доступа
- REMOTE_SETUP.md - подключение к удаленному memcp с клиентского устройства
Текущее состояние (2025-11-11):
✅ Завершено:
- MVP-01: Core API Service Objects (
Memories::RecallService,Memories::SaveService) - MVP-02: Векторный поиск (
Memories::EmbeddingService, гибридный поиск вRecallService,GenerateEmbeddingJob) - Sidekiq + Redis: Настройка очередей задач через Sidekiq с использованием Redis (конфигурация по аналогии с Insales, упрощенная версия)
- Конфигурация Redis в
config/database.ymlс отдельными базами для default (db: 0) и sidekiq (db: 1) - Инициализация
$redisвconfig/initializers/01_redis.rb - Конфигурация Sidekiq в
config/initializers/sidekiq.rb - Базовый класс
BaseSidekiqJobдля джоб
- Конфигурация Redis в
- Atlas Adapter (
Atlas::SyncService) — зеркалирование insales_atlas - File Sync Adapter (
FileSync::WatcherService) — синхронизация локальных документов - Skills & Planner MVP (
Skills::Registry,Planner::SimplePlanner, навыкиatlas_searchиdocuments_grep) - Observability Hub (базовая реализация:
Observability::HubService, JSONL-логи с ротацией, интеграция в планировщик)
Следующие шаги:
Этап 2 (текущий):
- MVP-03: Дедупликация (SimHash/MinHash для обнаружения похожих записей и upsert логика)
Этап 3 (отложено):
- Расширить Observability Hub: агрегаты по навыкам, экспорт в Prometheus/ClickHouse, алерты
- Когда необходимо: при реальной нагрузке (десятки запросов/мин), необходимости мониторинга в production, росте команды (нужны дашборды), автоматической диагностике проблем
- Demo Playbooks (resettable сценарии для dev-практик)
MIT