Часть 1. Детерминированный движок рассуждения на конечной таблице операции (в перспективе — замена LLM)
Когда речь заходит о больших языковых моделях, все сразу отмечают их талант к сочинению и пересказу текстов. Но вот встроить такую модель в реальный продукт — задача куда более каверзная, чем кажется на первый взгляд. На практике вылезают три системных «подводных камня», из‑за которых работать с ними бывает откровенно неудобно.
Первый камень — непредсказуемость. Представьте: вы посылаете модели один и тот же запрос дважды — а в ответ получаете два разных варианта. И это не случайность, а закономерность. Виной тому целый веер причин: от "памяти" о предыдущих диалогах (контекст) до тонких настроек генерации и естественного "плавания" внутренних весов модели. В итоге результат зависит не только от вашего запроса, но и от кучи невидимых факторов.
Второй момент — проблема с проверкой. В обычном программном коде мы чётко знаем: вот входные данные, вот ожидаемый вывод, вот критерии успеха. С языковыми моделями такой ясности нет. Как доказать, что ответ "правильный", если у задачи может быть десяток равноценных решений? Где та грань, за которой креативность превращается в выдумку?
Третий сюрприз ждёт после обновлений. Допустим, вы полгода отлаживали интеграцию, подобрали идеальные параметры, написали кучу тестов — и тут разработчики выпускают новую версию модели. И вдруг всё начинает работать иначе: прежние запросы дают странные ответы, тесты падают один за другим. Дело в том, что даже небольшие изменения в обучении модели могут кардинально поменять её "стиль" и логику. Предсказать, как именно она поведет себя в вашем сценарии, почти невозможно — а значит, каждое обновление грозит превратиться в марафон по починке сломавшегося
Этот проект строится по принципиально иной схеме. Его "интеллектуальное ядро" работает совсем не так, как языковые модели. Вместо вероятностной магии — жёсткая детерминированная логика.
В основе его лежит чёткий, осязаемый объект — таблица операций (таблица конечной магмы). Именно она задаёт поведение системы: никаких сюрпризов, никаких "творческих трактовок". Что записано в таблице — то и будет выполнено.
Чтобы гарантировать надёжность, в системе предусмотрены два стража качества:
валидаторы — проверяют корректность на каждом шаге;
регрессионный контур — следит, чтобы после изменений ничего не «поплыло».
Важно понимать: речь не идёт о том, чтобы вычеркнуть LLM из процесса навсегда. Языковая модель вполне может остаться в команде — но на другой р��ли. Мне она представляется скорее как вежливый переводчик: она принимает человеческий текст, понимает его смысл и аккуратно упаковывает в структурированные данные для основного ядра.
Итак, если сейчас LLM выступает в роли «решателя» — того, кто думает и принимает решения. Я предлагаю, чтобы эту функцию взяло на себя табличное ядро. К нему добавляются:
процедуры замыкания — механизмы, которые доводят решение до логического конца;
тестовый контур — система проверок, гарантирующая стабильность.
Получается чёткая цепочка: LLM‑интерфейс переводит запрос в структуру → табличное ядро вычисляет ответ по жёстким правилам → валидаторы и тесты следят за чистотой процесса. Никакой неопределённости — только предсказуемость и контроль.
Как корректно воспроизвести архив
Архив плоский (без верхней директории). Поэтому самый надёжный способ распаковки — так:
mkdir MP_YANTRA_CORE_iter116
unzip MP_YANTRA_CORE_iter116.zip -d MP_YANTRA_CORE_iter116
cd MP_YANTRA_CORE_iter116
python TOOLS/bootstrap.py
Ожидаемое поведение: bootstrap возвращает PASS по ядру. HTTP-слой (FastAPI) опционален и не должен ломать нулевой прогон.
Запуск в ChatGPT (без локальной установки)
Если у вас нет желания поднимать Python-окружение локально, архив можно прогнать прямо в ChatGPT — нужен режим выполнения кода (Advanced Data Analysis / Code Interpreter).
Создайте новый чат и прикрепите файл
MP_YANTRA_CORE_iter116.zipпервым сообщением.Напишите:
Следуй инструкциям в файле
DOCS/00_NEW_CHAT_PROTOCOL.mdиз загруженного архива.
Дальше ChatGPT выполнит “канонический прогон” из протокола: распаковку, bootstrap (PASS/FAIL), обязательные демонстрации и сформирует отчёты.
Примечание: чтобы ознакомиться с более широким контекстом проекта, воспользуйтесь навигацией по разделам DOCS/ и GRAPH/INDEX/*
1) Главный объект: таблица бинарной операции
Корректные названия:
“таблица бинарной операции на конечном множестве (operation table);”
“в терминах алгебры: таблица операции конечной магмы (magma operation table; часто говорят «таблица Кэли»);”
“по-инженерному: lookup table / transition table.”
Формально: есть конечный алфавит состояний P и функцияop: P x P -> P, заданная таблицей. Здесь L3 означает |P| = 3, L4 означает |P| = 4, то есть это просто размер конечного алфавита состояний.
2) Две реальные таблицы из архива (L3 и L4)
2.1. L3 (3x3): базовый кадр на трёх состояниях
Файл: SPEC/TABLES/L3_STAR_SUN_A.json
Алфавит: P = {A, B, C}, операция *.
Симметрия таблицы — это такое переименование элементов, при котором сама операция не меняется. Поясню более формально: есть отображение σ: P → P, и если для любых a, b из P выполняется σ(op(a, b)) = op(σ(a), σ(b)), то σ сохраняет структуру таблицы. Такое σ называется автоморфизмом.
То есть все "правильные" преобразования σ, которые сохраняют операцию op, вместе образуют группу. Все эти автоморфизмы вместе образуют группу, которую обозначают Aut(op). В проекте она используется, чтобы:
уменьшить число перебираемых вариантов (факторизация по симметриям);
проверять корректность таблиц и их инварианты в валидаторах.
Фактически Aut(op) — это подгруппа Sym(P), то есть полного множества всех перестановок элементов P.
| A | B | C |
|---|---|---|---|
A | A | A | A |
B | B | C | A |
C | C | A | B |
Примеры вычислений:
B * C = AC * B = AB * B = C
2.2. L4 (4x4): прикладной кадр “статусов/ремонта”
Файл: SPEC/TABLES/L4_KAP_SR_NEG_SUN_v1.json
Алфавит: P = {S, NEG, R, SUN}, операция *.
| S | NEG | R | SUN |
|---|---|---|---|---|
S | NEG | R | SUN | S |
NEG | R | SUN | NEG | NEG |
R | SUN | S | NEG | R |
SUN | SUN | SUN | SUN | SUN |
Примеры:
S * R = SUNR * NEG = SSUN * x = SUN(вся строка SUN)x * SUN = x(столбец SUN возвращает метку строки)
3) Минимальные определения
3.1. Алфавит состояний
Инженерно: небольшой набор дискретных меток состояния.
Строго: конечное множество
P,n = |P|.
3.2. Табличная операция
Инженерно: lookup-table:
(x, y) -> z.Строго: функция
op: P x P -> P, заданная таблицей (таблица Кэли конечной магмы).
Суть в том, что ни одно алгебраическое свойство — ни ассоциативность, ни коммутативность, ни наличие нейтрального элемента — не считается верным заранее.Даже если оно кажется очевидным или типичным для таких операций.
3.3. Эпизод и замыкание
Эпизод — эт�� просто набор меток состояний длины m (формально элемент Pm).
В демо чаще берётся m = 3, то есть эпизод — это тройка состояний, описывающая локальное «окно» системы.
Замыкание — это детерминированная функция F: Pm → Pm, которую система применяет к эпизоду снова и снова.
Поскольку множество Pm конечно, итерации не могут продолжаться бесконечно — рано или поздно состояние повторится.
В этот момент процесс входит либо в фикс‑точку (стабильное состояние), либо в цикл (устойчивый повтор).
Это чистая дискретная динамика — без эвристик и интерпретаций: всё вычисляется точно и однозначно.
4) Как ядро использует таблицу (псевдокод)
# op — таблица: P x P -> P
# state — m-канальный эпизод: (x1, x2, ..., xm)
function step(state):
x1' = op(x1, x2)
x2' = op(x2, x3)
...
xm' = op(xm, x1)
return (x1', x2', ..., xm')
function closure(state, limit):
seen = map() # состояние -> индекс
for t in 0..limit:
if state in seen:
return cycle(seen[state]..t)
seen[state] = t
state = step(state)
return "no_converge_within_limit"
Смысл: таблица задаёт локальную редукцию, а замыкание превращает её в устойчивый вычислительный режим (фикс/цикл + трасса).
5) PASS/FAIL: что это означает для читателя
PASS (ok: true) означает:
процедуры исполнимы;
входы/выходы соответствуют схемам;
результат воспроизводим;
регрессионные эталоны не сломаны.
FAIL означает: есть структурированная ошибка с контекстом (что именно нарушено и где).
6) Быстрый запуск (после bootstrap): демо + публичный отчёт
Демо согласования/дрейфа (верная команда для текущего релиза):
python TOOLS/mp_engine_cli_v1.py run-bridge-v2 \
--input RAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l4_drift_v2.json \
> REPORTS/habr_bridge_internal.json
Экспорт публичного отчёта (стабильный JSON-контракт):
python TOOLS/mp_engine_cli_v1.py export-public-report \
--in REPORTS/habr_bridge_internal.json \
--out REPORTS/habr_bridge_public.json
Файл REPORTS/habr_bridge_public.json — удобная “витрина результата” для статьи и для интеграции (контракт не зависит от внутренней структуры модулей).
Часть 2. Как этим пользоваться в продукте: сценарии, интеграция, отчётность
В первой части была зафиксирована сама модель: конечный набор меток состояний, таблично заданная бинарная операция, механизм замыкания и учёт симметрий.
Плюс был показан базовый запуск ядра — от распаковки архива до первого PASS/FAIL.
Во второй части речь уже про практику: как именно использовать движок в продукте, какие задачи он решает, как выглядит результат в публичном JSON-отчёте и как подключить ядро как сервис.
0) Важная практическая деталь про распаковку архива
Архив плоский (без верхней директории). Чтобы команды из статьи воспроизводились одинаково у всех, используйте такой шаблон:
mkdir MP_YANTRA_CORE_iter116
unzip MP_YANTRA_CORE_iter116.zip -d MP_YANTRA_CORE_iter116
cd MP_YANTRA_CORE_iter116
python TOOLS/bootstrap.py
После bootstrap можно запускать демо/CLI/сервис.
1) Сценарий A: согласование меток двух источников (label alignment), без «семантики текста»
Задача
Есть два источника (два агента/сервиса/подсистемы), которые выдают «одно и то же», но в разных кодировках меток. Например:
один пишет статусы как
S, R, -, ☼,другой — тем же алфавито��, но «сдвинутым» или переименованным.
Движок решает инженерно корректную задачу: подобрать отображение σ (переименование меток), которое лучше всего согласует наблюдения, причём:
класс допустимых σ ограничен (чтобы не «подгонять мощностью»),
результат сопровождается метриками и witnesses (эпизодами, которые реально различают гипотезы).
Запуск на готовом примере из архива
python TOOLS/mp_engine_cli_v1.py run-bridge-v2 \
--input RAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l4_drift_v2.json \
> REPORTS/habr_bridge_internal.json
python TOOLS/mp_engine_cli_v1.py export-public-report \
--in REPORTS/habr_bridge_internal.json \
--out REPORTS/habr_bridge_public.json
Что смотреть в результате (публичный отчёт)
Откройте REPORTS/habr_bridge_public.json. Ключевые поля:
result.global.best_sigma— лучшая гипотеза σ (в примере σ имеет параметры вида{u, t}: это конкретный класс переименований, заданный данными);result.global.best.stabilityиresult.global.best.mismatches— метрика качества согласования;result.uncertainty.witnesses— короткий список «свидетельств», которые отличают топ-гипотезы.
Мини-выдержка (сокращённо по смыслу):
{
"schema": "REPORT_PUBLIC_V1",
"result": {
"global": {
"best_sigma": {"u": 1, "t": 2},
"best": {"stability": 0.5, "mismatches": 6}
},
"uncertainty": {
"witnesses_count": 8,
"witnesses": [
{"eid":"01", "obs":["S","-","☼"], "top1": {...}, "top2": {...}},
...
]
}
}
}
Инженерный смысл witnesses: вместо спора «кто прав» появляется проверяемый вопрос: каких данных не хватает, чтобы устойчиво выбрать одну гипотезу σ и отбраковать конкурирующую.
Примечание: в public-отчёте список witnesses может быть ограничен по длине, полный набор остаётся во внутреннем отчёте.
2) Сценарий B: дрейф и смена режима (change-point) как оптимизация
Задача
Даже если два источника в целом «согласуемы», отображение σ может меняться по времени: обновили прошивку, поменяли правила статусов, переключили режим обработки.
Вместо того чтобы выбирать «одну σ на всё», движок делает разбиение на сегменты, где σ стабильна, и выдаёт границы смены режима.
Что смотреть в отчёте
В REPORTS/habr_bridge_public.json:
result.segments.count— сколько сегментов найдено;result.segments.boundaries— список границ между сегментами.
Пример поля границы (по смыслу):
"boundaries": [
{"between":[5,6], "prev_sigma":{"u":3,"t":0}, "next_sigma":{"u":1,"t":2}}
]
Инженерная интерпретация:
на эпизодах 0..5 лучшая σ одна,
на эпизодах 6..N — другая,
это и есть «событие смены режима», которое удобно превращать в алерт/событие аудита.
3) Сценарий C: детерминированный «policy kernel» в дискретном пространстве (вместо вероятностного решателя)
Задача
Нужно ядро принятия решения/диагностики, которое:
детерминировано (одинаковый вход → одинаковый результат),
покрывается регрессией,
объясняется трассой вычисления, а не пост-фактум комментариями.
Таблично заданная бинарная операция здесь выступает как инженерный опе��атор редукции/композиции состояний. Сверху накладывается детерминированное замыкание (итератор), которое приводит эпизод к фикс-точке или циклу — и это уже используется как «решение».
Почему таблица не обязана быть ассоциативной — и это нормально
Ассоциативность означает, что результат не зависит от расстановки скобок:
(a b) c == a (b c).
Для вычислительного контроллера это часто лишнее ограничение:
В продуктовых правилах порядок объединения важен.
В реальном пайплайне вы почти всегда агрегируете сигнал по этапам: вход → нормализация → ремонт → финал. Неассоциативность позволяет таблице «помнить» порядок композиции и тем самым реализовывать приоритеты.Ассоциативность резко сужает пространство конструкций.
Если вы хотите одновременно иметь «поглотитель» (состояние, которое доминирует) и «правую единицу» (состояние, которое не меняет), то требование ассоциативности быстро превращается в набор жёстких алгебраических ограничений, которые вам как инженеру чаще не нужны.В движке скобки фиксированы алгоритмом.
Замыкание — это конкретный детерминированный итератор. Он не «переставляет скобки», он применяет правило обновления каналов строго по схеме. Поэтому ассоциативность здесь не «обязательная корректность», а отдельное свойство, которое имеет смысл вводить только если оно нужно прикладной области — и тогда оно оформляется как проверяемый аудит/гейт.
Так, если потребовать ассоциативность, L4-таблица схлопнется в тривиальную. В нашей L4-таблицеSUN — левый поглотитель (SUN x = SUN) и одновременно правая единица (x SUN = x). При ассоциативности для любого x: x = x SUN = (SUN x) SUN = SUN (x SUN) = SUN x = SUN. Значит, все элементы равны SUN, т.е. структура вырождается. Поэтому неассоциативность здесь не “недостаток”, а необходимое условие нетривиального поведения.
Иными словами: в этой архитектуре "математическая корректность" достигается не через навешивание чужих аксиом, а через честное именование структуры (конечная магма) и через валидируемые контракты.
4) Как подать свои данные: два «канонических» входа
В проекте идея простая: ядро не обязано понимать текст, оно обязано работать на каноническом формате. Поэтому входы стандартизованы.
4.1. CONVECTION_BRIDGE_INPUT_V2 — когда у вас уже есть эпизоды «до/после»
Смотрите примеры в:
RAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l4_drift_v2.jsonRAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l3_const_sigma_v1.json
Это удобный формат, если данные уже приведены к дискретным меткам.
4.2. EPISODE_STREAM_V1 — поток эпизодов (и адаптер)
Если у вас поток эпизодов, его можно прогнать через встроенный адаптер:
вход: RAW/EPISODE_STREAM_EXAMPLES/episode_stream_l4_drift_v2.json
запуск:
python TOOLS/mp_engine_cli_v1.py run-episode-stream \
--input RAW/EPISODE_STREAM_EXAMPLES/episode_stream_l4_drift_v2.json \
> REPORTS/habr_stream_internal.json
python TOOLS/mp_engine_cli_v1.py export-public-report \
--in REPORTS/habr_stream_internal.json \
--out REPORTS/habr_stream_public.json
5) Интеграция как HTTP-сервис (опционально)
HTTP-слой намеренно вынесен как необязательный: ядро не зависит от веб-фреймворков. Но если нужно встроить в продукт, сервисный слой уже есть.
Установка зависимостей сервиса
pip install -r requirements.txt
Запуск сервиса
python TOOLS/serve_api_v1.py --host 127.0.0.1 --port 8000
Проверка
curl http://127.0.0.1:8000/health
Вызов расчёта (публичный контракт)
curl -X POST http://127.0.0.1:8000/run/bridge/public \
-H "Content-Type: application/json" \
--data @RAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l4_drift_v2.json
Ответ — JSON в формате REPORT_PUBLIC_V1 (тот же, что генерирует export-public-report).
6) Docker Compose (если хочется «одной командой»)
В архиве есть Dockerfile и docker-compose.yml:
docker compose up --build
Это удобный путь для демонстрации и для интеграционных тестов.
Итоги
Я собрал и выложил детерминированный движок рассуждения, который работает не на вероятностях текста, а на конечной дискретной модели: есть метки состояний, таблично заданная бинарная операция и чёткий контур воспроизводимости — спецификации, валидаторы, регрессия.
Такой подход даёт то, чего трудно добиться в LLM-парадигме: одинаковый вход всегда даёт одинаковый выход, а любое отклонение фиксируется тестами.
1. В чём инженерская уникальность
Детерминизм как базовая гарантия.
Я не объясняю результат словами — я его вычисляю по конечному объекту. При тех же входных данных поведение не дрейфует.
Строгость процедурная, а не декларативная.
Не нужно верить в красивую теорию: всё зафиксировано контрактами (SPEC/), проверено валидаторами (VALIDATOR/), задокументировано в отчётах (REPORTS/) и удержано регрессией (REGRESSION/).
Истина проекта — в корректности процедур и их воспроизводимости.
Никакой подмены математики словами.
Базовая структура называется тем, чем она является: таблица конечной магмы — таблица Кэли, без обещаний про ассоциативность, группу или поле.
Если свойство важно — оно оформляется как аудит или гейт, и при нарушении я предъявляю контрпример.
Прикладные задачи решаются на дискретных данных, без “семантики текста”.
Согласование схем меток (σ-alignment) и поиск смены режима (change-points) выполняются как вычислимые процедуры с метриками и конкретными свидетелями (witnesses), а не как интерпретации.
2) Что в основе работы: замыкание как вычислительный механизм (и почему это важно)
В основе моего движка лежит идея, которую в инженерных системах почти никогда не делают центральной — замыкание. Я не просто применяю правило один раз, а строю итератор на конечном множестве состояний и гоню процесс до устойчивого результата.
Схема такая:
вход кодируется в эпизод (элемент Pm), далее — детерминированное обновление по табличному правилу, после чего включается замыкание: итерации идут, пока система не стабилизируется — не выйдет на фиксированную точку или цикл. Уже это устойчивое состояние я и принимаю за результат.
Почему это важно.
Результат задаётся не единичным шагом, а аттрактором — устойчивой формой поведения в конечной динамике. Из-за этого поведение системы становится воспроизводимым: если вход тот же, то и аттрактор будет тем же. В обычных rule engine итог часто зависит от порядка применения правил или случайностей пайплайна, а здесь порядок фиксирован и уходит в устойчивый режим через замыкание.
Да, математически замыкание — простая идея.
Но я утверждаю, что в прикладных «движках решений» его почти не используют как основной вычислительный принцип. Здесь же оно — архитектурное ядро, за счёт чего система работает не как набор if-else и не как генератор текста, а как дискретная динамика с аттракторами.
3) Почему это не ИИ
Я не заявляю, что создал “искусственный интеллект”. Я построил решатель, который можно держать под контролем.
Мои отличия от классического rule engine:
я работаю не только с правилами, но и с устойчивыми режимами (фикс/цикл) как с результатом;
я использую согласование схем (поиск σ) и выдаю не только ответ, но и witnesses — минимальные эпизоды, которые различают конкурирующие гипотезы;
я фиксирую результат в стабильном контракте
REPORT_PUBLIC_V1, пригодном для CI и интеграции;я держу строгий режим воспроизводимости: PASS/FAIL — это про процедуры, а не про “впечатление”.
4) Как я позиционирую это относительно LLM (чтобы не вводить в заблуждение)
Я не утверждаю, что движок "понимает" текст или может заменить собеседника. Суть в другом: LLM хорош как интерфейс для генерации и объяснений, но для детерминированного решения задач (с тестами и регрессией) нужен иной тип решателя.
5) Что читатель может проверить сам
Я сделал так, чтобы проверка была не риторикой, а практикой.
Нулевой прогон (bootstrap) сразу даёт PASS или FAIL.
Демо запускается одной командой.
Результаты фиксируются в публичном отчёте стандартного формата.
Проще говоря: это не «концепт», а исполняемый артефакт, который можно запустить, проверить и сверить результаты.
