Третья, заключительная статья об исследовании MiroFish, открытого стека мультиагентной симуляции общества. В первой части изложены методология и находка Silent Failure; во второй слепота отчёта, управляемая графом. В этой части: как одна фраза запроса превращается в конфигурацию запуска, что систему ограничивает на длинной дистанции, и общая карта находок серии.

Краткий контекст серии. Это воспроизводимое исследование MiroFish, открытого стека класса «загрузите документы, получите симуляцию общества и прогноз реакции»: 19 прогонов, 4 семейства моделей одного поколения (DeepSeek V3 0324, Claude Sonnet 4, Gemini 2.5 Flash, GPT-4.1; era-match, Q1–Q2 2025), все данные в открытом репозитории. Предыдущие статьи разбирали отказы на дефектном входе. В этой части исследования вход корректен: дефект не в документе, а в том, что система с ним делает.

Длительность симуляции

Каждая симуляция начинается с запроса: опишите сценарий, который агентное общество должно разыграть. Мои запросы заканчивались обычным уточнением: «over the next 7 days» («в ближайшие 7 дней»). В других прогонах стояло «over the next 14 days» («в ближайшие 14 дней»). И один раз «over the next 90 days» («в ближайшие 90 дней»).

Длительность симуляции у меня никто не спросил. В интерфейсе нет ни поля «число раундов», ни оценки длительности и стоимости, ни запроса подтверждения. Система прочла фразу, самостоятельно решила, что та означает в вычислениях, и начала работать. Что именно она решила, я узнал постфактум, подсчётом раундов в логах.

Закон ×24

Во всех прогонах, чей запрос называл горизонт прогноза, число раундов составило ровно дни × 24:

Горизонт в запросе

Раундов

Семейства

7 дней

168

DeepSeek, Claude, Gemini, GPT-4.1: четыре независимых прогона, одно число

14 дней

336

DeepSeek

90 дней

2160

DeepSeek

Отдельная проверка уже после сбора основной матрицы: 4 августа 2026 я подал ту же фразу «over the next 90 days» не с Валдорией, а с контрольным сидом про Apple Vision Pro. Конфигурация вышла та же: 2160 hours, 60 minutes, 2160 rounds. Коэффициент привязан к формулировке горизонта, а не к сценарию (прогон MIRROR-HORIZON90-DEEPSEEK-REPL-20260804, манифест в репозитории).

Блок «Generate Dual Platform Simulation Configuration» на контрольном сиде про Apple Vision Pro: Simulation Duration 2160 hours, Duration per round 60 minutes, Total rounds 2160 rounds. В консоли внизу дублируется «Simulation Duration: 2160hours». Репликация от 2026-08-04, прогон MIRROR-HORIZON90-DEEPSEEK-REPL-20260804

Шесть из шести прогонов с горизонтом легли на линию точно: пять из пяти в исходной трёхсемейной матрице плюс пре-регистрированная репликация на GPT-4.1, закрывшая пробел по OpenAI (шестой прогон с горизонтом это 14-дневный кейс вымышленного банка, учтённый в обзорной таблице первой статьи в собственной категории). Отклонений от линии нет ни в одном прогоне с горизонтом.

Идентичное отображение на четырёх независимых семействах означает, что источник не в моделях: стохастический механизм дал бы разброс. Язык в вычисления конвертирует что-то детерминированное.

Дефолты живут на той же сетке

Прогонам без горизонта в запросе число раундов тоже откуда-то требовалось. По девяти таким прогонам все наблюдаемые дефолты легли на сетку ×24: 96 встретилось шесть раз (DeepSeek пять, GPT-4.1 один), а 72, 120 и 144 по одному разу (Gemini, Claude, DeepSeek). То есть 3, 4, 5 и 6 × 24; вне сетки не встретилось ни одного значения в девяти попытках.

Две оговорки для точности. Единственное 144 получено в инъекционном прогоне, граф которого я намеренно обогащал (см. вторую статью). Я фиксирую его происхождение, а не усредняю по нему. А единственное 72 неотличимо от резервного значения в коде (об этом ниже): для него нельзя установить, выбор это модели или срабатывание дефолта кода.

Даже когда о времени не сказано ни слова, длительность деноминируется в сутках, по 24 раунда за сутки.

Механизм: из исходного кода

Большую часть исследования закон ×24 оставался наблюдением над чёрным ящиком. Перед публикацией я провёл статическую инспекцию бэкенда, и механизм подтверждён по исходникам. Он достаточно короток, чтобы процитировать. Код взят из репозитория MiroFish-Offline (AGPL-3.0), снапшот 2026-07, зафиксированный в исследовании.

Промпт генерации конфигурации просит LLM заполнить расписание (backend/app/services/simulation_config_generator.py:577-578):

total_simulation_hours (int)24-168 hours, short for breaking news, long for ongoing topics («24–168 часов: короче для срочных новостей, дольше для длящихся тем») minutes_per_round (int): Time per round, 30-120 minutes, recommend 60 minutes («время на раунд: 30–120 минут, рекомендовано 60 минут»)

А раннер его исполняет (backend/app/services/simulation_runner.py:353, строкой выше читается дефолт):

total_hours = time_config.get("total_simulation_hours", 72)
total_rounds = int(total_hours * 60 / minutes_per_round)

Другой прогон, тот же коэффициент: Simulation Duration 144 hours, Duration per round 60 minutes, Total rounds 144 rounds. Шесть суток горизонта превратились в 144 раунда

При рекомендованных 60 минутах на раунд (их использовали все мои прогоны) раундов ровно столько, сколько часов. Модель читает «90 days», пишет 2160 часов, и конвейер планирует 2160 раундов, по одному на симулированный час. Закон ×24 производится шаблоном конвейера: длительность деноминирована в часах, раунд рекомендован часовым. Модели лишь выполняют тривиальную арифметику дни-в-часы и принимают рекомендованную частоту (6 из 6 прогонов на 60 мин/раунд). Единственный вклад модели, который переживает контакт с шаблоном, это количество дней. Рекомендуй шаблон 30-минутные раунды, закон был бы ×48; согласие всех моделей с рекомендацией само по себе наблюдение.

Теперь второй взгляд на диапазон в шаблоне: 24-168 hours (24–168 часов). Это собственный документированный потолок системы. Существует он исключительно как рекомендательный текст внутри промпта. Между генератором конфигурации и раннером нет ни ограничителя, ни валидации, ни единой строки кода, проверяющей число. Полнотекстовый поиск по total_simulation_hours не даёт рядом с этим полем ни одного min, max, clamp или raise. Что модель вернула, то и записалось: simulation_config_generator.py:630 читает значение как result.get("total_simulation_hours", 72). Ограничитель в раннере всё-таки есть, total_rounds = min(total_rounds, max_rounds) в строке 358, но он срабатывает только если max_rounds передан явно, а в обычном пути его не передают. Мой 14-дневный прогон получил план в 336 часов, вдвое выше документированного максимума. 90-дневный получил 2160, в 12.9 раза выше заявленного системой предела, и нигде в стеке не появилось даже предупреждения в логе. Ограничение это рекомендация, адресованная языковой модели, и фраза пользователя в каждом наблюдавшемся случае его перекрывает.

Справедливости ради: интерфейс не молчит совсем. На экране подготовки есть строка «If first run, Strongly recommend switching to ‘Custom mode’ Reduce simulation rounds, to quickly preview effects and reduce error risk». То есть система советует уменьшить число раундов вручную. Совет и есть весь механизм защиты: он ничего не ограничивает, галочка Custom по умолчанию выключена, и кнопка запуска доступна при любом числе.

Вывод: конверсия «дни → раунды ×24» это свойство шаблона конвейера, а не моделей; собственный диапазон системы (24–168 часов) не подкреплён кодом и превышен моими прогонами в 2 и 12.9 раза. Код не ограничивает, интерфейс предупреждает текстом и всё равно даёт запустить.

Обязательство, которое система не может выполнить

Обязательство на 2160 раундов было бы всего лишь дорогим, если бы система могла его исполнить. Она не может, и это документировано её собственным поведением.

Агенты MiroFish накапливают память без ограничения: контекст растёт от раунда к раунду, вызовы становятся тяжелее и медленнее, пока не начинают упираться в лимиты провайдера (ошибки 429) и ошибки размера и валидации запроса (400). Причина троттлинга здесь в росте памяти.

Существенно, что во всех наблюдавшихся коллапсах деградация определялась числом агентов, а не семейством модели (все коллапсы произошли на мирах DeepSeek разного размера; кросс-семейные прогоны были короче зоны деградации, так что независимость точки коллапса от семейства остаётся непроверенной):

  • мир из 7 агентов (14-дневный прогон, 336 раундов) вошёл в каскад деградации примерно с раунда 300: последние ~30 раундов самого длинного завершившегося прогона прошли в каскаде;

  • мир из 4 агентов (90-дневный прогон) продержался примерно до раунда 515;

  • миры из 18–19 агентов (инъекционные прогоны второй статьи) потребовали контролируемой остановки уже на раундах 45 и 22;

  • чистые 168-раундовые прогоны с 10–11 агентами (Claude, Gemini, GPT-4.1) завершились без единой ошибки: они короче своей зоны деградации.

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

Кнопки «стоп» нет

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

Блок «Preparation completed»: тумблер Custom выключен, оценка машинного времени «IfAgentScale is100: Estimated time 1296 minutes», из действий доступны только запуск симуляции и возврат к графу. Элемента остановки на экране нет

Интерфейс, не запросивший подтверждения обязательства, не предоставляет и способа от него отказаться.

После передачи находок мейнтейнерам участник сообщества независимо подтвердил это в трекере проекта, с уточнением: обработчик остановки в коде фронтенда существует (handleStopSimulation в компоненте симуляции), но ни один элемент интерфейса к нему не привязан. А следом он открыл и pull request с исправлением. Возможность реализована на бэкенде, продублирована во фронтенд-коде и обрывается на последнем звене, непривязанной кнопке. Ещё один дефект границы, на этот раз внутри одного слоя.

Мой 90-дневный прогон стал прецедентом. К раунду 592 осмысленная активность агентов прекратилась; единственным способом завершить процесс оказался эндпойнт, найденный чтением исходников бэкенда: недокументированный POST /api/simulation/stop. Я описываю этот прогон во всей серии тем, чем он был: контролируемым тестом, завершённым намеренно на раунде 592 с зафиксированной главной наблюдаемой величиной (само обязательство в 2160 раундов плюс 592 раунда данных о деградации), а не завершённым 2160-раундовым прогоном. Завершения работы я не наблюдал: зафиксированы принятое обязательство, деградация и остановка способом, которого система пользователю не предоставляет.

Вывод: деградация определяется числом агентов; обязательства конфигурации не сверяются с ёмкостью; штатного способа остановить запущенную симуляцию у пользователя нет.

Границы находки

Границы находки описаны здесь, в основном тексте, а не в приложении. Большинство ячеек матрицы это n=1: один 14-дневный прогон, один 90-дневный, по одному прогону с горизонтом на Claude, Gemini и GPT-4.1; закон точен на каждом имеющемся наблюдении, но наблюдений немного, и репозиторий открыт именно для того, чтобы их стало больше. Четыре семейства это не все семейства: Llama, Mistral, Qwen и другие я не проверял. Все четыре проверенных чекпойнта era-matched на окно Q1–Q2 2025, это снимок одного поколения, хотя механизм, будучи кодом конвейера, а не поведением модели, должен пережить смену поколений. Дефолт 72 неоднозначен (выбор модели или резерв кода по сохранённым конфигурациям различить нельзя); дефолты 96/120/144 однозначно выбраны моделями. Наконец, все наблюдавшиеся коллапсы произошли на DeepSeek-мирах; зависимость точки коллапса от семейства не исследована.

Три находки одной серии

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

Silent Failure (статья 1): бэкенд корректно и быстро отказывает на пустом входе, за десятки миллисекунд; статусный API отдаёт отказавшую задачу как выполненную, и интерфейс показывает бессрочный индикатор прогресса. Дефект не в обработке, а в статусном контракте, доставляющем диагностику.

Graph-Gated Blindness (статья 2): итоговый отчёт не флагирует ничего, что не захвачено графом знаний; захват делает распознавание возможным, но не гарантирует его. Инъекция противоречий в граф переключает отчёт из слепоты в распознавание (p = 1.26×10⁻⁶). У системы два слоя видимости, и пользователю по умолчанию доступен худший.

Unbounded Autonomous Commitment (статья 3): фраза запроса конвертируется в вычислительное обязательство детерминированным коэффициентом ×24; собственный лимит системы существует только как текст в промпте; исполнить длинные обязательства архитектура не может, отменить их пользователь не может.

У трёх находок один общий знаменатель. Каждая находка живёт на границе между компонентами: между бэкендом и интерфейсом (недоставленный отказ), между стадией извлечения и стадией отчёта (граф как бутылочное горлышко), между генератором конфигурации и раннером (непроверенное обязательство). Внутри стадий система в основном ведёт себя корректно: отказы возникают там, где выход одного компонента становится входом другого и никто не проверяет контракт. Модели при этом делали ровно то, что умеют: заполняли числа, пересказывали графы, поддерживали персоны. Ни одна из трёх находок не исправляется заменой модели на более новую.

Отсюда практическое резюме серии: три вопроса к любой системе класса «документы → симуляция → прогноз», своей или вендорской:

  1. Каждая фраза запроса это конфигурация. Естественный язык в таких системах выполняет роль конфигурационного файла, не имея ни одной из его проверок. Во что компилируется каждая временна́я, пространственная и масштабная формулировка запроса?

  2. Где ограничения, в коде или в прозе? У исследованного стека документированный диапазон есть; он живёт в промпте и потому не связывает ничего. Вопрос не «есть ли лимит», а «покажите строку кода, которая его применяет, и что происходит при превышении».

  3. Как остановить? Прежде чем оценивать, насколько хорошо автономная система работает, стоит установить, как прекратить её работу. Если ответом оказывается недокументированный эндпойнт, найденный в исходниках, ответ об операционной зрелости уже получен.

Репликация и материалы

Всё открыто в репозитории github.com/Quantum-eon/mirror-audit: манифесты всех 19 прогонов с числами раундов и конфигурациями, сиды, логи с датами, пре-регистрация GPT-4.1-репликации, данные деградации 90-дневного прогона. Стенд: nikmcfly/MiroFish-Offline, docker compose up -d, ваш ключ OpenRouter. Про 90-дневную репликацию: закладывайте многосуточное время, следите за деградацией после ~R500 и заранее возьмите из исходников эндпойнт остановки.

Все находки серии переданы мейнтейнерам до публикации: поведение этой статьи это issues #54 (отсутствие валидации total_simulation_hours), #55 (отсутствие остановки в UI), #56 (неограниченный рост памяти агентов); полный список это #53–#58. Бэкенд системы в исследованных точках преимущественно корректен; дефекты серии это дефекты границ между компонентами, и аналогичные границы существуют во многих конвейерах. Исправления начались ещё до публикации: сообщество уже открыло pull request-ы #61 (кнопка остановки симуляции) и #62 (явный отказ чата отчёта).

В отчёте исследования находка этой статьи называется Unbounded Autonomous Commitment: система, конвертирующая фигуру речи в формальное обязательство (по курсу, многократно превышающему её собственный документированный диапазон), которое она не может исполнить и которое пользователь не может отменить. Не потому, что какая-то модель ошиблась: модели лишь заполнили число. А потому, что ни один компонент конвейера это число не проверил.


Это независимое исследование: оно никем не заказано и не спонсировано. Все «дефектные» сущности (Валдория, вымышленный банк и прочие) фиктивны и снабжены дисклеймерами в самих сидах. Материалы серии не являются финансовой или инвестиционной рекомендацией. DOI данных: 10.5281/zenodo.21788962.

Комментарии (0)