Примечание: это перевод моей же статьи https://scrobot.substack.com/p/agentis-memory-redis-compatible-store, так что автор точно доносит свою мысль, без потери контекста при переводе :)
В наше время уже никого не удивишь разработкой агентов, очередной оптимизацией, новой моделью или новой инфраструктурой для нейронок. Всё это в порядке вещей. Однако одно дело читать в Twitter «мы написали агента X и он оптимизировал нам процессы на 300000%», и совсем другое — начать копать чуть глубже. Копнёшь — а «агентом» называют скилл с одним промптом.
Разработка настоящих агентов — задача не тривиальная. Достаточно посмотреть на утёкшие исходники Claude CLI — это не просто CLI, а целая инфраструктура бизнес-логики вокруг LLM. Я бы сравнил разработку агентов с разработкой типичных бэкенд-компонентов. Аналогия такая: если вы пишете каноничный бэкенд-сервис — вам нужна СУБД. Если Web3-сервис — блокчейн. Но на СУБД или блокчейне происходит в лучшем случае 50% всей логики. Вся магия крутится именно на бэкенде. С агентами то же самое: подключаешь AI SDK, конфигурируешь мыслительное ядро и пишешь вокруг него всю обвязку — мониторинги, AIOps, оркестрацию, memory management.
Вот про memory management и пойдёт речь.
Представьте: у вас шесть AI-агентов расследуют инцидент на продакшене. Один нашёл OOMKilled в логах — это root cause. Второй в это же время копается в Grafana и строит свою теорию про CPU spike. Третий вытаскивает из Slack, что вчера был деплой. Они работают параллельно, но ничего не знают друг о друге. Результат — три конкурирующих гипотезы, из которых две мусор.
У наших агентов не было общей памяти. И я решил её написать.
Получился сервер, который говорит на Redis-протоколе, но умеет в семантический поиск из коробки. Один бинарник, эмбеддинги считаются локально, любой Redis-клиент подключается без переделок. Никаких REST API, никаких внешних зависимостей, никаких API-ключей.
Вот история о том, как я к этому пришёл, почему попытка форкнуть Redis провалилась, и как Java-код, который был в два раза медленнее Redis, стал в 1.36 раза быстрее.
У наших агентов была амнезия
Полгода назад мы строили агента для расследования инцидентов на продакшене. Архитектура, в общем-то, каноничная для таких систем: Supervisor принимает алерт, составляет план через PlannerService, а дальше запускает пачку субагентов параллельно через Promise.allSettled(). Каждый субагент идёт в свой источник данных, копает что может, и потом Synthesizer собирает из их находок финальный отчёт.
Субагентов шесть, и у каждого свой MCP-инструмент, свой scope, своя зона ответственности:

MetricsInvestigator лезет в Grafana, LogsInvestigator — в логи, SlackContextInvestigator — в переписку, HistoryInvestigator — в базу прошлых инцидентов. На бумаге — красота, всё параллельно, всё быстро.
А на практике — все шесть агентов работали в абсолютном вакууме друг от друга.
LogsInvestigator находит в логах OOMKilled на поде payment-service. Отлично, это root cause, дело закрыто. Но MetricsInvestigator об этом ни сном ни духом — он в это время параллельно копается в Grafana, видит spike CPU и старательно строит свою альтернативную теорию. SlackContextInvestigator вытаскивает из переписки, что вчера деплоили новую версию, но не может сопоставить это с OOM, потому что не знает про OOM. В итоге Synthesizer получает на вход три конкурирующих гипотезы, из которых две — откровенный мусор. А иногда и вовсе галлюцинации, потому что единого источника правды у агентов просто не существовало.
Первое, что мы попробовали — md-файлы. Идея простая до безобразия: каждый агент пишет свои findings в общий файл, остальные читают. На словах звучит разумно, на деле работает отвратительно. Race conditions при параллельной записи, никакой структуры, никакого поиска по смыслу, никакого TTL. Это не память — это блокнот, который шестеро человек одновременно пытаются заполнить одной ручкой.
Стало очевидно, что нужна оперативная память. Не долгосрочный RAG на OpenSearch, который мы и так менеджим и курируем для других задач — а именно быстрый shared-контекст. Такой, куда агент записал факт, и через миллисекунды другой агент нашёл его по смыслу. По сути — как Redis, только с пониманием семантики. И вот с этой мыслью я полез смотреть, что есть на рынке.
Что есть на рынке и почему ничего не подошло
Ландшафт решений для «памяти агентов» выглядит на первый взгляд вполне зрело: три основных кандидата, у каждого документация, SDK, примеры. Но стоит присмотреться к архитектуре — и начинается головная боль.
Mem0 — позиционируется как «memory layer for AI agents». Звучит именно как то, что нужно, пока не начинаешь смотреть, из чего оно состоит. REST API, Python SDK. Чтобы завести, тебе нужен Docker, Qdrant или Postgres для хранения векторов, и API-ключ от OpenAI или Cohere для эмбеддингов. Считаем: каждый MEMSAVE — это HTTP-запрос к твоему серверу, который делает ещё один HTTP-запрос к OpenAI, ждёт вектор, пишет его в Qdrant. Три сетевых хопа, чтобы сохранить один факт. Для оперативной памяти, где счёт идёт на миллисекунды — это, мягко говоря, расточительно.
Zep — примерно та же история, вид сбоку. REST API, свой SDK, Postgres под капотом, эмбеддинги через внешний API. Плюс бонусом — необходимость менеджить ещё одну базу данных. Как будто у нас их мало.
Redis + Vector Search (Redis Stack) — вот это уже ближе к делу. RESP-протокол родной, любой клиент подходит. Но векторный поиск идёт через модуль RediSearch, а эмбеддинги надо считать снаружи и передавать готовые вектора. То есть ты всё равно где-то поднимаешь отдельный сервис для инференса, и к каждому запросу прилетает дополнительный сетевой вызов к embedding API. Полшага до цели, но полшага — это не цель.

Главная боль, которая объединяла все эти решения — сеть и маршалинг. Мне не нужен REST. Мне не нужен кастомный SDK, который я потом буду обновлять и отлаживать. Мне не нужен API-ключ от OpenAI, чтобы сохранить факт «под payment-service закончилась память». Я хочу сделать MEMSAVE key text и получить +OK. Всё. В одном процессе, без сетевых вызовов, без внешних зависимостей. Это же оперативная память, а не бюрократия.
Попытка номер один: форкнуть Redis
Окей, раз на рынке ничего подходящего нет — значит, надо строить самому. И первая идея была, казалось бы, очевидной: берём Redis, пишем модуль на C, встраиваем ONNX Runtime прямо в него. Redis уже умеет в KV, протокол готов, production-tested, осталось только добавить эмбеддинги и HNSW-индекс. Что может пойти не так?
Я попробовал. Пошло не так всё.
Встраивать ONNX Runtime в C-код Redis — это, скажем так, специфический вид инженерного удовольствия. Ручное управление памятью на стыке Redis allocator и ONNX, линковка нативных библиотек, отладка сегфолтов в месте, где event loop Redis встречается с inference thread от ONNX. Через неделю у меня был полурабочий прототип, который стабильно падал при конкурентных запросах, и стойкое ощущение, что я двигаюсь в неправильном направлении.
Я остановился и задал себе честный вопрос: зачем я мучаю себя C, если 12 с лишним лет пишу на Java?
Ответ был неприятно прост — я зачем-то решил, что «настоящий» Redis должен быть написан на C. Что это чуть ли не закон природы. Но если подумать трезво: мне не нужен «настоящий Redis». Мне нужен сервер, который говорит на RESP-протоколе и умеет в эмбеддинги. А на чём он внутри написан — дело десятое. Пользователю вообще без разницы, он видит только redis-cli -p 6399 PING → PONG.
Стек: GraalVM, Java 25 и игрушки из инкубатора
Итак, решение принято — пишем с нуля на Java. Но не на классической JVM, нет. Тут важный нюанс.
GraalVM native-image — это штука, которая компилирует Java-байткод в самый настоящий нативный бинарник. Никакой JVM при запуске, никакого прогрева JIT-компилятора, startup за миллисекунды. На выходе — один файл размером ~150MB, внутрь которого вшита модель эмбеддингов. Скачал, запустил, работает. Ровно как Redis — никаких плясок с зависимостями.
А раз уж проект всё равно внутренний, не для продажи, и на нём никто в продакшене жизнью не рискует — я решил заодно обкатать несколько технологий из инкубатора. Когда ещё представится такая возможность поиграться с ними на чём-то живом?
Java Vector API (Project Panama) — это SIMD-инструкции прямо из Java-кода, без JNI, без unsafe. Зачем это здесь? Всё просто: самая горячая операция в семантическом поиске — вычисление косинусного сходства между векторами. Когда MEMQUERY пробегает по HNSW-графу и на каждом шаге считает расстояние до очередной ноды — именно здесь SIMD даёт ощутимый прирост. Не теоретический, а вполне измеримый, о чём расскажу ниже.
Project Loom (Virtual Threads) — легковесные потоки, которые Java-рантайм переключает сам, без overhead на OS-треды. Каждое RESP-соединение живёт в своём virtual thread. Никакого event loop, никаких колбэков, никакого reactor pattern — просто пишешь линейный код, а рантайм разруливает конкурентность. После years of reactive programming это ощущалось как глоток свежего воздуха.
ONNX Runtime — инференс модели all-MiniLM-L6-v2 прямо в процессе сервера. 384-мерные вектора, inference ~2-5ms на чанк. Никуда не ходим по сети, всё прямо здесь, в том же процессе.
jvector — Java-реализация HNSW-графа для approximate nearest neighbor search. Зрелая библиотека, хорошо ложится в экосистему.
История про то, как производительность упала, а потом взлетела
А теперь — самое интересное, ради чего, возможно, стоит читать эту статью.
Первая версия работала на обычной JVM. Без Vector API, без native-image. Просто Java, просто ConcurrentHashMap, просто ONNX Runtime через Java-binding. Я запустил memtier_benchmark, посмотрел на цифры и… увидел throughput в два раза хуже, чем у Redis.
Знаете это ощущение, когда ты неделю писал код, запускаешь бенчмарк, и тебе хочется молча закрыть ноутбук и переосмыслить жизненные решения? Вот это было оно.
Но вместо экзистенциального кризиса я сделал две конкретные вещи. Во-первых, скомпилировал всё через GraalVM native-image — убрал JVM startup overhead, убрал JIT warmup, получил предсказуемую производительность с первой секунды. Во-вторых, переписал hot path вычисления косинусного сходства на Vector API с явным использованием SIMD-инструкций.
Результат: с 0.5x Redis до 1.36x Redis на строковых операциях. С ~60 тысяч ops/sec до 168 тысяч. Тот самый момент, когда понимаешь, что Java может быть быстрой — надо только знать, где именно она тормозит, и что с этим делать.
Как это работает
Внутри всё устроено достаточно прямолинейно — один процесс, два движка, которые живут рядом и делят данные:

По левую сторону — классический KV-store. 90+ стандартных Redis-команд: строки, хэши, списки, множества, sorted sets, TTL, SCAN, даже базовый pub/sub. Можно подключить Redis Insight на порт 6399 и всё будет выглядеть ровно как обычный Redis. Потому что для клиента это и есть Redis.
Но поверх этого живут четыре команды, ради которых всё и затевалось:
MEMSAVE — принимает текст, разбивает его на чанки по предложениям, каждый чанк прогоняет через ONNX-модель для получения 384-мерного вектора, и индексирует результат в HNSW-граф. При этом сама KV-запись возвращает +OK мгновенно — вся тяжёлая работа с эмбеддингами и индексацией идёт в фоне, асинхронно. Типичная задержка от сохранения до готовности к поиску: 5-10ms на чанк.
redis-cli -p 6399 MEMSAVE "agent:fact:stack" "We use Python 3.12 with FastAPI"
# → OK
MEMQUERY — семантический поиск. Ты формулируешь запрос человеческим языком, система его векторизует и находит ближайших соседей в HNSW-графе по косинусному сходству. Возвращает ключ, текст и score.
redis-cli -p 6399 MEMQUERY agent "what web framework do we use" 5
# → 1) 1) "agent:fact:stack"
# 2) "We use Python 3.12 with FastAPI"
# 3) "0.89"
MEMSTATUS — статус индексации конкретного ключа: indexed, pending или error.
MEMDEL — удаление из KV-store и из векторного индекса одновременно.
И вот что мне нравится в этом подходе больше всего: никаких кастомных SDK. Jedis, redis-py, ioredis, go-redis — берёшь любой Redis-клиент, который у тебя уже есть в зависимостях, и отправляешь MEMSAVE/MEMQUERY как обычные кастомные команды через call() или execute_command():
import redis
r = redis.Redis(host='localhost', port=6399)
r.execute_command('MEMSAVE', 'agent:fact:1', 'Payment service hit OOM at 03:42 UTC')
results = r.execute_command('MEMQUERY', 'agent', 'memory issues', '5')
Для нашего incident investigation agent это изменило всё. LogsInvestigator нашёл OOM — сделал MEMSAVE. MetricsInvestigator перед тем, как лезть строить свои теории, делает MEMQUERY и сразу видит: коллега уже нашёл root cause, можно не дублировать работу, а дополнить контекст метриками. Один источник правды, никаких галлюцинаций на пустом месте.
Бенчмарки: честные цифры
Примечание: замеры перформанса были произведены с помощью memtier_benchmark, инструмент от redis labs, на отдельном ubuntu-сервере, не на локальной машине, так что результат бенчмарка можно считать легитимным.
Результаты можно посмотреть тут https://scrobot.github.io/agentis-memory/benchmarks/report.html, ну или произвести самостоятельно.
Слова и архитектурные диаграммы — это замечательно, но в нашем деле принято показывать цифры. Я прогнал memtier_benchmark против Redis 7.4, Dragonfly и Lux. Конфигурация стандартная: 4 threads × 50 clients, данные по 256 байт. Никаких подкруток.
Throughput (ops/sec)

На строках — 1.36x Redis. На mixed workload — 1.40x. Lux быстрее нас (1.62x) — тут без иллюзий. Но у Lux нет встроенного векторного поиска, и это принципиальная разница.
Latency p99 (ms)

И вот тут нужно быть честным: p99 latency на строках у Redis ощутимо ниже — 3.82ms против наших 6.27ms. Это цена за garbage collection. Даже используя G1, один из лучших GC на сегодняший день, GraalVM native-image минимизирует GC-паузы, но не убирает их полностью — и на хвосте распределения это видно. На хэшах и множествах — примерный паритет со всеми конкурентами.
Pipeline scaling
А вот здесь картина становится по-настоящему интересной:

При pipeline depth 100 — 3.19 миллиона ops/sec. Это 1.71x Redis. Здесь начинают играть virtual threads и отсутствие single-threaded event loop: когда запросы идут пачками, многопоточная архитектура масштабируется куда лучше.
Но я хочу подчеркнуть главное: ни один конкурент из этой таблицы не умеет MEMSAVE/MEMQUERY из коробки. Agentis Memory — единственный в этом сравнении, кто совмещает такую производительность на KV-операциях со встроенным семантическим поиском в одном процессе, без внешних зависимостей.
Privacy-first: почему эмбеддинги должны быть локальными
Отдельно хочу сказать про эмбеддинги, потому что это не просто техническое решение, а осознанный выбор.
Наши агенты работают с данными инцидентов. Логи, метрики, Slack-переписка, PagerDuty алерты — всё это чувствительная информация, которая может содержать что угодно: от имён клиентов до деталей внутренней инфраструктуры. Отправлять всё это куда-то в OpenAI, чтобы получить вектор? Нет, спасибо. Даже если формально это «только эмбеддинги» — зачем рисковать, если можно не рисковать?
all-MiniLM-L6-v2 работает через ONNX Runtime прямо в процессе Agentis Memory. Никаких API-ключей, никаких сетевых вызовов, никаких задержек от внешних сервисов, никакой зависимости от чужого uptime. Inference занимает ~2-5ms на чанк — это быстрее, чем один roundtrip до любого облачного API.
И да, это бесплатно. Ноль долларов за embedding, хоть миллион операций в день. Для внутреннего инструмента, который гоняет через себя тысячи фактов — это существенно.
Вместо заключения
Технологии вокруг AI сейчас сменяют одна другую с такой скоростью, что не успеваешь моргнуть. Гонки моделей, гонки инструментов, гонки подходов — даже культура разработки меняется быстрее, чем ты успеваешь к ней привыкнуть. И в этом хаосе очень много инженеров пытается внести свой вклад в новую философию, найти своё место.
Я работаю с AI уже почти пять лет. Мне посчастливилось попробовать GitHub Copilot раньше, чем он стал доступен всем. С тех пор я адаптирую AI в разработке, оптимизирую процессы и переизобретаю подходы — иногда по несколько раз в месяц, потому что то, что работало вчера, завтра уже устаревает.
Agentis Memory — это тоже попытка внести свой вклад в это общее дело. Конечно, это не серебряная пуля и не само совершенство. Можно и покритиковать — и даже нужно, я буду рад конструктивному фидбеку. Но эта штука уже решила множество моих проблем: у меня Claude Desktop, Claude Code, Codex, Antigravity и Junie — все используют shared-память через Agentis Memory. Качество результата стало заметно выше: агенты быстрее понимают, что я от них хочу, совершают меньше ошибок, не дублируют работу.
Свою проблему я решил. Надеюсь, смогу помочь решить и вашу.
GitHub: https://github.com/scrobot/agentis-memory
Автор: Scrobot
- Запись добавлена: 03.04.2026 в 06:14
- Оставлено в
Советуем прочесть:
- Распознавание изображений локальными LLM
- Acer представила рабочую станцию c Nvidia Blackwell для работы с локальными ИИ-моделями
- Claude Code теперь можно запускать с локальными open-source моделями
- Gigabyte представила материнскую плату со встроенным в BIOS драйвером Wi-Fi
- Векторный кэш: делаем умные ответы еще быстрее
- Open-source персистентная память для LLM
- Вышла российская ОС «МСВСфера» 10.1 на базе Linux со встроенным локальным ИИ-ассистентом «Василиса»
- Оркестрация чатов LLM моделей через Redis
- Skyworth представил G7F Pro: умный телевизор со встроенным DeepSeek
- Reddit работает над нейросетевым поиском, который поможет пользователям находить ответы на сложные вопросы


