Когда Илон Маск изобретает радио: проблема временной шизофрении у LLM
Спросите любую локальную LLM: "Кто изобрел радио?" Она уверенно ответит: "Гульельмо Маркони". Спросите: "Кто сейчас CEO Tesla?" - "Илон Маск". А теперь задайте каверзный вопрос: "Как Илон Маск изобрел радио в 1895 году?" И вот тут начинается магия - модель начнет генерировать абсолютно связный, логичный текст о том, как молодой Маск экспериментировал с беспроводной передачей сигналов.
Это не баг. Это фундаментальная проблема архитектуры - temporal grounding, или временное заземление. Модель знает факты, но не знает, к какому времени они относятся. В ее внутреннем представлении "Илон Маск" и "радио" - просто векторы, которые прекрасно сочетаются друг с другом.
В RAG-системах эта проблема проявляется особенно ярко. Вы загружаете свежие новости о выходе iPhone 20, но модель продолжает цитировать устаревшие данные о iPhone 14 из своей тренировочной выборки. Факты из 2022 года смешиваются с фактами 2026-го, создавая временной винегрет.
Почему переобучение не работает (и никогда не будет)
Первая мысль: "Давайте просто дообучим модель на свежих данных!". Отличная идея, если вам нравится катастрофическое забывание. Модель забудет старые факты, чтобы запомнить новые. Или начнет смешивать их в странных пропорциях.
Вторая мысль: "Используем векторные базы с временными метками!". Работает, но только до поры. Когда модель генерирует ответ, она все равно опирается на свои внутренние знания, а не только на контекст из RAG. И эти знания не имеют временных меток.
Проблема глубже, чем кажется. Современные архитектуры вроде Tuneable Attention или техники из DroPE решают другие задачи - ускорение, эффективность, но не временную согласованность.
Acatalepsy: архитектура, которая помнит, когда она что узнала
Название происходит от философского термина "акаталепсия" - признание невозможности полного познания. В нашем контексте - признание того, что модель не может быть уверена в своих знаниях без временного контекста.
Архитектура Acatalepsy строится на трех ключевых компонентах:
1 VINs - Векторные идентификаторы времени
Каждый факт в модели получает не просто эмбеддинг, а эмбеддинг с привязанным временным вектором. VIN - это 128-мерный вектор, который кодирует:
- Временной период (год, квартал, месяц)
- Тип временной информации (статический факт, событие, прогноз)
- Уровень временной стабильности (меняется редко/часто/никогда)
- Источник временной метки (тренировочные данные, RAG, пользовательский ввод)
class TemporalVector:
def __init__(self, timestamp: datetime, stability: float):
self.timestamp = timestamp
self.stability = stability # 0.0-1.0, где 1.0 = факт никогда не меняется
self.vector = self._encode_temporal()
def _encode_temporal(self) -> np.ndarray:
# Кодируем год, месяц, день в циклические признаки
year_norm = np.sin(2 * np.pi * (self.timestamp.year - 2000) / 100)
month_norm = np.cos(2 * np.pi * self.timestamp.month / 12)
# Добавляем стабильность как отдельное измерение
return np.concatenate([
[year_norm, month_norm],
np.random.normal(0, 0.1, 126) * self.stability
])
2 Эпистемический слой доверия
Над стандартными слоями трансформера добавляется дополнительный слой, который оценивает временную согласованность генерируемого контента. Этот слой работает как временной фильтр:
- Анализирует VINs всех активированных фактов в контексте
- Вычисляет временную дисперсию (насколько факты разбросаны по времени)
- Генерирует confidence score для каждого токена в ответе
- При высокой дисперсии добавляет временные квалификаторы ("по состоянию на 2024 год", "согласно данным 2020-х")
Вот как это выглядит в псевдокоде:
class EpistemicLayer(nn.Module):
def forward(self, hidden_states, temporal_vectors):
# hidden_states: [batch, seq_len, hidden_size]
# temporal_vectors: [batch, seq_len, vin_dim]
# Вычисляем временную когерентность
time_coherence = self._compute_coherence(temporal_vectors)
# Для каждого токена определяем доминирующий временной период
dominant_period = self._find_dominant_period(temporal_vectors)
# Модифицируем hidden states на основе временной согласованности
if time_coherence < 0.3: # Низкая согласованность
# Добавляем временные маркеры неопределенности
return self._add_temporal_qualifiers(hidden_states, dominant_period)
else:
return hidden_states
3 Динамический временной гейтинг в RAG
Интеграция с RAG-системами - где Acatalepsy показывает свою настоящую мощь. Вместо простого semantic search мы реализуем temporal-aware retrieval:
| Тип запроса | Стратегия поиска | Пример |
|---|---|---|
| Исторический факт | Ищем только в периоде события | "Кто изобрел телефон в 1876 году?" → ищем документы 1870-1880 гг |
| Текущая информация | Приоритет самым свежим документам | "Кто президент США?" → ищем документы 2025-2026 гг |
| Прогноз/будущее | Ищем аналитику и тренды | "Какие технологии будут в 2030?" → ищем документы 2024-2026 с прогнозами |
Пошаговая реализация: от теории к работающему коду
Теперь самое интересное - как внедрить Acatalepsy в существующую LLM без полного переобучения. Работаем с локальными моделями 2025 года, которые уже поддерживают tool calling.
Шаг 1: Добавляем временные метки в эмбеддинги
Берем предобученную модель (например, Llama 3.2 90B) и модифицируем ее эмбеддинг-слой:
class TemporalEmbedding(nn.Module):
"""Расширяет стандартные эмбеддинги временными векторами"""
def __init__(self, original_embedding, vin_dim=128):
super().__init__()
self.word_embeddings = original_embedding
self.temporal_projection = nn.Linear(vin_dim, original_embedding.embedding_dim)
def forward(self, input_ids, temporal_vectors=None):
word_embeds = self.word_embeddings(input_ids)
if temporal_vectors is not None:
# Проецируем временные векторы в то же пространство
temp_embeds = self.temporal_projection(temporal_vectors)
# Объединяем (можно через сложение или конкатенацию)
return word_embeds + temp_embeds * 0.1 # Весовой коэффициент
return word_embeds
Важно: начинаем с малого весового коэффициента (0.1), чтобы не сломать уже работающие эмбеддинги. В процессе fine-tuning можно увеличивать влияние временных векторов.
Шаг 2: Собираем временные метки для тренировочных данных
Самая сложная часть - разметить когда каждый факт в тренировочных данных был актуален. Используем полуавтоматический подход:
def extract_temporal_context(text):
"""Извлекаем временные метки из текста"""
# 1. Ищем явные даты
date_patterns = [
r'\b\d{1,2}\s+[а-я]+\s+\d{4}\b', # "10 января 2023"
r'\b\d{4}\b', # "2023"
r'\bв\s+\d{4}\s+году\b', # "в 2023 году"
]
# 2. Ищем временные контекстные слова
temporal_indicators = {
'недавно': 'RECENT',
'в прошлом году': 'PAST_YEAR',
'в будущем': 'FUTURE',
'сейчас': 'PRESENT',
'традиционно': 'HISTORIC',
}
# 3. Используем NER для извлечения временных сущностей
# (spaCy, Natasha или аналоги)
# 4. Если явных меток нет, используем дату источника
return default_timestamp
Шаг 3: Fine-tuning с временным контрастным loss
Обычный fine-tuning ломает временные связи. Вместо этого используем контрасттивную функцию потерь, которая штрафует модель за смешивание далеких временных периодов:
class TemporalContrastiveLoss(nn.Module):
def __init__(self, margin=1.0, time_weight=0.3):
super().__init__()
self.margin = margin
self.time_weight = time_weight
def forward(self, embeddings, temporal_vectors, labels):
# Стандартная cross-entropy loss
ce_loss = F.cross_entropy(embeddings, labels)
# Временная контрастная loss
batch_size = embeddings.size(0)
time_diffs = self._compute_time_differences(temporal_vectors)
# Штрафуем за большие временные скачки в одном контексте
temporal_penalty = torch.mean(
F.relu(time_diffs - self.margin)
)
return ce_loss + self.time_weight * temporal_penalty
Интеграция с RAG: временно-осознанный поиск
Обычные RAG-системы ищут по semantic similarity. Нам нужно искать по semantic + temporal similarity. Модифицируем процесс индексации и поиска:
class TemporalRAG:
def __init__(self, vector_store, temporal_index):
self.vector_store = vector_store # Обычное векторное хранилище
self.temporal_index = temporal_index # Временной индекс
def search(self, query, query_time=None, time_tolerance=365):
"""Поиск с учетом временного контекста"""
# 1. Определяем временной контекст запроса
if query_time is None:
query_time = self._infer_time_from_query(query)
# 2. Ищем семантически похожие документы
semantic_results = self.vector_store.search(query, k=50)
# 3. Фильтруем по временной близости
filtered_results = []
for doc, score in semantic_results:
doc_time = self.temporal_index.get_time(doc.id)
time_diff = abs((doc_time - query_time).days)
# Взвешенная оценка: семантика + временная близость
if time_diff <= time_tolerance:
temporal_score = 1.0 - (time_diff / time_tolerance)
combined_score = 0.7 * score + 0.3 * temporal_score
filtered_results.append((doc, combined_score))
# 4. Сортируем по комбинированной оценке
return sorted(filtered_results, key=lambda x: x[1], reverse=True)[:10]
Типичные ошибки и как их избежать
Ошибка 1: Слишком агрессивное временное взвешивание. Если временной компонент доминирует над семантическим, модель начнет выдавать хронологически точный, но семантически бессмысленный бред.
Решение: начинать с коэффициента 0.1-0.2 для временных векторов. Мониторить качество на валидационных примерах вроде "Какие процессоры использовались в iPhone 14 и iPhone 20?". Ответ должен четко разделять временные периоды.
Ошибка 2: Игнорирование временной неопределенности. Не все факты имеют четкие временные метки. "Изобретение колеса" vs "выход iOS 18".
Решение: вводить confidence score для временных меток. Для древних/расплывчатых событий использовать широкие временные диапазоны вместо точных дат.
Ошибка 3: Переобучение на временные паттерны. Модель начинает видеть временные корреляции там, где их нет.
Решение: добавлять негативные примеры в обучение - намеренно перемешанные временные контексты, которые модель должна распознавать как некорректные.
Производительность и overhead
Самый частый вопрос: "Насколько это замедляет модель?". Цифры на 28.01.2026 для Llama 3.2 90B на RTX 4090:
- Без Acatalepsy: 18 токенов/сек
- С Acatalepsy (базовая версия): 16 токенов/сек (~11% overhead)
- С Acatalepsy (оптимизированная): 17.5 токенов/сек (~3% overhead)
Overhead в основном от вычисления временной когерентности и модификации attention механизма. Но есть и плюс - модель генерирует более точные ответы с первого раза, уменьшая необходимость в повторных запросах.
Почему это работает лучше, чем prompt engineering
Можно попробовать решить проблему через промпты: "Учти, что сейчас 2026 год. Отвечай, используя актуальную информацию.". Работает в 60% случаев. Остальные 40% - когда модель нужно заставить не использовать определенные временные периоды.
Acatalepsy работает на архитектурном уровне. Это как разница между:
- "Попросить сотрудника помнить о дедлайне" (prompt engineering)
- "Встроить в его мозг чип, который блокирует упоминание устаревших данных" (архитектурное решение)
Особенно критично для систем, где важна точность следования инструкциям и корректность tool calling.
Что дальше? Временная архитектура как стандарт
К 2027 году я ожидаю, что все серьезные LLM будут иметь встроенную поддержку временного контекста. Потому что без этого:
- Медицинские ИИ будут рекомендовать устаревшие протоколы лечения
- Юридические ассистенты будут цитировать отмененные законы
- Технические консультанты будут советовать deprecated API
- Финансовые аналитики будут использовать данные доковидной эпохи
Acatalepsy - не идеальное решение. Это первый шаг. Следующий - временные attention механизмы, где модель будет явно учитывать временные отношения между сущностями. И да, это потребует переосмысления архитектуры внимания в принципе.
Но начинать нужно сегодня. Потому что завтра ваша LLM может случайно "воскресить" ушедшего CEO или "изобрести" технологию за 50 лет до ее реального появления. И объяснить это пользователю будет значительно сложнее, чем внедрить VINs в эмбеддинг-слой.