MCP + pgvector: память для LLM за один вечер
Без памяти LLM-ассистент быстро надоедает. Отвечает на вопросы, но не помнит ни ваши правила, ни прошлые разговоры с клиентом. Через пару недель люди просто перестают им пользоваться.
Расскажу, как я за вечер поднял минимальную рабочую схему: PostgreSQL с pgvector хранит документы и векторы, MCP-сервер отдаёт три инструмента — поиск, запись и последние записи. Всё просто, масштабируется до сотен тысяч документов.
Почему именно MCP
Model Context Protocol — открытый стандарт, по которому модель вызывает внешние инструменты. Один сервер описывает свои возможности, и любой клиент их подхватывает. Не нужно городить отдельную интеграцию под каждый ассистент.
Для корпоративной памяти это особенно удобно: разные люди пишут и читают, а MCP даёт единый доступ с правами и аудитом внутри сервера.
Как всё устроено
Три простых слоя:
- Хранилище — PostgreSQL 16 + pgvector. Таблица
memoryс текстом, эмбеддингом, метаданными и временем. - Энкодер — локальная модель
sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2. Работает на CPU, тянет русский, выдаёт вектор 384. - 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-сервера. Этого достаточно, чтобы ассистент начал реально помнить контекст и приносить пользу.
