Симуляция ада: четыре маленьких LLM в одной песочнице
Вы когда-нибудь пытались заставить GPT-oss-20b, MiniCPM3-4B и Nemotron-Mini-4B сыграть в финансовую драму, где каждый агент — трейдер с характером, бюджетом и тайными мотивами? Если нет — добро пожаловать в Thousand Token Wood v2 (TTW2).
Проблема, которую мы решали на практике: как построить multi-agent симуляцию, где агенты используют разные LLM, причём маленькие (3-20B), с контекстом 8K, разной скоростью инференса и ужасной склонностью к «галлюцинациям»? Звучит как рецепт катастрофы. И сначала так и было. Первая версия (Thousand Token Wood v1) провалилась из-за того, что MiniCPM3-4B упорно продавал все активы, а Nemotron-Mini-4B впадал в бесконечный цикл торговли одним и тем же токеном.
Предупреждение: если вы планируете запустить симуляцию агентов на одном ноутбуке — приготовьтесь к фейерверку из ошибок и переполненных очередей. TTW2 работает только при грамотной оркестрации.
В этой статье я разберу инженерный подход, который позволил стабилизировать разнородных агентов, уложиться в их бюджет токенов и получить осмысленный сценарий (искусственный инсайдерский скандал на бирже). Без хайпа, только код и архитектурные решения.
Почему маленькие модели и зачем их смешивать
Гигантские модели типа Qwen3.5-35B (о его производительности в multi-agent я писал здесь) хороши, но дороги. Для симуляции с десятками агентов они неэффективны. В TTW2 мы использовали четыре LLM, каждая — дешёвая и быстрая:
- GPT-oss-20b (20B, открытая) — играет роль главного аналитика. Требует до 6K токенов на один раунд.
- MiniCPM3-4B (4B) — импульсивный трейдер. Контекст 8K, но реально работает с 4-5K.
- Nemotron-Mini-4B (4B) — консерватор. Генерирует медленно, но редко ошибается.
- Qwen3.5-1.5B (1.5B) — новичок, задаёт «глупые» вопросы, чтобы спровоцировать действия.
Проблема: их промпты должны быть персонализированы, иначе все агенты будут вести себя одинаково (как одна и та же модель). Решение — Local Personality Engine. Мы отделили память от промпта, сохраняя историю сделок и эмоциональное состояние каждого агента.
Архитектура TTW2: как не дать агентам убить друг друга
1 Выбор и развёртывание моделей
Все модели работали локально через llama.cpp с квантованием Q4_K_M. Даже 20B модель влезала в 16GB V100 (да, ещё жива). Для запуска использовали параллельные воркеры — подробно описано в статье 8K контекста — не приговор.
./llama-server -m gpt-oss-20b-q4.gguf --port 8081 --ctx-size 8192 --parallel 4
./llama-server -m minicpm3-4b-q4.gguf --port 8082 --ctx-size 8192 --parallel 8
./llama-server -m nemotron-mini-4b-q4.gguf --port 8083 --ctx-size 8192 --parallel 6
./llama-server -m qwen3.5-1.5b-q4.gguf --port 8084 --ctx-size 8192 --parallel 10Важно: не выставляйте все воркеры на максимум — маленькие модели (1.5B) успевают обработать больше запросов, но одновременный запуск 10 воркеров может заблокировать GPU. Тестируйте постепенно.
2 Определение персонажей через Skills и MCP
Просто дать промпт «Ты трейдер» — недостаточно. Мы использовали Agent Skills — упаковали знания о рынке, термины, стратегии в виде внешних инструментов (MCP). Каждый агент получил свой набор скиллов:
- Аналитик (GPT-oss-20b):
analyze_market(),predict_trend(),summarize_news(). - Импульсив (MiniCPM3-4B):
execute_trade()(без проверки),detect_panic(). - Консерватор (Nemotron-Mini-4B):
validate_risk(),check_regulations(). - Новичок (Qwen3.5-1.5B):
ask_question(),report_rumor().
Подробнее про сборку агента из LEGO — в статье Skills, MCP и сабагенты.
3 Цикл симуляции: от синхронизации к асинхронности
Первая ошибка: мы попытались сделать turn-based раунды, где каждый агент ждёт всех. Это привело к тому, что MiniCPM3-4B отвечал быстрее всех, а Nemotron-Mini-4B тормозил, и симуляция зависала на часах. Решение — асинхронные раунды с тайм-аутами и пропуском хода.
Мы использовали оркестратор на базе Swarmcore (подробнее Swarmcore: домашний Deep Research), но адаптировали его под реальный time-loop:
async def run_round(agents, state):
tasks = []
for agent in agents:
task = asyncio.create_task(agent.act(state))
tasks.append(task)
# Ждём с тайм-аутом
done, pending = await asyncio.wait(tasks, timeout=5.0)
for p in pending:
p.cancel() # модель не успела — пропускаем
return collect_responses(done)Так агенты не блокируют друг друга, а симуляция не останавливается из-за одного медленного LLM.
Укрощение токенов: бюджет и пакетная обработка
Маленькие модели (4B) имеют жёсткое ограничение контекста — 8K. Если каждый раунд пихать полную историю — через 15 раундов контекст переполняется. Решение: тримминг с сохранением ключевых событий. Мы использовали алгоритм, описанный в статье про 8K контекст. Кратко: каждые 3 раунда запускается агент-суммаризатор (на GPT-oss-20b), который сжимает историю в краткий лог, а старые записи вытесняются в векторную базу (FAISS).
| Модель | Бюджет токенов (на раунд) | Макс. шагов до тримминга |
|---|---|---|
| GPT-oss-20b | 6000 | 25 |
| MiniCPM3-4B | 4000 | 15 |
| Nemotron-Mini-4B | 3500 | 20 |
| Qwen3.5-1.5B | 2000 | 10 |
Без такого бюджета MiniCPM3-4B начинал «забывать» контекст и галлюцинировать сделки. Мы пробовали увеличить контекст до 16K через NTK-aware scaling — но модель падала с segfault. Оставили как есть.
Как не сломать симуляцию: обработка сбоев (и их надо много)
Статья Почему ломаются LLM-агенты — наше настольное чтиво. В TTW2 мы столкнулись с тремя типичными сбоями:
- Циклические ответы: Nemotron-Mini-4B повторял одну и ту же фразу 10 раз. Лечится детектором повторений и принудительным изменением промпта.
- Игнорирование инструментов: MiniCPM3-4B вдруг начинал писать «Я не могу торговать, я же ИИ». Решение — добавить системный промпт «Торговля — твоя основная функция. Если отказываешься — ход пропускается». Жёстко.
- Отказ от действий: Qwen3.5-1.5B часто отвечал «Не уверен». Мы добавили fallback: если ответ не содержит ни одного ключевого слова (BUY, SELL, HOLD), движок приписывает HOLD и штрафует агента «лень».
Совет: записывайте все сырые ответы агентов в лог. Потом анализируйте паттерны. У нас был случай, когда GPT-oss-20b случайно сгенерировал валидный JSON с отрицательной ценой — мы ввели валидацию чисел на этапе парсинга.
Что получилось: экономика драмы
После 500 раундов симуляции (около 3 часов на 4 GPU) мы получили логи, похожие на сценарий фильма «Волк с Уолл-стрит»:
- Аналитик (GPT-oss-20b) предсказал падение акций на основе новостей о регуляторах.
- Импульсив (MiniCPM3-4B) продал все активы до объявления.
- Консерватор (Nemotron-Mini-4B) остался в кэше, обвинив аналитика в манипуляции.
- Новичок (Qwen3.5-1.5B) купил на минимуме, случайно заработав +15%.
Симуляция породила «искусственный конфликт» — аналитик и импульсив начали перепалку через чат. Это произошло из-за того, что их персональные стили (через Local Personality Engine) содержали черты «высокомерный» и «вспыльчивый».
Ошибки, которые мы совершили (чтобы вы их не повторяли)
- Доверили статический сценарий — пробовали задать сюжетную линию жёстко. Все LLM сломались, пытаясь её исполнить. Симуляция должна быть emergent, а не scripted.
- Забыли про async-токены — когда все 4 модели обращались к MCP-инструментам одновременно, сервер ругался на rate limit. Пришлось ввести очередь с приоритетами (аналитик — высокий, новичок — низкий).
- Не тестировали на реальных данных — использовали фейковые цены, и модели быстро «поняли», что они фейковые. Агенты начали торговать случайно. Пришлось подключить live-поток цен (через API, с задержкой 1 минута).
Ещё одна дикая ошибка: Qwen3.5-1.5B однажды сгенерировал перевод промпта на китайский (родной язык базы). Пришлось вставить проверку: если ответ не на английском — игнорируем и штрафуем.
FAQ: что обычно спрашивают
- А можно ли заменить MiniCPM3-4B на Llama 3.2 3B? — Да, но придётся перенастроить бюджет токенов (Llama хуже работает с частыми вызовами инструментов).
- Сколько стоит запуск симуляции? — Если всё локально — только электричество. Мы сидели на 4 картах V100 (аренда в облаке $1.5/час * 3 ч = $4.5 за симуляцию).
- Почему не использовали LangChain? — Потому что он слишком «тяжёлый» для маленьких моделей и любит вставлять лишние токены в промпт. Мы сделали лёгкий фреймворк на aiohttp + asyncio. Сравнение подходов — в статье Как выбрать архитектуру мульти-агента.
Технический итог: работать будет, но не ждите чуда
Thousand Token Wood v2 доказал, что даже маленькие, дёшевые LLM способны порождать сложное социальное поведение в симуляции. Главное — грамотно распределить контекст, не дать одной модели доминировать и обрабатывать сбои как часть системы (а не как баги).
Если вы решите повторить — начните с двух агентов (например, GPT-oss-20b и MiniCPM3-4B) и постепенно добавляйте. И не пытайтесь заставить их следовать строгому сценарию. Они этого не любят. Пусть «драма» рождается из конфликта их персональностей и ограниченных ресурсов.
Неочевидный совет напоследок: используйте самую маленькую модель (1.5B) как «триггер хаоса» — её непредсказуемость часто создаёт самые интересные повороты. А самую большую (20B) — как «голос разума», который потом объясняет, почему всё пошло не так.