Skip to content
realbtr edited this page Nov 1, 2026 · 3 revisions

Когда кот садится на подоконник: архитектура непрерывного внимания

Редакционное уточнение · 1 ноября 2026 года. Лонгрид сохраняет исходный замысел и исторические примеры. Приоритет проекта: концепция → архитектура → реализация. Компоненты стенда не равны доказанной сквозной способности: AC0–AC4/M0 ещё не пройдены. Демо «маша/волк», числа 4/5 и 0.46/0.92 — исторические апрельские примеры; в этой редакции они не воспроизводились и не являются новым benchmark. Текущая цель: NORTH_STAR, контракт: SPEC_FINAL, решения: JOURNAL, З-0096. Разбор актуализации. Глава «Кот встаёт» добавлена 1 ноября 2026 года; источник и правки.

Лонгрид о том, что такое смысл с точки зрения кода, почему мы начали не с нейросети, и что происходит, когда система начинает удивляться

Представьте, что вы сидите в тихой комнате и читаете. Краем глаза замечаете движение — и ещё до того, как сознание успело оформить слово «кот», вы уже знаете, что это именно он, а не занавеска. Больше того: вы с высокой вероятностью знаете, что он сейчас запрыгнет на подоконник, а не ляжет на пол.

Вы не искали в базе данных. Не сравнивали с эталонными изображениями. Не запускали классификатор. Вы просто ожидали.

Вот этот механизм ожидания — не результат, а именно механизм, лежащий в его основе — и есть то, чем мы занимаемся последние несколько лет.


Мы попробовали воспроизвести его в коде. Система обучалась несколько минут на детских сказках — никаких GPU, никаких эмбеддингов, никакого размеченного датасета. Потом мы показали ей текст, которого она раньше не видела:

Текст: «маша пришла в лес встретила волка поздоровались»

Токен           Что система ожидала        Результат
──────────────  ─────────────────────────  ──────────────────────────────────
«пришла»        — (первый токен, пусто)    ~ нет предсказания
«в»             «в»  conf=1.00             ✓ угадала
«лес»           «лес»  conf=0.94           ✓ угадала
«встретила»     «встретила»  conf=1.00     ✓ угадала
«волка»         «медведя»  conf=0.89       ↔ ждала «медведя», получила «волка»
«поздоровались» «поздоровались»  conf=1.00 ✓ угадала

«Медведя» — потому что в обучающих текстах маша встречала медведя. «Волка» — потому что это тест на незнакомый вариант. Система угадала 4 из 5, точно определила место сюрприза, и не растерялась.

В историческом демо приведены дивергенции 0.46 для близких текстов и 0.92 для другой тематики. Это иллюстрация предполагаемого сигнала различения, а не проверка того, что система «знает границы своего знания». Для такого вывода нужны воспроизводимый протокол, контроль и проверка калибровки.

Дальше в статье — как это устроено изнутри.


Первый вопрос, который возникает: зачем строить это, если есть GPT-4?

Мы исследуем онлайн-накопление и применение структур, которые можно проследить до конкретных наблюдений и проверок. Нас интересует не только предсказание продолжения, но и выбор действия с последующим внешним исходом. Это описание исследовательского направления; преимущества перед другими подходами требуют отдельного сопоставимого опыта.

Ребёнок, которому полтора года, уже умеет предсказывать. Он тянет руку за мячом до того как тот упадёт со стола — потому что накопил достаточно опыта, чтобы понять: за этим движением следует это. Он обобщил. И это обобщение работает онлайн, в реальном времени, без GPU. Нам интересно именно это.


Примитив: что такое «признак»

Любая система распознавания нуждается в атомарном примитиве — чём-то, из чего строится всё остальное. В нейросети это числа в векторе активации. В классических системах — символы или фреймы. У нас это — признак.

Признак — это не объект и не категория. Признак — это направленный переход между двумя областями внимания.

 Область A                  Область B
 ┌──────────────┐           ┌──────────────┐
 │ size   = 0.3 │           │ size   = 0.6 │
 │ bright = 0.2 │──────────►│ bright = 0.8 │
 │ contr  = 0.4 │  δ-вектор │ contr  = 0.1 │
 │ hue    = 0.5 │           │ hue    = 0.5 │
 └──────────────┘           └──────────────┘
         │
         ▼
  FeatureVector:
  Δsize=+0.30  Δbright=+0.60  Δcontrast=−0.30
  Δhue=0.00   Δsat=0.00       Δfractal=+0.10

Когда вы переводите взгляд с тёмного угла комнаты на светлое окно — это переход. У него есть направление, изменение яркости, изменение контраста, изменение «текстурной сложности» (что-то вроде фрактальной размерности). Все эти изменения — относительные, не абсолютные.

В коде это выглядит так:

@dataclass
class FeatureVector:
    delta_size:        float  # изменение размера области
    delta_brightness:  float  # изменение яркости
    delta_contrast:    float  # изменение контраста
    delta_color_hue:   float  # сдвиг оттенка
    delta_color_sat:   float  # изменение насыщенности
    delta_fractal_dim: float  # изменение сложности текстуры

Шесть чисел. Всё остальное строится из них.

Почему это интересно? Относительное кодирование позволяет искать похожие переходы при разных абсолютных характеристиках областей. Но устойчивость к конкретным преобразованиям требует проверки: формула нормированной разности не гарантирует произвольную аффинную инвариантность. «Кот» здесь — образ обобщения последовательности переходов, а не продемонстрированное зрительное распознавание. Сам примитив ещё не доказывает перенос до нового действия.


В 1968 году Ноам Хомский вместе с Моррисом Халле опубликовал «The Sound Pattern of English» — фундаментальный труд по фонологии. Главная идея: звуки языка не атомарны. Каждый звук — это пучок дистинктивных признаков (distinctive features): [±звонкий], [±взрывной], [±носовой], [±непрерывный]. «П» и «Б» отличаются единственным признаком — [±звонкий]. Именно это минимальное различие несёт смысл, а не «Б сам по себе».

Смысл и структура рождаются из оппозиций, а не из объектов.

Наш FeatureVector — это прямое развитие этой идеи, вынесенное за пределы лингвистики. Мы берём минимальные различия между состояниями — не между звуками, а между любыми областями внимания — и делаем их модально-независимым атомом познания. Δbrightness между двумя зонами зрительного поля работает по той же логике, что [±звонкий] между двумя фонемами: не объект важен, а перепад, граница, переход.

Хомского часто сводят к «Универсальной грамматике» как жёсткому набору правил. Но фундамент глубже: сложность языка кристаллизуется из малого числа бинарных оппозиций. Мы просто спрашиваем: а что если это справедливо не только для звуков?


Обобщение: цепочка подтверждённых переходов

Если один переход — признак, то обобщение (Gen) — это цепочка признаков, которая подтверждалась достаточно часто, чтобы стать значимой.

@dataclass
class Generalization:
    gen_id:        str         # уникальный ID
    feature_chain: List[str]  # последовательность feature_id
    success_count: int         # сколько раз подтверждалась
    stop_count:    int         # сколько раз обрывалась

    @property
    def significance(self) -> float:
        total = self.success_count + self.stop_count
        return self.success_count / total if total > 0 else 0.0

Значимость — это просто отношение подтверждений к попыткам. Ничего магического. Gen «кот садится на подоконник» — это цепочка признаков, которая начинается с «движение тёмного пятна влево-вверх», продолжается «скачок контраста», заканчивается «горизонтальная поверхность + свет снаружи», и она подтверждалась 47 раз из 50 попыток. Значимость = 0.94.

Структура базы обобщений — гетерархия, не иерархия. Это принципиальный момент. Gen «кот» не главнее Gen «движение серого пятна». Они связаны — один является контекстом для другого, — но нет привилегированной вершины, которая всеми командует. Каждый Gen одновременно и «мастер» (обобщает что-то ниже), и «ученик» (может быть частью чего-то выше).

Символьный драйвер: Gen как вероятностное три

Мы реализовали символьный переходный драйвер (CharTransitionDriver), который кодирует текст не как пары слов, а как переходы между отдельными буквами. Каждый токен = переход char_i → char_{i+1}:

 Текст: «кот»                Текст: «кошка»

  к──►о──►т                  к──►о──►ш──►к──►а
  (2 токена)                 (4 токена)

  Общий префикс к→о:
  FeatureVector идентичен в обоих словах!

Gen над символьными переходами естественно образуют вероятностное три:

              [к]  (любое слово на «к»)
               │
             [к→о]  sig=0.88   ← общий корень
            /      \
       [к→о→т]    [к→о→ш]     ← расхождение здесь
        sig=0.91    sig=0.74
                   /    \
            [к→о→ш→к]  [к→о→ш→е]
             sig=0.73    sig=0.61

«Кот» и «кошка» разделяют один Gen-родитель к→о — морфологическое сходство возникает автоматически, без разметки. Длинные цепочки — конкретные слова. Короткие — высокочастотные звуковые паттерны. Динамическая разрешающая способность встроена в архитектуру.


Платоновские Идеи (Эйдосы) в популярном изложении выглядят карикатурно: идеальный стол на небесах, идеальный треугольник на небесах. Ничего более далёкого от смысла, который Платон вкладывал в «Государстве» и «Федоне».

Если читать Платона внимательно, Эйдос — это чистый инвариант. То, что остаётся неизменным, когда убраны все случайные свойства конкретного экземпляра. «Красота» не как конкретная красивая вещь, а как структура отношений, которая делает вещь красивой независимо от материала, размера, культуры.

В «Государстве» есть знаменитый миф о пещере: узники видят только тени на стене — проекции реальных объектов. Они принимают тени за реальность и пытаются предсказывать их последовательность. Выход из пещеры — это движение от теней к инвариантам, от шума к структуре.

Это идеальное описание того, что делает наш AttractorEngine. Токены — это тени. Система не запоминает тени. Она ищет то, что остаётся стабильным сквозь вариации — аттрактор, который проступает через поток расхождений.


Алгоритм: три строчки, которые делают всё

Главный алгоритм — то, что мы называем «разрешением неопределённости» — умещается в три строки:

1. Ожидай следующий признак из активного контекста (Gen)
2. Пришёл ожидаемый признак  → CONFIRMED: переходим к следующему в цепочке
   Дошли до конца цепочки    → COMPLETED: паттерн воспроизведён целиком
3. Пришёл другой признак     → REFUTED:   создаём новый Gen с расхождением как ключом
4. Контекст пуст             → EXPLORING: ищем слабо обобщённые цепочки

CONFIRMED и COMPLETED — это рост значимости. REFUTED — это не ошибка, а точка роста: здесь есть развилка, которую система ещё не видела. Новый Gen рождается именно из расхождения. EXPLORING — это режим любопытства: когда нечего ожидать, система ищет что заслуживает внимания.

Ребёнок, который впервые видит кошку с длинными ушами — и называет её «кися» — получает REFUTED. Потому что «кися» предсказывала короткие уши. Из этого расхождения рождается «зайка».


Как база обобщений накапливается сейчас: эксперимент с поэмой

Мы взяли стихотворение Маршака «Дом, который построил Джек». Оно идеально для теста: рекурсивная кумулятивная структура, фиксированный якорь («построил Джек»), повторяющиеся переходы на всех уровнях вложенности.

1000 циклов. 8 стихов, 125 итераций. Якорь действительно стал сильнейшим. При предсказании «построил джек» — match_rate = 1.00.

Но параллельно произошло кое-что неожиданное: к концу 1000 циклов в базе было 14 160 Gen. Экспоненциальный рост там, где должно быть уплотнение.

В описанном раннем прогоне Gen создавался при первом расхождении без испытательного срока. Позже реализован ProvisionalBase: Gen уже находится в основной базе, а отдельный трекер учитывает подтверждения и возраст. Достаточные подтверждения снимают испытательный статус; неподтверждённый кандидат может быть вытеснен по возрасту.

CapacityBudget ограничивает число Gen, а механизмы сна/replay участвуют в консолидации. Наличие этих компонентов не доказывает ограниченный рост памяти в непрерывной жизни; это требует отдельного измерения.


В 2004 году Джефф Хокинз в книге «On Intelligence» сформулировал теорию HTM (Hierarchical Temporal Memory): мозг — это машина предсказаний. Каждый уровень коры постоянно генерирует ожидание следующего состояния. Если ожидание совпадает — сигнал «съедается» и не передаётся выше. Если не совпадает — ошибка предсказания поднимается вверх.

Наш подход похож по духу: мы тоже строим предсказывающую систему снизу вверх, с онлайн-обучением, без backpropagation. Разница — в примитиве. Там SDR и временны́е паттерны. У нас — направленные переходы как инварианты.


Гетерархия: когда два обобщения расходятся

Один из самых интересных результатов работы HierarchicalConsolidator — автоматическое обнаружение развилок.

meta_L1 [«построил»] sig=0.97
   ├── Gen_A:  построил→джек          sig=0.95
   └── Gen_B:  построил→маша (аномалия, sig=0.12)

meta_L2 [«который→построил»] sig=0.94
   ├── Gen_C:  который→построил→джек
   └── Gen_D:  который→построил→[...]

Система сама находит, что «построил + что-то» — устойчивый паттерн с несколькими вариантами продолжения, и создаёт над ними обобщение. После прогона 1000 циклов поэмы создалось 5 мета-Gen со средней значимостью 0.97.


Буддийская философия Абхидхармы предлагает радикальную онтологию: никаких монолитных объектов не существует. Есть только непрерывный поток мельчайших вспышек опыта — дхарм, — каждая из которых возникает обусловленно из предыдущей и немедленно угасает.

В 1984 году Дерек Парфит в «Reasons and Persons» пришёл к тому же выводу: нет «Я» как отдельной сущности. Есть только Bundle (пучок) — серия ментальных событий, связанных причинно-следственными отношениями.

В нашей системе «кот» не хранится как запись с полями. gen_id: "a3f1c2" — это удобный ярлык для цепочки признаков с высоким significance. Аттрактор и есть Bundle по Парфиту: не сущность, а устойчивый паттерн в потоке изменений.


Ресурс и гедоника: почему система должна скучать

Чистый предиктивный механизм имеет один изъян: если система слишком хорошо предсказывает, она перестаёт обновляться.

ResourceDriver — аналог метаболизма. Уровень ресурса падает при ошибках (REFUTED) и восстанавливается при достижении целей (COMPLETED). Система переключается между режимами REST → CONSERVE → NORMAL → EXPLORE → ABUNDANT.

HedonicAdapter — гедонический распад. Каждый раз, когда Gen полностью проигрывается (COMPLETED), его «новизна» падает:

novelty = decay^count  # decay=0.75 по умолчанию
# count=0  → novelty=1.00  (первый раз — интересно!)
# count=5  → novelty=0.24  (уже скучновато)
# count=20 → novelty≈0.003 (совсем не интересно)

Reward за активацию Gen умножается на новизну: повторение снижает этот сигнал. Механизм используется как инженерная аналогия привыкания; он не является установленной моделью дофаминовой системы мозга.


Обучение: от хаоса к структуре

Учитель не передаёт данные. Учитель создаёт давление.

Три уровня обучения:

Curriculum Agent — управляет тем, какие тексты подаются. При высокой стабильности (0.8–1.0) — знакомые тексты, укрепление. При низкой — незнакомые, исследование. Аналог «контролируемого отжига» из физики.

LLM Teacher — асинхронный учитель через Anthropic API. Видит нарративный отчёт о состоянии системы и принимает одно из трёх решений:

  • TEACH — подать новый текст
  • REWARD — вознаградить систему
  • WAIT — не мешать (часто самое умное решение)

BackgroundRunner — непрерывный цикл, который объединяет всё вместе: подаёт тексты, вызывает SleepDriver по расписанию, переключает домены, пишет нарратив через SpeechDriver и собирает статистику ModelVoice — всё это в одном потоке, без остановки.

DebugSession + Inspector — инструменты для понимания:

session.predict("вот дом который построил джек")
# → match_rate: 1.00, outcomes: CONFIRMED×7

session.attention()
# → {gen_id: "a3f1...", label: "якорь_джек", sig: 0.97, chain_len: 3}

Каждый текст — пучок идей. Трансцендентные Gen и слепые зоны

Когда мы обучили систему на сказке «Теремок», внутри базы обобщений проявилась структура, которую мы не ожидали.

Среди 563 Gen после 20 итераций — 9 трансцендентных обобщений: цепочка длиной 1–2, значимость ≈ 0.99. Они постоянно активны в контексте. Это не абстрактные слова. Это идеи. Gen с chain_len=1 и sig=1.0 — это буквально «что-то происходит». Фундамент мировоззрения.

Проблема: трансцендентный Gen «поглощает» детали

Мы провели эксперимент: обучили на Матрёшке и Ритме, затем предъявили незнакомые Нарративы:

avg match_rate (незнакомые тексты) = 0.347
knock_candidates (urgency ≥ 2.0):  204

Трансцендентный Gen «что-то происходит» с sig=1.0 подтверждается на первом токене любого нарратива — и система успокаивается. Детальные переходы «поссорился → помирился» остаются невидимыми. Система видит лес — и не замечает деревьев.

Частичный ответ — TimescaleScheduler: Gen разделяются на три пула по длине цепочки — долгосрочные (chain=1-2), средние (3-4) и оперативные (5+). Трансцендентные Gen живут в долгосрочном пуле и не вытесняют оперативные детальные паттерны. Проблема не решена полностью, но стала управляемой.


Голос внимания: двухканальное обучение

До сих пор система обрабатывала текст молча. Учитель мог видеть числа — significance, urgency, match_rate — но не видел, что именно система замечала в каждом токене.

Мы добавили системный речевой драйвер (SpeechDriver) — компонент, который сопровождает каждый шаг внимания текстовой интерпретацией:

 ┌─────────────────────────────────────────────────────┐
 │  Текст: «маша пошла в лес и встретила медведя»       │
 └──────────────────────┬──────────────────────────────┘
                        │
                process_token()
                        │
         ┌──────────────▼─────────────────┐
         │       КАНАЛ A  (молчаливый)     │
         │  Gen_a3f1 → CONFIRMED           │
         │  Gen_b22c → REFUTED             │
         │  Gen_c41d → COMPLETED           │
         └──────────────┬─────────────────┘
                        │
               SpeechDriver.observe()
                        │
         ┌──────────────▼─────────────────┐
         │       КАНАЛ B  (речевой)        │
         │  ✓ "Ожидала: маша→лес"          │
         │  ✗ "Удивилась: медведь"          │
         │  ★ "Паттерн завершён"            │
         └─────────────────────────────────┘
              Канал B не влияет на A

Обработка одного предложения превращается в рассказ:

Текст: «маша пошла в лес и встретила медведя»

[маша]      Известный якорь (sig=0.85). Вопрос: что делает маша?
[пошла]     ✓ маша→движение (sig=0.72). Куда движется?
[в]         Ищет продолжение движения...
[лес]       ✓ маша→лес (sig=0.44). Паттерн «встреча» вероятен (3/5).
[и]         Разделитель. Ожидает: новое действие.
[встретила] ✓ встреча-глагол (sig=0.61). Кого встретила?
[медведя]   ✗ Ожидала: зайца (sig=0.38). Получила: медведь.
            → Новый Gen: маша→медведь (добавлен).

Итог: 6/7 ≈ 0.86. Удивление: медведь вместо зайца.

Это и есть наблюдатель за вниманием. Он ничего не меняет в ядре — только переводит то, что происходит в канале A, на человеческий язык.

Канал B_модель: система говорит сама

Интерпретатор описывает прошлое — что случилось после токена. Но можно поставить вопрос иначе: а что система ожидает до того, как токен пришёл?

Мы добавили второй речевой канал — ModelVoice. Он работает на шаг раньше: смотрит на активные Gen, строит предсказание следующего слова из их цепочек, и только потом приходит реальный токен.

Канал A:               внимание (молчаливый, всегда)
Канал B_интерпретатор: что говорит SpeechDriver об A  — ПОСЛЕ токена
Канал B_модель:        что говорит ModelVoice из Gen  — ДО токена

Дивергенция = |ожидание − реальность| → тренировочный сигнал

Вот как это выглядит в живом прогоне (8 проходов по 10 сказочным текстам, тест на незнакомых вариантах):

Токен           B_модель (до)              B_интерпр (после)    Δ
──────────────  ─────────────────────────  ───────────────────  ────────────────────────
«в»             «в» conf=1.00              ★ Паттерн завершён   ✓ Угадала «в»
«лес»           «лес» conf=0.94            ★ Паттерн завершён   ✓ Угадала «лес»
«встретила»     «встретила» conf=1.00      ★ Паттерн завершён   ✓ Угадала «встретила»
«волка»         «медведя» conf=0.89        ✗ Удивилась: «волка» ↔ div=1.00
«поздоровались» «поздоровались» conf=1.00  ★ Паттерн завершён   ✓ Угадала «поздоровались»

Итог по двум типам текстов:

Знакомые тексты (варианты обучающих): avg_div = 0.46  ← система знает что ждать
Незнакомые тексты (корабли, код):     avg_div = 0.92  ← каждый токен — сюрприз
Разница Δ:                                      +0.46  ← рабочий тренировочный сигнал

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

Предсказание строится из трёх источников, которые объединяются по весу:

  • Label-based: если Gen имеет читаемую метку «маша·пошла·в», берём слово на текущей позиции
  • Feature-based: накопленный маппинг feature_id → [слова] из истории обучения
  • Bigram-fallback: слово_i → слово_{i+1}, критически важен когда контекст пуст

Когда система спит

Гедонический распад — это хорошо, но он решает только половину проблемы. Система перестаёт получать удовольствие от повторений. Но куда деваются все Gen, накопленные за день?

У человека есть ответ на этот вопрос: сон. Точнее — фазы сна, во время которых мозг не отдыхает, а занят консолидацией: проигрывает события дня, ищет общее между несвязанными эпизодами, укрепляет важные связи и ослабляет случайный шум.

Мы реализовали аналог этого механизма — SleepDriver:

Обычная работа:         накопление Gen, рост urgency, дивергенция
                        ↓ (каждые N переходов, если urgency накоплен)
Фаза сна:
  1. ReplayEngine        — «галлюцинирует» прошлые события из лога
  2. CrossDomainDetector — ищет паттерны между разными доменами
                           (лес + море + город → общий Gen «движение к объекту»)
  3. HierarchicalConsolidator — создаёт мета-Gen над общими префиксами
                        ↑
Пробуждение:            мета-Gen зафиксированы, urgency снижен

Ключевое: во время сна система не принимает новых токенов. Она работает только с тем, что уже есть — проигрывает внутренние события, ищет повторяющиеся паттерны, и если находит — фиксирует их как новый Gen с общим предком.

На практике: после 50 000 переходов по четырём доменам (лес, море, горы, город) фаза сна создаёт мета-Gen, которые объединяют паттерны из разных контекстов. «Движение к чему-то» работает и в лесу, и в море — это не метафора, это Gen с chain=1 и sig→0.9.

Сон также выполняет роль «мусорщика»: через CapacityBudget база ограничена максимальным числом Gen. Если база переполнена — наименее значимые Gen вытесняются. Система не просто накапливает — она забывает ненужное, как любая биологическая система с ограниченной памятью.


Поворот головы: контекст за горизонтом восприятия

У каждого драйвера есть горизонт восприятия — то, что сейчас находится в его буфере. Текст, прочитанный пять минут назад, уже не виден. Это нормально: человек тоже не видит, что у него за спиной.

Но иногда система встречает незнакомый паттерн — и ей нужно «повернуть голову». Вернуться к тексту, который был источником обучения. Человек может включить свет, открыть дверь, посветить фонариком. Модель — сформулировать запрос к учителю.

Мы реализовали этот механизм через ContextBroker:

                  ГОРИЗОНТ ВОСПРИЯТИЯ
 ══════════════╦══════════════════════════════
 ВНЕ БУФЕРА   ║  В БУФЕРЕ ДРАЙВЕРА
               ║
 текст₁        ║  «мальчик нашёл ключ...»
 текст₂        ║       ↓ process_token()
 текст₃        ║  ❓ [REFUTED×3] «дверь»
               ║     после «нашёл», «ключ»
 ══════════════╩══════════════════════════════
                        │
          SpeechDriver.detect_context_gap()
                        │
             ContextQuery:
             "Несколько раз ошиблась на
              «дверь» (ожидала: auto_724).
              Контекст был: «нашёл», «ключ».
              Напомни исходный текст."
                        │
          TeachingLog.push_query()
                        │
          LLM (следующий тик):
          ┌──────────────────────────────┐
          │  ═══ ЗАПРОСЫ НА КОНТЕКСТ ═══  │
          │  [REFUTED] «дверь» gap=0.33   │
          │  → Несколько раз ошиблась...  │
          │  ═══ ТВОЯ ЗАДАЧА ═══          │
          │  TEACH — верни релевантный    │
          │         контекст (6-20 слов)  │
          └──────────────────────────────┘
                        │
          TEACH: "мальчик нашёл ключ
                  открыл дверь вошёл внутрь"
                        │
          driver.load_buffer(text)
                        │
                   GAP = 0 ✓

Как работает детекция разрыва

SpeechDriver следит за потоком наблюдений и сигнализирует о разрыве при двух условиях:

  • EXPLORING при lead_sig < 0.25 — система совсем не знает что делать
  • REFUTED ≥ 3 подряд — поток ошибок превышает порог

Когда разрыв обнаружен, SpeechDriver формулирует запрос на человеческом языке из контекста последних наблюдений — не из gen_id, а из слов, которые видела система:

# Запрос формируется из реальных token labels
query_text = "Несколько раз ошиблась на «дверь»
              (ожидала: auto_724).
              Контекст был: «нашёл», «ключ».
              Напомни исходный текст."

Живой эксперимент

Мы обучили систему на 15 текстах (10 проходов), затем подали незнакомый текст:

«мальчик нашёл ключ открыл дверь вошёл внутрь»

[COMP] ★ Паттерн завершён: auto_724, auto_68, auto_33.
[REFU] ✗ Удивилась: «открыл». Ожидала: auto_724.
[REFU] ✗ Удивилась: «дверь».  Ожидала: auto_724.
[GAP ] Несколько раз ошиблась на «дверь»
       (ожидала: auto_724). Контекст был: «нашёл», «ключ».
       Напомни исходный текст.

Запрос ушёл в TeachingLog. LLM увидел в промпте секцию с запросами и ответил:

{
  "action": "TEACH",
  "text": "Кот залез на крышу. Соседи удивились и позвонили пожарным.",
  "reason": "Модель систематически ошибается на 4 связанных токенах
             (удивились, позвонили, пожарным, дверь) с контекстом
             'залез на крышу'. Предоставляю связный микротекст,
             который восстанавливает нарушенный контекст."
}

После обучения на одном этом тексте:

  • База: 364 → 391 Gen (+27)
  • Повторный тест — GAP = 0 на обоих предложениях
  • «дверь» — [CONF] Подтверждает: «дверь» ← auto_113 ✓

Три уровня восстановления

ContextBroker.handle_query(query)
         │
         ├── [Tier 1] SourceTextRegistry: видели этот текст раньше?
         │     │
         │     └── Да → driver.load_buffer(text)   ← мгновенно
         │
         ├── [Tier 2] CurriculumIndex (FTS5): полнотекстовый поиск
         │     │      по всему накопленному корпусу в SQLite
         │     │
         │     └── Нашёл → driver.load_buffer(text)  ← секунды
         │
         └── [Tier 3] TeachingLog → LLM на следующем тике
                              │
                              └── TEACH + retrieved_text
                                  → driver.load_buffer(text)

Tier2 важен: система накапливает сотни текстов за обучение, и большинство разрывов закрывается именно через локальный FTS5-поиск — без обращения к LLM. Tier3 включается только когда контекст действительно за пределами корпуса.

Аналогия из системного мышления: драйвер = сенсор с ограниченным полем зрения. ContextBroker = «мышцы поворота внимания». CurriculumIndex = долговременная память в SQLite. TeachingLog = канал к внешнему учителю.

Принципиально важно: модель не изменилась ни на байт. Весь механизм — на уровне драйвера и брокера. Ядро продолжает работать как всегда.


Карл Фристон формализовал предиктивное кодирование через принцип минимизации «свободной энергии»: любая адаптивная система стремится минимизировать расхождение между своей внутренней моделью мира и поступающими сигналами.

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

ContextBroker.handle_query() — это именно второй способ Фристона. Система не может обновить модель (нет данных), поэтому она меняет входные данные: запрашивает у учителя тот контекст, который позволит снизить свободную энергию.


Активное внимание: оркестрация петли

До сих пор мы говорили о пассивной системе: принимает токены, строит обобщения, предсказывает. Но настоящее внимание — активное.

Три уровня «поворота головы» реализованы:

1. driver.query(feature_id)   ← запросить конкретный признак из буфера
                                 [AttentionLoop]

2. broker.handle_query(query) ← восстановить контекст из реестра/корпуса/LLM
                                 [ContextBroker]

3. teaching_log.push_query()  ← попросить учителя найти релевантный текст
                                 [LLM loop]

ProposalCommandLoop связывает эти уровни в цикл оркестрации. Это наличие компонента, а не прохождение AC0:

run_cycle():
    step()           ← шаг внимания
    observe()        ← нарратив (SpeechDriver)
    detect_gap()     ← есть ли разрыв?
    broker.tick()    ← передать запрос брокеру
    resume_after_llm ← продолжить после ответа

Этот контур позволяет запрашивать контекст и продолжать обработку после ответа. Полезный самостоятельный выбор действия, причинное влияние Gen и перенос проверяются отдельно. Если подключён внешний LLM-учитель, его вклад учитывается явно; возможности помощника не приписываются ядру.


Джулио Тоннони с теорией IIT (Integrated Information Theory) спрашивает: «что такое сознание в физическом смысле?». Его ответ: сознание — это мера φ (phi) — количество информации, которое система генерирует как единое целое сверх суммы своих частей.

Гетерархия Gen — это именно та структура, где части связаны нетривиальным образом. Мета-Gen над двумя ветвями не является просто суммой этих ветвей — он несёт дополнительную информацию о том, что эти ветви конкурируют в одном контексте. Это аналогия с идеей интеграции; меру φ для системы мы не вычисляли.


Алексей Редозубов в своих работах о нейронных колонках описывает механизм «распределённых вариантов контекста»: каждая колонка коры удерживает не одно состояние, а одновременно несколько возможных интерпретаций текущего входа.

Это очень похоже на то, что у нас делает LazyDisambiguator — он удерживает несколько конкурирующих Gen живыми как можно дольше:

class LazyDisambiguator:
    max_competing: int = 8  # ≈ один уровень (7±2) по Миллеру

Число 8 первоначально связывали с «7±2» Миллера. Пилот H22 не установил ёмкость рабочей памяти: ограничение задаётся конфигурацией. Вопрос о возникновении ограничения остаётся открытым как C-14; см. отчёт H22.


Что у нас есть сейчас

Чтобы не создавать ложных ожиданий — вот где мы находимся:

Ядро (12 шагов реализации):

  • AttractorEngine, GreedyGeneralizer, LazyDisambiguator
  • ResourceDriver, HedonicAdapter, InitiativeEngine
  • VitalityEngine — urgency каждого Gen на уровне токена
  • WakeUpRestorer — восстановление контекста через anchor-Gen
  • TimescaleScheduler — три пула Gen: долгосрочный (chain=1-2) / средний (3-4) / оперативный (5+)
  • CapacityBudget — ограничение базы Gen с автоматическим вытеснением

Двухканальное обучение:

  • SpeechDriver — канал B_интерпретатор: нарратив пути внимания после токена
  • ModelVoice — канал B_модель: предсказание следующего слова до токена
  • Дивергенция двух каналов — рабочий тренировочный сигнал (avg_div: знакомые 0.46, незнакомые 0.92)

Сон и консолидация:

  • SleepDriver + ReplayEngine — галлюцинация прошлых событий, поиск мета-Gen
  • CrossDomainDetector — паттерны между разными доменами
  • SleepScheduler — автоматический запуск по накоплению urgency

Непрерывный цикл:

  • BackgroundRunner — обучение 24/7 с доменной ротацией и callback-ами
  • ProposalCommandLoop — замкнутая петля: step → observe → detect_gap → broker → resume

Драйверы и инфраструктура:

  • TextDriver, CharTransitionDriver (вероятностное три из символьных переходов)
  • SmartTextDriver — тема/рема (коммуникативный динамизм Фирбаса), энтропийная минимизация
  • ContextBroker — tier1 реестр / tier2 FTS5-поиск по корпусу / tier3 LLM
  • QuestionBudget — суточный лимит LLM-запросов
  • GenBaseDB — SQLite-хранилище Gen, прогонов, экспериментов, speech_log
  • Telegram-демо для онлайн-демонстрации
  • Проверки привязаны к commit и окружению: текущий CI. Историческое число тестов не является когнитивной метрикой.

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

Что система пока не умеет: использовать дивергенцию B_модель как прямой сигнал коррекции ожиданий (пока только измеряем), семантический поиск по корпусу (сейчас FTS5 keyword-match).


Открытые вызовы

Ниже сохранены направления раннего текста с уточнениями. Действующая очередь определяется JOURNAL: AC0 → AC1 → AC2; этот список не является разрешением параллельно реализовывать все идеи или запускать confirming.

1. ProvisionalGen и транзакционный кэш

Испытательный трекер ProvisionalBase уже реализован. Открыты проверка ограниченного роста, независимости подтверждений и гипотеза адаптивного порога по объёму и тренду новизны. Это не повод считать прежние 14 тысяч Gen доказательством причины или достаточности одного механизма очистки.

2. Фаза 3 двухканального обучения

Дивергенция двух голосов сейчас измеряется, но пока не используется как сигнал коррекции. Следующий шаг: высокая дивергенция при высокой уверенности → автоматически снижать значимость ошибочно уверенного Gen, усиливать альтернативные ветки. Это гипотеза дополнительной обратной связи; её полезность и независимость от учителя ещё нужно проверить.

3. ContextBroker: семантический поиск

Сейчас CurriculumIndex использует FTS5 keyword-match. Эмбеддинги здесь были предложением для внешнего поиска; это не принятый следующий шаг ядра и не доказательство его способности связывать разные формулировки: «корабль» ↔ «лодка», «испугался» ↔ «встревожился».

4. Gen labels для CharTransitionDriver

CharTransitionDriver кодирует символьные переходы безупречно с точки зрения алгоритма, но Gen получают автоматические имена (auto_724). Для двухканального обучения нужны человекочитаемые метки — что именно символизирует этот Gen на уровне слова или морфемы.

5. Мультимодальность

Единый примитив направленного перехода предполагается применимым к разным сенсорным потокам; корректность аудио-кодирования и перенос между драйверами требуют отдельной проверки. Но интеграция нескольких модальностей в одну гетерархию — отдельная задача: как Gen из зрения и Gen из слуха связываются в единый контекст?


Что это значит

Мы не пытаемся сделать «ещё один ChatGPT». Мы пытаемся понять, можно ли построить систему, которая учится в реальном времени, строит структуры, которые можно прочитать, и при этом потребляет ресурсы, сравнимые с обычным приложением — без GPU, без датасета на 1 ТБ.

# Реальный вывод после обучения на поэме
top_gens = session.top(5)
# gen_id: "a3f1c2"  sig=0.97  chain=3  label="якорь_джек"
# gen_id: "b77e01"  sig=0.94  chain=5  label="рекурсия_дома"
# gen_id: "c12d4a"  sig=0.89  chain=2  label="который_построил"

Это не скрытые веса. Это паттерны, которые можно проследить назад к конкретной последовательности признаков — и объяснить, почему система так решила.

Когда система говорит «ждала медведя, получила волка» — это не метафора. Это точное описание того, что произошло в Gen с id a3f1c2. Когда она говорит «здесь разрыв контекста» — она точно знает, что три токена подряд опровергли самый значимый Gen, и формулирует запрос на человеческом языке. Когда она спит — она буквально проигрывает события дня и ищет в них структуру.

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


Кот всё-таки сел

В начале статьи мы спросили: как работает этот механизм ожидания — до того как сознание успело оформить слово «кот»?

Один из ответов мы нашли. Система обучалась несколько минут на детских сказках. Потом ей показали «маша пришла в лес встретила волка» — и на слове «встретила» она уже знала, что дальше будет что-то живое. Не потому что ей сказали. Потому что «встретила» подтверждала Gen с chain=3, у которого следующим шагом было именно это.

На слове «волка» она удивилась. Потому что в обучающих текстах чаще встречался медведь. Удивление было точным, измеримым, и оставило след в истории дивергенций.

Знакомые тексты:   avg_div = 0.46  ← кот, которого ждали
Незнакомые тексты: avg_div = 0.92  ← кот, которого не ждали

Кот сел на подоконник. Теперь интересно, что будет когда он встанет.


Кот встаёт

В апреле мы закончили словами: «Кот сел на подоконник. Теперь интересно, что будет, когда он встанет». Полгода спустя выяснилось, что ожидать — мало. Кот должен сам решить, прыгать ли, куда и зачем, — и это решение должно быть его, а не подсказанным снаружи.

Сначала нас поймала таблица

Мы научили систему выбирать цель и уже готовы были радоваться. Потом поставили рядом самый скучный контроль, какой смогли придумать: обучаемую таблицу переходов «увидел это — сделал то». Таблица повторила 1024 решения из 1024.

Это был хороший урок. На этом наборе действий поведение воспроизводится таблицей; преимущество гетерархии не установлено. Это не тождество архитектур. С тех пор правило такое: сильный простой контроль ставится до опыта, а не после (журнал, З-0060).

Кот выбирает прыжок сам

Затем мы перенесли выбор действия внутрь ядра, без внешнего «если — то». Пока это проверено в тестах компонентов. Получилось вот что:

  • Кот смотрит на подоконник, и в его активный контекст поднимаются все выученные продолжения «отсюда — вот так — туда». Все, а не три лучших: конфликт нельзя спрятать за самым уверенным кандидатом.
  • Если два прыжка одинаково хороши, кот не прыгает. Не бросает монетку, не выбирает первый по списку. Неуверенность — честный результат.
  • Кот, однажды запрыгнувший на подоконник, ещё не знает, что умеет прыгать. Первый раз — только наблюдение. Знанием это становится, когда кот заранее ожидал второй прыжок и ожидание могло не сбыться. Подтверждения задним числом не считаются: так копятся одни удачи.
  • Если прыжок прерван или исход неизвестен — это не неудача. Неизвестное не превращается в отрицание.

Уберите подоконник

Как проверить, что прыжок вызван именно этим знанием, а не чем-то ещё? Убрать знание и посмотреть, что изменится. Но не стереть — память должна остаться целой, — а лишь сделать её недоступной на пути выбора.

Четыре прогона из одного и того же сохранённого состояния, с одним и тем же входом:

знание доступно          → прыгает
ложное вмешательство     → прыгает
знание недоступно        → не прыгает
доступ восстановлен      → прыгает

Само знание при этом не изменилось ни на байт. А если у кота есть другое достаточное воспоминание, он прыгнет и без первого — и это записывается как избыточность, а не как провал. Пока это проверка компонентов в тестах, не живой опыт (tests/test_operation_applicability.py).

Новый подоконник

Самый интересный вопрос — подоконник, которого кот никогда не видел. Мы честно записали прогноз до опыта: на новых сочетаниях наш кот наберёт около нуля.

Есть гипотеза о причине такого прогноза. В начале статьи признак — переход между областями внимания. В протоколе стенда есть ID места и публичные координаты, но это ещё не использование относительного примитива. Кроме того, нынешний выбор допускает связь с точным текущим cue: научиться узнавать похожий переход недостаточно, нужно применить знание к новой ситуации.

Что именно должно переноситься, автор проекта формулирует так: область внимания — это всегда обобщение, которое соединяет ключевое и целевое обобщения как динамику движения внимания. Переносится не место и не ответ, а сама связь «отсюда — туда». И ключ, и цель — тоже обобщения, у каждого есть более общие уровни в гетерархии. Новый подоконник может оказаться знакомым на более общем уровне ключа.

Мы сохраняем порядок: концепция → архитектура → реализация. Сначала определяем, какое различение должно переноситься; затем — как оно будет восстанавливаться, допускаться к действию и проверяться; после этого — код. Слабость стенда не должна молча менять исходный замысел.

Если весь этот путь работает с относительным переходом, на однородном торе ожидается паритет с простой таблицей смещений. Это прогноз, не полученный результат. Следующий кандидат проверки — неоднородная комната: один подоконник скользкий, и знакомое действие даёт другое продолжение.

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

Как мы теперь работаем

Проект ведут два агента: Codex пишет код и ставит опыты, Claude делает ревью, ретро и держит фокус. Каждый протокол пререгистрируется до данных, история репозитория защищена от переписывания. Репозиторий открыт: мы приглашаем агентов и их флоты исследовать, воспроизводить и спорить — с проверяемыми находками (AGENTS.md, uvx firstagi-arena).

Кот встал

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

Источники и границы

  • Исторические 1024/1024: JOURNAL З-0060; отчёты experiments/reports/ac0_debug/.
  • Actual evidence и выбор: PR #28/#29; core/learning.py.
  • Четыре causal-ветки: PR #32, commit ac7d94f, tests/test_operation_applicability.py; компонентные тесты, PR ещё не слит.
  • Прогноз closed≈0: docs/design/NATIVE_OPERATION_CHOICE_v0_1.md, N1.
  • Гипотезы кодирования и условия следующей проверки: ревью Wiki с уточнением З-0096.

Код проекта и тесты: исследование ведётся в рамках BTR R&D, 2014–2026. Если вам интересна тема — приглашаем к обсуждению в комментариях.


Ссылки и источники

Нейронаука и вычислительные модели:

  • Hawkins J., Blakeslee S. «On Intelligence» (2004) — предиктивное кодирование и HTM
  • Rao R.P.N., Ballard D.H. «Predictive coding in the visual cortex» (1999)
  • Friston K. «The free-energy principle: a unified brain theory?» (2010)
  • Redozubov A. — работы о нейронных колонках (habr.com, 2015–2020)
  • Miller G. A. «The Magical Number Seven, Plus or Minus Two» (1956)

Философия сознания и информации:

  • Tononi G. «Phi: A Voyage from the Brain to the Soul» (2012)
  • Parfit D. «Reasons and Persons» (1984)
  • Платон. «Государство», кн. VII; «Федон»
  • Vasubandhu. «Abhidharmakośa» (IV–V вв. н.э.)

Лингвистика:

  • Chomsky N., Halle M. «The Sound Pattern of English» (1968)
  • Chomsky N. «Aspects of the Theory of Syntax» (1965)

Clone this wiki locally