Когда речь заходит о больших языковых моделях, все сразу отмечают их талант к сочинению и пересказу текстов. Но вот встроить такую модель в реальный продукт — задача куда более каверзная, чем кажется на первый взгляд. На практике вылезают три системных «подводных камня», из‑за которых работать с ними бывает откровенно неудобно.
Первый камень — непредсказуемость. Представьте: вы посылаете модели один и тот же запрос дважды — а в ответ получаете два разных варианта. И это не случайность, а закономерность. Виной тому целый веер причин: от "памяти" о предыдущих диалогах (контекст) до тонких настроек генерации и естественного "плавания" внутренних весов модели. В итоге результат зависит не только от вашего запроса, но и от кучи невидимых факторов.
Второй момент — проблема с проверкой. В обычном программном коде мы чётко знаем: вот входные данные, вот ожидаемый вывод, вот критерии успеха. С языковыми моделями такой ясности нет. Как доказать, что ответ "правильный", если у задачи может быть десяток равноценных решений? Где та грань, за которой креативность превращается в выдумку?
Третий сюрприз ждёт после обновлений. Допустим, вы полгода отлаживали интеграцию, подобрали идеальные параметры, написали кучу тестов — и тут разработчики выпускают новую версию модели. И вдруг всё начинает работать иначе: прежние запросы дают странные ответы, тесты падают один за другим. Дело в том, что даже небольшие изменения в обучении модели могут кардинально поменять её "стиль" и логику. Предсказать, как именно она поведет себя в вашем сценарии, почти невозможно — а значит, каждое обновление грозит превратиться в марафон по починке сломавшегося
Этот проект строится по принципиально иной схеме. Его "интеллектуальное ядро" работает совсем не так, как языковые модели. Вместо вероятностной магии — жёсткая детерминированная логика.
В основе его лежит чёткий, осязаемый объект — таблица операций (таблица конечной магмы). Именно она задаёт поведение системы: никаких сюрпризов, никаких "творческих трактовок". Что записано в таблице — то и будет выполнено.
Чтобы гарантировать надёжность, в системе предусмотрены два стража качества:
валидаторы — проверяют корректность на каждом шаге;
регрессионный контур — следит, чтобы после изменений ничего не «поплыло».
Важно понимать: речь не идёт о том, чтобы вычеркнуть 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 первым сообщением.
Напишите:
Дальше ChatGPT выполнит “канонический прогон” из протокола: распаковку, bootstrap (PASS/FAIL), обязательные демонстрации и сформирует отчёты.
Примечание: чтобы ознакомиться с более широким контекстом проекта, воспользуйтесь навигацией по разделам DOCS/ и GRAPH/INDEX/*
Корректные названия:
“таблица бинарной операции на конечном множестве (operation table);”
“в терминах алгебры: таблица операции конечной магмы (magma operation table; часто говорят «таблица Кэли»);”
“по-инженерному: lookup table / transition table.”
Формально: есть конечный алфавит состояний P и функцияop: P x P -> P, заданная таблицей. Здесь L3 означает |P| = 3, L4 означает |P| = 4, то есть это просто размер конечного алфавита состояний.
Файл: 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 = A
C * B = A
B * B = C
Файл: 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 = SUN
R * NEG = S
SUN * x = SUN (вся строка SUN)
x * SUN = x (столбец SUN возвращает метку строки)
Инженерно: небольшой набор дискретных меток состояния.
Строго: конечное множество P, n = |P|.
Инженерно: lookup-table: (x, y) -> z.
Строго: функция op: P x P -> P, заданная таблицей (таблица Кэли конечной магмы).
Суть в том, что ни одно алгебраическое свойство — ни ассоциативность, ни коммутативность, ни наличие нейтрального элемента — не считается верным заранее.Даже если оно кажется очевидным или типичным для таких операций.
Эпизод — это просто набор меток состояний длины m (формально элемент Pm).
В демо чаще берётся m = 3, то есть эпизод — это тройка состояний, описывающая локальное «окно» системы.
Замыкание — это детерминированная функция F: Pm → Pm, которую система применяет к эпизоду снова и снова.
Поскольку множество Pm конечно, итерации не могут продолжаться бесконечно — рано или поздно состояние повторится.
В этот момент процесс входит либо в фикс‑точку (стабильное состояние), либо в цикл (устойчивый повтор).
Это чистая дискретная динамика — без эвристик и интерпретаций: всё вычисляется точно и однозначно.
# 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"
Смысл: таблица задаёт локальную редукцию, а замыкание превращает её в устойчивый вычислительный режим (фикс/цикл + трасса).
PASS (ok: true) означает:
процедуры исполнимы;
входы/выходы соответствуют схемам;
результат воспроизводим;
регрессионные эталоны не сломаны.
FAIL означает: есть структурированная ошибка с контекстом (что именно нарушено и где).
Демо согласования/дрейфа (верная команда для текущего релиза):
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 — удобная “витрина результата” для статьи и для интеграции (контракт не зависит от внутренней структуры модулей).
В первой части была зафиксирована сама модель: конечный набор меток состояний, таблично заданная бинарная операция, механизм замыкания и учёт симметрий.
Плюс был показан базовый запуск ядра — от распаковки архива до первого PASS/FAIL.
Во второй части речь уже про практику: как именно использовать движок в продукте, какие задачи он решает, как выглядит результат в публичном JSON-отчёте и как подключить ядро как сервис.
Архив плоский (без верхней директории). Чтобы команды из статьи воспроизводились одинаково у всех, используйте такой шаблон:
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/сервис.
Есть два источника (два агента/сервиса/подсистемы), которые выдают «одно и то же», но в разных кодировках меток. Например:
один пишет статусы как 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 может быть ограничен по длине, полный набор остаётся во внутреннем отчёте.
Даже если два источника в целом «согласуемы», отображение σ может меняться по времени: обновили прошивку, поменяли правила статусов, переключили режим обработки.
Вместо того чтобы выбирать «одну σ на всё», движок делает разбиение на сегменты, где σ стабильна, и выдаёт границы смены режима.
В 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 — другая,
это и есть «событие смены режима», которое удобно превращать в алерт/событие аудита.
Нужно ядро принятия решения/диагностики, которое:
детерминировано (одинаковый вход → одинаковый результат),
покрывается регрессией,
объясняется трассой вычисления, а не пост-фактум комментариями.
Таблично заданная бинарная операция здесь выступает как инженерный оператор редукции/композиции состояний. Сверху накладывается детерминированное замыкание (итератор), которое приводит эпизод к фикс-точке или циклу — и это уже используется как «решение».
Ассоциативность означает, что результат не зависит от расстановки скобок:(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, т.е. структура вырождается. Поэтому неассоциативность здесь не “недостаток”, а необходимое условие нетривиального поведения.
Иными словами: в этой архитектуре "математическая корректность" достигается не через навешивание чужих аксиом, а через честное именование структуры (конечная магма) и через валидируемые контракты.
В проекте идея простая: ядро не обязано понимать текст, оно обязано работать на каноническом формате. Поэтому входы стандартизованы.
Смотрите примеры в:
RAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l4_drift_v2.json
RAW/CONVECTION_BRIDGE_EXAMPLES/convection_bridge_l3_const_sigma_v1.json
Это удобный формат, если данные уже приведены к дискретным меткам.
Если у вас поток эпизодов, его можно прогнать через встроенный адаптер:
вход: 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
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).
В архиве есть Dockerfile и docker-compose.yml:
docker compose up --build
Это удобный путь для демонстрации и для интеграционных тестов.
Я собрал и выложил детерминированный движок рассуждения, который работает не на вероятностях текста, а на конечной дискретной модели: есть метки состояний, таблично заданная бинарная операция и чёткий контур воспроизводимости — спецификации, валидаторы, регрессия.
Такой подход даёт то, чего трудно добиться в LLM-парадигме: одинаковый вход всегда даёт одинаковый выход, а любое отклонение фиксируется тестами.
Детерминизм как базовая гарантия.
Я не объясняю результат словами — я его вычисляю по конечному объекту. При тех же входных данных поведение не дрейфует.
Строгость процедурная, а не декларативная.
Не нужно верить в красивую теорию: всё зафиксировано контрактами (SPEC/), проверено валидаторами (VALIDATOR/), задокументировано в отчётах (REPORTS/) и удержано регрессией (REGRESSION/).
Истина проекта — в корректности процедур и их воспроизводимости.
Никакой подмены математики словами.
Базовая структура называется тем, чем она является: таблица конечной магмы — таблица Кэли, без обещаний про ассоциативность, группу или поле.
Если свойство важно — оно оформляется как аудит или гейт, и при нарушении я предъявляю контрпример.
Прикладные задачи решаются на дискретных данных, без “семантики текста”.
Согласование схем меток (σ-alignment) и поиск смены режима (change-points) выполняются как вычислимые процедуры с метриками и конкретными свидетелями (witnesses), а не как интерпретации.
В основе моего движка лежит идея, которую в инженерных системах почти никогда не делают центральной — замыкание. Я не просто применяю правило один раз, а строю итератор на конечном множестве состояний и гоню процесс до устойчивого результата.
Схема такая:
вход кодируется в эпизод (элемент Pm), далее — детерминированное обновление по табличному правилу, после чего включается замыкание: итерации идут, пока система не стабилизируется — не выйдет на фиксированную точку или цикл. Уже это устойчивое состояние я и принимаю за результат.
Почему это важно.
Результат задаётся не единичным шагом, а аттрактором — устойчивой формой поведения в конечной динамике. Из-за этого поведение системы становится воспроизводимым: если вход тот же, то и аттрактор будет тем же. В обычных rule engine итог часто зависит от порядка применения правил или случайностей пайплайна, а здесь порядок фиксирован и уходит в устойчивый режим через замыкание.
Да, математически замыкание — простая идея.
Но я утверждаю, что в прикладных «движках решений» его почти не используют как основной вычислительный принцип. Здесь же оно — архитектурное ядро, за счёт чего система работает не как набор if-else и не как генератор текста, а как дискретная динамика с аттракторами.
Я не заявляю, что создал “искусственный интеллект”. Я построил решатель, который можно держать под контролем.
Мои отличия от классического rule engine:
я работаю не только с правилами, но и с устойчивыми режимами (фикс/цикл) как с результатом;
я использую согласование схем (поиск σ) и выдаю не только ответ, но и witnesses — минимальные эпизоды, которые различают конкурирующие гипотезы;
я фиксирую результат в стабильном контракте REPORT_PUBLIC_V1, пригодном для CI и интеграции;
я держу строгий режим воспроизводимости: PASS/FAIL — это про процедуры, а не про “впечатление”.
Я не утверждаю, что движок "понимает" текст или может заменить собеседника. Суть в другом: LLM хорош как интерфейс для генерации и объяснений, но для детерминированного решения задач (с тестами и регрессией) нужен иной тип решателя.
Я сделал так, чтобы проверка была не риторикой, а практикой.
Нулевой прогон (bootstrap) сразу даёт PASS или FAIL.
Демо запускается одной командой.
Результаты фиксируются в публичном отчёте стандартного формата.
Проще говоря: это не «концепт», а исполняемый артефакт, который можно запустить, проверить и сверить результаты.
Источник


