Вы наверняка когда-нибудь пытались скормить ИИ-модели документацию по целому проекту или написать сложный код с помощью ИИ-агента, и знаете главную боль современных трансформеров — размер контекста. Чтобы модель помнила весь диалог, вы вынуждены хранить его целиком в текстовом виде, каждое следующее сообщение и каждый ответ модели добавлять в этот текст. Чем длиннее текст, тем дольше он доставляется в LLM, тем больше токенов тратится на повторение.

И это никого не устраивает. Ни пользователей, кому приходится покупать больше и больше токенов, чтобы беседы были более осмысленными, ни вендорам, у которых вычислительные мощности впустую тратятся на повторение одних и тех же текстов. Но как иначе, если модель на всех одна, а помнить каждую беседу -- это или держать ее на стороне клиента в текстах, или держать на стороне модели в кэше внимания? Кэш весь расположен на GPU, и его размер зависит от длины беседы квадратично (O(n^2)). В текстах на стороне клиента -- дешевле выйдет, чем арендовать кэш. Особенно, в ИИ-кодинге. У агента-кодера есть весь текст его проекта, набор одних и тех же промптов, спеков и скриптов, которые он постоянно отправляет в LLM, получает, обрабатывает и снова отправляет с парой новых строчек. Весь этот текст, бегающий по кругу, у агента называется State. А сама LLM -- stateless, ее состояние всегда для всех новое при каждом обращении.

Конечно, есть локальные решения: файнтюнинг моделей -- добавление им дополнительных слоёв, дообученных на собственных данных, в результате чего модель приобретает память о конкретных фактах пользователя. Но тюнить можно локальные модели, под это нужны собственные вычислительные мощности, да и процесс файнтюнинга небыстрый, поэтому там хранятся только неизменяемые знания. Для ежедневной работы с быстрым запоминанием диалога файнтюнинг не подходит.

Вот бы модель запоминала всё, что ей отправили, прямо в своих весах, а не в k-v кэше. И чтобы можно было бы эти веса сохранять на флешку и встраивать прямо в LLM, когда надо. Захотел -- поменял флешку -- получил другого агента.

Как оказалось, можно.

В этой статье мы полезем под капот новых архитектур (MIRAS и Titans) и разберемся, как возрождение рекуррентных сетей и концепция динамического запоминания во время инференса готовятся изменить классические трансформеры. А может уже и изменили, последние модели с миллионными контек��тными окнами как бы намекают, что квадратичная зависимость, похоже, преодолена.

Статья, которую я изучал и пытался встроить в свою картину мира: It's All Connected: A Journey Through Test-Time Memorization, Attentional Bias, Retention, and Online Optimization

Кому нужен перевод на русский - у меня есть, лежит в ТГ-канале https://t.me/veriga_pro_AI.
В этой статье всё сильно сложнее и шире, чем в моем изложении, и она будет интересна математикам, а мой текст больше предназначен для разработчиков.

Когда-то, столкнувшись с невозможностью векторизации LSTM, я пытался решать временные ряды с помощью одномерных сверток. Они работали не так эффективно, и сложные паттерны выделяли не очень, но зато их можно было гонять на GPU.
В этом тексте я покажу, как элегантно используется идея сверток в новых рекуррентных нейронных сетях.

Почему умерла LSTM

Самые первые трансформеры с механизмом внимания, придуманные в 2014 году, содержали рекуррентные слои, кодирующие слова, в основном на LSTM (Long Short-Term Memory). У них было прекрасное свойство: память O(1). Модель читала текст по одному слову и обновляла свой вектор скрытого состояния h_t. Каждому законченному предложению соответствовал такой вектор h, который указывал в точку многомерного пространства, соответствующую смыслу предложения. Вектор -- это набор чисел фиксированной длины, в нем закодирован смысл фразы, декодер умеет делать предсказание последовательности слов на основе этого вектора, полученного из входящего промпта.

Но у LSTM было несколько нерешаемых проблем. Во-первых, вектор скрытого состояния был фиксированной длины и чем больше смысла в него надо уложить, тем больше должна быть размерность этого вектора. Слишком маленький вектор не вмещал сложные смыслы, очень большой вектор опять приводил к квадратичному росту сложности O(n^2), причем, неважно, длинную фразу он кодировал, короткую, или вообще, состоящую из одного токена -- все векторы должны быть одной размерности. Во-вторых, нелинейность LSTM не позволяла обучать эти слои на графических процессорах. Каждое следующее состояние зависело от предыдущего, и приходилось скармливать ему слова по одному, в порядке их следования в предложении. Вы не могли посчитать 100-й токен, пока последовательно не просчитали 99 предыдущих. Обучение на гигантских датасетах занимало вечность, а видеокарты простаивали.

В общем, в 2018 году выходит статья "Внимание -- всё что нам нужно", в которой авторы показали, как избавиться от слоёв LSTM и заменить их слоями внимания. Оказывается, если к каждому слову добавить информацию о его позиции в предложении, внимание будет учитывать порядок слов в предложении не хуже рекуррентного слоя. А избавившись от нелинейности, разработчики смогли укладывать все операции на GPU и обрабатывать весь входящий пример за один такт, тут главное чтобы видео-памяти хватило. И распараллелить обучение теперь можно сразу на сотни GPU, и прогонять промпты размером с википедию. Тут у них дело и пошло. То, к чему это пришло в 2026, вы сами видите.

Немного экономики LLM

В основном, режим работы LLM - это диалог. Что люди, что агенты - все общаются с моделью в режиме чата. И одно предложение от пользователя занимает немного места, никто не отдаст под его обработку всю модель, никаких денег не хватит. Но по мере диалога текст, как мы уже говорили, растет, и под него надо отдавать всё больше и больше ресурса. Для инференса не нужны те же мощности, что для обучения, в этом режиме у модели гораздо меньше работы. Но, чтобы обслуживать сразу много подключений и отбивать вложенные в обучение модели миллиарды, надо, чтобы входящий промпт каждого пользователя лёг в память целиком, тогда GPU его быстро прогонит через все слои модели и сгенерирует ответ. И тут же забудет о нем, освободив память для следующего подключения.
Но режим чата подразумевает, что оба собеседника помнят, о чем идет речь в предыдущих сообщениях. Значит, на каждое подключение надо выделять ресурс, достаточный, чтобы обработать весь диалог. Размер этого ресурса каждый раз вычисляется динамически, но он не бесконечен. И поэтому введено ограничение на размер входящего промпта, известное как размер контекста. Он декларируется как свойство модели и стал маркетинговым показателем, как количество камер у смартфона.
А ограничение у контекста связано с той же самой квадратичной зависимостью. Если модел�� выделяет под контекст в пять раз больше, то ресурсов на инференс она тратит в 25 раз больше. Ну, или около того.

Недавно Google выставил линейку Gemini 3 с контекстом в 1млн токенов, опередив ближайших конкурентов в пять раз.
И что, они реально нарастили вычислительные мощности в 25 раз? При их гигаватных датацентрах такое резкое увеличение мощности заставило бы всё земное освещение замигать и выбить пробки.

Рекуррентные сети вернулись!

Да. Наконец-то мы добрались до самого главного.

Отправленные в утиль рекуррентные сети оказались решением проблемы квадратичного роста мощности. Помните же? Весь контекст можно закодировать в вектор скрытого состояния фиксированной длины. Или порезать контекст на чанки и каждый чанк закодировать в вектор. Или как-нибудь еще, неважно, главное -- на выходе из текста любой длины можно получить вектор фиксированного размера.
Да, LSTM ввиду их последовательного выполнения "слово за словом" никак не могли бы помочь, нет пока у человечества способа выполнять такие последовательные вычисления со скоростью параллельных. Но! Если выкинуть нелинейность из рекуррентного пути, можно любую рекурсию превратить в линейное уравнение. И исследователи придумали Линейные RNN.
Теперь вектор скрытого состояния формируется как линейная комбинация предыдущего состояния h_{t-1} и текущего входа x_t. В рекуррентном пути нет нелинейных активаций.
Базовое уравнение вычисление вектора состояния:

h_t = A_t h_{t-1} + B_t x_t

где:
B_t x_t отвечает за то, что именно нужно извлечь из текущего слова и добавить в память.
A_t отвечает за то, сколько прошлой информации (h_{t-1}) нужно сохранить (удержать) или стереть (забыть).

То есть, если нейросеть видит токен, обозначающий конец концепции (например, точку в конце абзаца, слово «Однако» или «Конец»), то механизм генерации A_t выдаст значения, близкие к нулю. Это умножит прошлую память h_{t-1} на 0 (сбросит её), освобождая место для новой мысли, которая придет через B_t x_t.

Как именно вычисляются и ?

Вся прелесть современных линейных RNN в том, что эти параметры вычисляются с помощью самых обычных, маленьких линейных слоев (Dense/Linear), которые применяются к текущему эмбеддингу токена x_t.

Поскольку матричные веса этих слоев обучаются в процессе тренировки сети (градиентным спуском), модель сама учится распознавать, на какие слова реагировать нулями, а на какие — единицами.
Вот как это работает под капотом (на примере базовой логики селективных моделей):

1. Вычисление новой информации ()

У модели есть скрытая матрица весов W_B, которая одинакова для всего текста и обучается в процессе тренировки. Когда приходит токен x_t (вектор чисел), модель просто умножает его на эту матрицу:

B_t = Linear_B(x_t) = W_B \cdot x_t
# В терминах Torch
Bt=nn.Linear(hidden_dim, hidden_dim)(x)

2. Вычисление коэффициента удержания/забывания ()

Аналогично, у модели есть другая матрица весов W_A. Чтобы значения A_t работали как шлюз и находились в диапазоне от 0 (полностью забыть) до 1 (полностью запомнить), результат линейного преобразования пропускается через нелинейную функцию активации, что-нибудь типа Сигмоиды (\sigma) или Softplus:

A_t = \sigma(Linear_A(x_t)) = \sigma(W_A \cdot x_t)

Если x_t — это слово-разделитель, слой W_A \cdot x_t выдаст большое отрицательное число, и сигмоида превратит его в ноль. Память сброшена.

A_t — коэффициент удержания/забывания, а B_t x_t — новая информация из текущего слова x_t на шаге t.
Формула всё еще рекуррентная, но теперь ее можно развернуть в обычную, потому что множители A_t и B_t это матрицы, вычисляемые по значению x_t .

Всё. Алгоритм параллельного префиксного сканирования позволяет видеокарте вычислять скрытые состояния для сотен тысяч токенов одновременно.

Вот так можно раскрыть рекурре��тную формулу h_t = A_t h_{t-1} + B_t x_t с первого шага:

h_t = B_t x_t + A_t B_{t-1} x_{t-1} + A_t A_{t-1} B_{t-2} x_{t-2} + ...

И это в точности классическая операция одномерной свертки (1D CNN). Только вместо того, чтобы выбирать подходящее жесткое ядро свертки, модель генерирует ядро длиной во весь текст на лету маленькими линейными слоями W_A и W_B прямо из текущего слова x_t. Увидела модель слово «Однако» — слой выдал для A_t ноль. Прошлый конечный h_t сохранился, зафиксировал свою сентенцию, память очистилась для новой мысли. Быстро, дешево, O(N) вместо O(N^2).

Запоминание во время инференса

Авторы концепции Titans пошли еще дальше и превратили генерацию текста в дообучен��е модели. Это, пожалуй, самый крышесносная идея архитектуры.

Как это происходит:

  1. в трансформер приходит новый токен x_t. предобученный механизм внимания проецирует его в пару "Ключ" (k_t) и "Значение" (v_t). Пока всё как обычно

  2. В дополнительном слое линейной RNN, вместо того, чтобы просто прибавить новый вектор к существующему: h_t = A_t h_{t-1} + v_t k_t^\top, Titans выполняет шаг градиентного спуска. Он смотрит, что её текущая память \mathcal{M}_{t-1} выдает по ключу k_t, сравнивает это со значением v_t, полученным из внимания трансформера, вычисляет ошибку (Loss) и обновляет свои веса:

W_t = W_{t-1}-\eta\nabla L(W_{t-1}, k_t, v_t)

(Плюс добавляется механизм импульса/momentum и вентили удержания, чтобы не забыть прошлое).

Градиент здесь играет роль фактора неожиданности слова. Чем это слово сильнее отличается по смыслу от ожидаемого, тем выше вес его запоминания. Как у людей: предсказуемый шаблонный текст не цепляет, и чтобы разбудить аудиторию, лектор вставляет парадоксы или неожиданные метафоры. Эффект обманутого ожидания.

Рекуррентный цикл RNN — это и есть цикл обучения (Test-Time Training) для внутренней памяти. А развернутый в виде ядра одномерной свертки, весь цикл выполняется за один шаг.

Супер. Очень мне нравится. По-программистски красиво, быстро и эффективно с точки зрения вычислительной стоимости.

Локальное сохранение контекста

Модель выводит текст и тут же запоминает его в собственных небольших матрицах весов, отделенных от огромных слоёв трансформера. Огромный k-v-кэш не нужен.
Если одним и тем же ИИ одновременно пользуются 100 человек, в видеопамяти сервера будет лежать одна неизменная копия базовых весов и 100 разных маленьких матриц W_t для каждого чата.

Такой небольшой объем весов вполне можно держать на стороне сервера на протяжении сессии. Теперь не надо гонять текст всего чата через интернет, модель помнит весь ваш разговор. Как только вы закрываете чат, эта матрица (весь динамически накопленный опыт за сессию) удаляется из памяти.

Но никто не мешает сохранить эти веса в файл, если надо будет к ним вернуться. Ну, как отдельная опция для pro-аккаунтов.

Матрица имеет фиксированный размер — условно 50 МБ — и не растет, даже если модель прочитает "Войну и мир". Разве что, запомнит его не совсем дословно, но близко к тексту. Для кода проекта можно регулировать точность запоминания, там "близко к тексту" будет нехорошо, но разные юзер-кейсы и прочие расшифровки в зуме вполне можно обобщать и отжимать там воду до самого скелета.

Представьте сценарий:

  1. Вы запускаете локального агента-разработчика, даете ему документацию и просите написать проект.

  2. В процессе работы формируется матрица (состояние self.memory_mlp).

  3. Вы закрываете чат. Программа сохраняет эти 50 МБ в файл my_project.ai_memory (грубо говоря, torch.save()).

  4. Завтра вы загружаете этот файл в агента. Модель мгновенно «вспоминает» весь вчерашний контекст.

Мощная облачная модель выступает лишь как движок, а файл с личностью и контекстом лежит у вас.

Еще один сценарий из будущего: "гиппокамп" между Агентом и RAG

Обычно автономные ИИ-агенты используют векторые базы RAG как семантическую память предприятия. Здесь LLM работает как человек с памятью золотой рыбки, ему каждую секунду надо сверяться со справочником.

  1. Агент получает вопрос.

  2. Ищет куски текста в RAG.

  3. Вставляет найденный текст прямо в промпт.

  4. Когда промпт переполняется, агент забывает старые выводы. Контекст рвется.

Теперь мы ставим модуль динамического запоминания между агентом и RAG.

  1. Агент запрашивает документацию из RAG.

  2. Модель читает этот документ, и через градиентный спуск впечатывает его смысл в матрицу W_t (свой гиппокамп).

  3. В промпте для ответа теперь нет документов из RAG, модель уже знает как отвечать. И в следующий раз она не полезет в справочник за ответом на этот вопрос.

  4. На 10-й день работы агент всё еще "помнит" логику архитектуры проекта, потому что она уже стала частью его нейронных весов. RAG нужен только для поиска совершенно новых сухих фактов, а понимание контекста проекта живет внутри W_t.

Как это выглядит в PyTorch (псевдокод)

В классическом трансформере на инференсе мы делаем torch.no_grad(). В архитектуре Titans градиенты нужны всегда, но только для внутренней памяти:

import torch
import torch.nn as nn
import torch.optim as optim

class TitansMemoryLayer(nn.Module):
    def __init__(self, hidden_dim):
        super().__init__()
        # Базовые (замороженные) проекции
        self.to_key = nn.Linear(hidden_dim, hidden_dim)
        self.to_value = nn.Linear(hidden_dim, hidden_dim)
        
        # Наша "глубокая память" сессии (MLP)
        self.memory_mlp = nn.Sequential(
            nn.Linear(hidden_dim, hidden_dim * 2),
            nn.GELU(),
            nn.Linear(hidden_dim * 2, hidden_dim)
        )
        # Оптимизатор, который работает ПРЯМО ВО ВРЕМЯ ИНФЕРЕНСА
        self.memory_optimizer = optim.SGD(self.memory_mlp.parameters(), lr=0.01)

    def forward(self, x):
        """ Обработка одного токена (инференс) """
        k_t = self.to_key(x)
        v_t = self.to_value(x)
        
        # 1. Запрос к текущей памяти
        memory_output = self.memory_mlp(k_t)
        
        # 2. Вычисляем удивление (Attentional Bias из статьи MIRAS)
        # Насколько сильно текущее предсказание отличается от реальности?
        loss = torch.nn.functional.mse_loss(memory_output, v_t)
        
        # 3. Шаг Test-Time Training (обучаем память на лету!)
        self.memory_optimizer.zero_grad()
        loss.backward()
        self.memory_optimizer.step()
        
        # Отдаем результат дальше по слоям модели
        return memory_output```

Вместо добавления токена в список, мы сделали loss.backward() для маленькой матрицы memory_mlp. Контекст "впитался" в веса.

Архитектуры типа Titans и современные линейные RNN (фреймворк MIRAS) обещают смену парадигмы. Превращение цикла вывода в цикл онлайн-оптимизации, отказ от хранения контекста в текстовом виде преодолевают ограничения kv-кэша и открывают двери к агентам, которые могут работать месяцами, не теряя нить повествования. Вычислительные затраты моделей резко падают, а ИИ впервые получает настоящую долгосрочную память, которую можно положить в карман. Правда, существующую инфраструктуру агентов, которые гоняют свой state в большом цикле, придется переписывать. Может, поэтому Гугл, где рождаются все эти архитектуры, от LSTM до Titans, не спешит внедрять это в массы. А может, просто привинтил RNN к своей Gemini Deep Think, снизил затраты на поддержку миллионного контекста и поднял интеллектуальность модели до недосягаемых 84.2% по бенчмарку ARC-AGI-2. И пока никому не показал. Они любят выдавать постепенно. Сейчас, без нездоровых сенсаций, вышла gemini-3.1-pro. Просто там решена проблема гниения контекста, просто отвечает быстрее и рассуждает умнее.

Но кодинг всё равно лучше у Anthropic :-)