MCP + pgvector: память для LLM за один вечер

MCP + pgvector: память для LLM за один вечер

MCP + pgvector: память для LLM за один вечер

Без памяти LLM-ассистент быстро надоедает. Отвечает на вопросы, но не помнит ни ваши правила, ни прошлые разговоры с клиентом. Через пару недель люди просто перестают им пользоваться.

Расскажу, как я за вечер поднял минимальную рабочую схему: PostgreSQL с pgvector хранит документы и векторы, MCP-сервер отдаёт три инструмента — поиск, запись и последние записи. Всё просто, масштабируется до сотен тысяч документов.

Почему именно MCP

Model Context Protocol — открытый стандарт, по которому модель вызывает внешние инструменты. Один сервер описывает свои возможности, и любой клиент их подхватывает. Не нужно городить отдельную интеграцию под каждый ассистент.

Для корпоративной памяти это особенно удобно: разные люди пишут и читают, а MCP даёт единый доступ с правами и аудитом внутри сервера.

Как всё устроено

Три простых слоя:

  1. Хранилище — PostgreSQL 16 + pgvector. Таблица memory с текстом, эмбеддингом, метаданными и временем.
  2. Энкодер — локальная модель sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2. Работает на CPU, тянет русский, выдаёт вектор 384.
  3. MCP-сервер — Python с тремя инструментами: search_memory, add_memory, list_recent.

LLM подключается по stdio или HTTP и вызывает нужные инструменты.

Поднимаем базу

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TABLE memory (
    id          BIGSERIAL PRIMARY KEY,
    content     TEXT NOT NULL,
    embedding   VECTOR(384) NOT NULL,
    metadata    JSONB DEFAULT '{}'::jsonb,
    created_at  TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX memory_embedding_idx ON memory USING hnsw (embedding vector_cosine_ops);
CREATE INDEX memory_created_at_idx ON memory (created_at DESC);
CREATE INDEX memory_metadata_idx ON memory USING gin (metadata);

HNSW-индекс даёт быстрый поиск даже на больших объёмах, а JSONB позволяет фильтровать по отделу, тегам и автору.

Эмбеддинги без GPU

MiniLM-L12 — хороший компромисс. 384 измерения, быстро работает на обычном CPU.

from sentence_transformers import SentenceTransformer

_model = SentenceTransformer("sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2")

def embed(text: str) -> list[float]:
    vec = _model.encode(text, normalize_embeddings=True)
    return vec.tolist()

Нормализация упрощает поиск. Если нужен русский получше — берите intfloat/multilingual-e5-base.

MCP-сервер: три инструмента

Берём официальный SDK и получаем сервер примерно в 150 строк. Ассистент сразу начинает отвечать со ссылками на регламенты и протоколы встреч.

Что добавить перед продом

Для пилота хватит, а для реальной эксплуатации стоит добавить:

  • Нарезку документов на куски по 300–500 слов.
  • Фильтры по правам через metadata.
  • Аудит вызовов.
  • Реранкер для улучшения качества.
  • Инструменты обновления и удаления.

Где это реально помогает

У меня лучше всего работало в трёх случаях: внутренняя база знаний, память диалогов с клиентами и поддержка разработки (архитектурные решения, постмортемы). Главный плюс — модульность. Сегодня GigaChat, завтра YandexGPT, а MCP-сервер и база остаются теми же.

Итог

PostgreSQL + pgvector + MiniLM-L12 + 150 строк MCP-сервера. Этого достаточно, чтобы ассистент начал реально помнить контекст и приносить пользу.