中文翻译暂不可用,显示俄语原文。

Что такое streaming LLM для бесконечного контекста (техника rollback)?

Краткий тезис

Streaming LLM — это подход, позволяющий языковой модели обрабатывать последовательности произвольной длины без перезапуска инференса, за счёт вытеснения старых токенов из KV cache и пересчёта позиционных эмбеддингов. Ключевая техника — rollback: при достижении лимита контекста удаляются наименее важные токены, но сохраняется attention sink (первые токены последовательности), что обеспечивает стабильность генерации. Это даёт возможность вести бесконечный диалог с одним вызовом LLM, что критично для Agentic RAG и долгоживущих агентов.


1. Проблема: ограничения стандартных LLM

Стандартные трансформеры имеют фиксированную длину контекста (например, 4K, 8K, 128K токенов). При превышении лимита модель либо обрезает вход (теряя информацию), либо требует дорогостоящего перезапуска с новым контекстом. Основные причины:

  • Квадратичная сложность attention: O(L²) по длине последовательности L.
  • Фиксированные Encoding|позиционные эмбеддинги: большинство моделей (например, GPT, LLaMA) используют RoPE (Position Embedding) или ALiBi (Attention with Linear Biases), которые не рассчитаны на длины, превышающие тренировочные.
  • Рост KV cache: для каждого слоя хранятся ключи и значения всех предыдущих токенов, что линейно растёт с длиной и упирается в память GPU.

Streaming LLM решает эти проблемы не за счёт увеличения контекстного окна, а за счёт динамического управления памятью.


2. Техника rollback: базовый принцип

Rollback — это стратегия, при которой при заполнении контекстного окна (например, 4K токенов) мы:

  1. Удаляем из KV cache часть старых токенов (обычно самые ранние, кроме attention sink).
  2. Пересчитываем позиционные индексы для оставшихся токенов, чтобы они соответствовали новому началу последовательности.
  3. Продолжаем генерацию, используя обновлённый кэш.

Важно: модель не перезапускается — инференс идёт непрерывно. Это напоминает скользящее окно (sliding window), но с дополнительным механизмом сохранения критически важных токенов.

Псевдокод rollback

def streaming_generate(model, prompt, max_new_tokens, window_size=4096):
    # Инициализация KV cache
    kv_cache = model.forward(prompt)
    generated = prompt
    
    for _ in range(max_new_tokens):
        if len(generated) > window_size:
            # Определяем, какие токены удалить
            # Сохраняем attention sink (первые 4 токена) и последние N токенов
            sink_len = 4
            keep_len = window_size - sink_len
            # Новый контекст: sink + последние keep_len токенов
            new_context = generated[:sink_len] + generated[-keep_len:]
            # Пересчитываем позиции (например, сбрасываем на 0 для sink)
            # Обновляем KV cache: удаляем старые записи, переставляем
            kv_cache = update_kv_cache(kv_cache, new_context)
            generated = new_context
        
        # Генерация следующего токена
        next_token = model.generate_next(generated, kv_cache)
        generated.append(next_token)
    
    return generated

3. Attention sink: почему первые токены неприкосновенны

Attention sink — это эмпирический феномен, обнаруженный в работе "Efficient Streaming Language Models with Attention Sinks" (2023). Оказалось, что первые токены последовательности (особенно начальные токены промпта) получают непропорционально большое внимание от всех последующих токенов. Если их удалить, модель резко теряет качество генерации.

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

Поэтому при rollback первые 4–8 токенов всегда сохраняются (их называют sink tokens). Остальные старые токены вытесняются по одному из следующих критериев:

  • FIFO (First-In-First-Out): самые старые токены удаляются первыми.
  • Важность (importance scoring): оценивается вклад каждого токена в последующие предсказания (например, через градиенты или attention scores).
  • Случайное вытеснение: редко используется из-за нестабильности.

4. KV cache management: eviction policies

Управление KV cache — ключевая инженерная задача. При rollback мы не просто удаляем токены, но и физически освобождаем память, занимаемую их ключами и значениями. Основные стратегии:

ПолитикаОписаниеПлюсыМинусы
Sliding windowХраним последние M токенов + sinkПростота, низкие накладные расходыПотеря дальних зависимостей
Heavy Hitter (H2O)Оставляем токены с наибольшим attention scoreСохраняет важные деталиДополнительные вычисления для оценки важности
ScissorhandsКомбинация sink + последние токены + случайные старыеБаланс между простотой и качествомЭвристический подход

Для streaming LLM чаще всего используется sliding window + attention sink как самый надёжный и быстрый вариант.


5. Пересчёт позиционных эмбеддингов

После rollback позиции токенов меняются. Если модель использует RoPE, то позиционные кодировки встроены в вычисление attention через вращение. При удалении части токенов нужно пересчитать позиции для оставшихся:

  • Sink tokens получают позиции 0, 1, 2, 3 (как в начале).
  • Последние токены получают позиции, начиная с 4 (или с учётом длины sink).

Для ALiBi (используется в MPT, Bloom) позиционные смещения линейны, и их можно просто пересчитать, сдвинув начало.

Важно: модель должна быть обучена с поддержкой таких пересчётов, иначе качество упадёт. Streaming LLM обычно дообучают на задачах с длинными последовательностями, используя технику windowed attention и attention sink.


6. Сравнение с другими подходами к длинному контексту

ПодходМеханизмДлина контекстаНеобходимость дообученияПрименимость в реальном времени
Streaming LLM (rollback)Вытеснение старых токенов + sinkНеограниченнаяДа (fine-tuning на длинных текстах)Да (один вызов модели)
LongLoRA / YaRNРасширение позиционных эмбеддинговДо 256K–1MДа (LoRA-адаптация)Нет (требуется полный контекст)
RAGВнешняя база знанийОграничена окном LLMНетДа, но latency выше
Memory-augmented LLMВнешняя память (например, MemGPT)НеограниченнаяДа (специальная архитектура)Да, но сложнее

Streaming LLM выигрывает в сценариях, где важна непрерывность и низкая задержка (чат-боты, агенты), но проигрывает в задачах, требующих точного доступа к любому фрагменту прошлого (юридические документы, код).


7. Практическая реализация: пример с Hugging Face

Библиотека transformers поддерживает streaming через параметр use_cache=True и кастомные cache implementations. Пример для модели LLaMA:

from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")

# Включаем streaming cache (кастомная реализация)
from transformers.cache_utils import DynamicCache

class StreamingCache(DynamicCache):
    def __init__(self, window_size=4096, sink_size=4):
        super().__init__()
        self.window_size = window_size
        self.sink_size = sink_size
    
    def evict(self):
        # Удаляем все, кроме sink и последних токенов
        total = self.key_cache[0].shape[2]  # длина последовательности
        if total > self.window_size:
            keep = self.sink_size + self.window_size - self.sink_size
            # Обрезаем key_cache и value_cache для всех слоёв
            for layer in range(len(self.key_cache)):
                self.key_cache[layer] = torch.cat([
                    self.key_cache[layer][:, :, :self.sink_size, :],
                    self.key_cache[layer][:, :, -keep:, :]
                ], dim=2)
                self.value_cache[layer] = torch.cat([
                    self.value_cache[layer][:, :, :self.sink_size, :],
                    self.value_cache[layer][:, :, -keep:, :]
                ], dim=2)
            # Пересчёт позиций (упрощённо: сбрасываем счётчик)
            # В реальности нужно обновить position_ids в model.forward

# Использование
cache = StreamingCache()
with torch.no_grad():
    outputs = model.generate(
        input_ids,
        max_new_tokens=1000,
        past_key_values=cache,
        use_cache=True
    )

На практике для production используют оптимизированные библиотеки: vLLM, TensorRT-LLM, llama.cpp — они уже содержат встроенную поддержку sliding window и attention sink.


8. Преимущества и недостатки streaming LLM

Преимущества

  • Бесконечный диалог без перезапуска модели.
  • Низкая задержка (нет повторного прогона всего контекста).
  • Эффективное использование памяти GPU (фиксированный размер KV cache).
  • Совместимость с существующими архитектурами (достаточно fine-tuning).

Недостатки

  • Потеря информации о старых токенах (кроме sink).
  • Необходимость дообучения модели для стабильной работы.
  • Сложность пересчёта позиций для некоторых эмбеддингов (например, абсолютных).
  • Риск "забывания" важных деталей из начала диалога.

9. Применение в Agentic RAG

В Agentic RAG агент может выполнять множество шагов: поиск в базе знаний, вызов инструментов, генерация ответов. Каждый шаг добавляет токены в контекст. Без streaming LLM агент быстро упрётся в лимит окна и будет вынужден либо перезапускаться (теряя историю), либо использовать RAG для подкачки релевантных фрагментов.

Streaming LLM позволяет агенту:

  • Поддерживать непрерывную историю взаимодействия.
  • Хранить в контексте последние N шагов (например, 10 последних действий).
  • Сохранять attention sink (начальный промпт с системными инструкциями) для стабильности поведения.

Пример: агент, который анализирует лог событий в реальном времени. Он может обрабатывать миллионы токенов, постепенно вытесняя старые записи, но всегда помнить начальные инструкции.


10. Пет-проект для закрепления

Задача Реализовать простой чат-бот с бесконечной памятью на основе streaming LLM.

Инструменты Python, Hugging Face Transformers, библиотека streaming-llm (или кастомная реализация).

Шаги:

  1. Выберите небольшую модель (например, TinyLlama-1.1B-Chat-v1.0).
  2. Реализуйте класс StreamingCache с политикой sliding window + attention sink.
  3. Напишите функцию chat_stream, которая принимает историю сообщений, добавляет новый запрос, генерирует ответ и обновляет кэш.
  4. Добавьте логирование: выводите размер кэша и количество вытесненных токенов.
  5. Протестируйте на длинном диалоге (50+ сообщений) и сравните качество с обычным подходом (перезапуск каждые 4K токенов).

Ожидаемый результат Чат-бот, который может вести бесконечный диалог без потери контекста последних 10–20 сообщений, при этом потребляя фиксированный объём памяти.


11. Связь с другими вопросами

ВопросТема
641Что такое Agentic RAG и как он отличается от классического RAG?
642Как агент управляет памятью (краткосрочной и долгосрочной)?
643Какие стратегии долгосрочной памяти существуют для агентов?
644Как агент выбирает и вызывает инструменты?
645Как агент планирует последовательность действий?
646Как агент наблюдает за результатами своих действий?

12. Навигация


Навигация