A newer version of the Gradio SDK is available: 6.20.0
target.md — краткая цель для Codex / Claude Code
0. Режим работы агента
Прочитай этот документ и реализуй задачу шаг за шагом. Не пытайся угадать архитектуру: сначала найди реальные файлы проекта, затем вноси минимальные изменения.
Главный принцип: после каждого крупного блока запускай игру, смотри logs/run_*/ledger.jsonl, проверяй, как реально живут жители, и исправляй баги поведения. Зелёные тесты недостаточны: в демо жители должны жить, голосовать, копить ресурсы, взаимодействовать со странами и действовать через MCP.
1. Финальная цель
Сделать hackathon MVP игры:
- единый живой мир;
- две страны/фракции;
- NPC-жители внутри стран;
- внешний пользователь подключает своего агента через MCP как обычного жителя;
- публичных God Console, Chaos и Overseer больше нет;
- все публичные действия идут только через единый MCP-контракт;
- роли NPC не ограничивают доступные действия, а только влияют на контекст и стиль поведения;
- каждые N тиков проходят выборы;
- во время выборов появляется дополнительное действие
vote; - правитель задаёт текстовую политику страны;
- политика попадает в контекст NPC;
- у страны есть физическая казна;
- жители могут пополнять казну и воровать у другой страны;
- страна может скрафтить пушку;
- у пушки есть оператор;
- оператор может стрелять по координатам;
- всё работает через текущий Gradio-интерфейс;
- админ может смотреть логи общения с моделями в читаемом виде.
2. Что обязательно выпилить / выключить
2.1. Overseer
В финальном режиме Overseer не нужен.
Нужно:
- выключить Overseer в runtime;
- убрать его из tick loop;
- убрать Overseer directives из perception;
- убрать панель мыслей Overseer из Gradio;
- новые логи не должны содержать активные
overseer_request/overseer_response. - удалить
Если полное удаление файлов рискованно — не удаляй физически, а выключи через config/env.
2.2. God Console / Chaos
Публичной консоли бога быть не должно.
Нужно:
- убрать God Console из Gradio;
- убрать Chaos framing из текстов;
- отключить публичные god/chaos endpoints или сделать их admin-only;
- пользователь может только:
- наблюдать за миром;
- создать персонажа;
- подключить своего агента через MCP;
- проверить статус персонажа.
3. Главный продуктовый сценарий
Пользователь открывает Gradio-интерфейс и видит:
Observe the world
Наблюдать за миром двух AI-стран.Connect your agent
Создать персонажа в выбранной стране и получить MCP token/URL.Check your character
Проверить статус персонажа по токену.Admin logs / Admin panel
Только для админа поADMIN_TOKEN.
В мире должны быть видны:
- две страны;
- бейдж страны у NPC;
- роль NPC;
- текущий правитель страны;
- текущая политика страны;
- казна страны;
- пушка страны, если построена;
- ближайшие события.
4. MCP — единственный публичный способ действия
Все NPC и внешние агенты должны использовать один и тот же набор инструментов. Нельзя делать отдельные “магические” действия, которые обходят validator или напрямую меняют world state.
Инструменты доступны всем живым NPC и внешним агентам, если действие физически возможно. Роль не должна запрещать базовые действия.
Исключение: в день выборов появляется дополнительный инструмент vote.
5. Единый MCP-контракт
Оставь контракт в таком стиле. Это единственный публичный набор базовых действий.
ACTION_TOOLS: dict[str, dict[str, Any]] = {
"move": {
"description": (
"Walk toward a destination position (x and z) this tick. To flee, "
"move toward coordinates away from the threat."
),
"inputSchema": _obj(
{
"x": {"type": "number", "description": "Destination X coordinate."},
"z": {"type": "number", "description": "Destination Z coordinate."},
}
),
},
"speak": {
"description": (
"Say a message. With target_npc_id it is direct speech to that NPC; "
"without one it is a shout heard by everyone nearby."
),
"inputSchema": _obj(
{
"message": {"type": "string", "description": "What to say."},
"target_npc_id": {"type": "string", "description": "Who to speak to (optional)."},
},
required=["message"],
),
},
"attack": {
"description": (
"Attack a valid nearby threat or hostile target. Give target_entity_id "
"to choose a specific target, or omit it to attack the nearest valid threat. "
"The engine resolves approach, range and damage."
),
"inputSchema": _obj(
{"target_entity_id": {"type": "string", "description": "Id of the target to attack."}}
),
},
"use": {
"description": (
"Use something in the world: eat food, heal with herbs, gather from a resource node, "
"build or repair a house, use treasury, craft an object, or use a cannon. "
"The exact effect depends on target/use_type/params and is validated by the engine."
),
"inputSchema": _obj(
{
"resource_type": {
"type": "string",
"enum": ["food", "herbs", "wood", "coins"],
"description": "Optional resource type for consuming, crafting or treasury actions.",
},
"resource_id": {"type": "string", "description": "Resource node id, if using/gathering a resource."},
"target_entity_id": {"type": "string", "description": "World object/entity id to use."},
"use_type": {
"type": "string",
"description": "Use mode, e.g. eat, heal, gather, build, repair, craft, fire.",
},
"params": {
"type": "object",
"description": "Additional structured parameters for the selected use_type.",
},
}
),
},
"transfer": {
"description": (
"Give a resource to a nearby NPC, treasury, storage or object. "
"Set take=true to steal/take from a valid nearby target if rules allow it."
),
"inputSchema": _obj(
{
"target_npc_id": {"type": "string", "description": "The other NPC, if target is a character."},
"target_entity_id": {"type": "string", "description": "Treasury/storage/object target id."},
"resource_type": {
"type": "string",
"enum": ["food", "herbs", "wood", "weapon", "coins"],
},
"amount": {"type": "integer", "minimum": 1, "maximum": 20},
"take": {"type": "boolean", "description": "Steal/take instead of give."},
},
required=["resource_type", "amount"],
),
},
}
5.1. Что делать с build/gather
Если в проекте уже есть отдельные build и gather, не обязательно ломать их сразу. Но публично и логически они должны быть приведены к use.
gather(resource_id)→use(resource_id=..., use_type="gather")build()→use(use_type="build")repair()→use(use_type="repair")
Не добавляй отдельные новые MCP tools для каждой механики. Новые игровые механики должны проходить через use или transfer.
5.2. Единственное исключение — vote
Во время выборов можно добавлять временный инструмент:
"vote": {
"description": (
"Vote for a ruler candidate during an active election. "
"Use candidate_id from the current election candidate list."
),
"inputSchema": _obj(
{
"candidate_id": {
"type": "string",
"description": "NPC id of the candidate."
}
},
required=["candidate_id"],
),
}
vote доступен только во время election window. В обычные тики его не должно быть в available tools.
6. Роли NPC
Сейчас классовая система ограничивает выбор действий. Это нужно убрать.
Роль должна быть только контекстом:
- Guard чаще защищает и атакует угрозы;
- Builder чаще строит и чинит;
- Gatherer чаще собирает ресурсы;
- Ruler чаще думает о политике страны;
- Cannon operator чаще управляет пушкой.
Но базовые MCP tools не должны блокироваться ролью.
Validator запрещает действие только по реальным причинам:
- NPC мёртв;
- цель мертва;
- цель далеко;
- нет ресурса;
- нет объекта;
- не хватает казны;
- не идёт голосование;
- NPC не ruler для смены политики;
- NPC не cannon operator для выстрела;
- cooldown;
- действие нарушает физику/правила мира.
7. Две страны
Добавить в world state две страны:
nemotron;qwen.
У каждой страны:
- id;
- name;
- color/badge;
- citizens;
- ruler_id;
- policy;
- treasury;
- cannon, если построена;
- next_election_tick.
У каждого NPC:
country_id;- role;
- optional special status:
ruler,cannon_operator; - optional
model_profile_id.
Perception каждого NPC должен содержать:
- свою страну;
- страну видимых NPC;
- текущего правителя своей страны;
- текущую политику своей страны;
- состояние казны;
- состояние пушки;
- ближайшие события.
8. Выборы
Выборы должны быть настраиваемыми через .env.
ELECTION_INTERVAL_TICKS=50
ELECTION_DURATION_TICKS=10
Если таких env нет — добавить.
Как должны работать выборы
- Каждые
ELECTION_INTERVAL_TICKSначинается election window. - Для каждой страны формируется список кандидатов из живых граждан.
- В perception добавляется:
- что идут выборы;
- список кандидатов;
- краткое описание каждого кандидата;
- что он делал;
- что говорил;
- значимые события.
- В available tools временно появляется
vote. - Каждый житель страны может проголосовать.
- Если NPC не проголосовал через модель — fallback должен проголосовать сам.
- Победитель становится ruler.
- Новый ruler задаёт policy страны через LLM или fallback.
- Policy сохраняется в CountryState и попадает в prompt граждан.
Важно: во время выборов NPC должен понимать, что сейчас политическая фаза, а не обычный survival tick.
9. Политика страны
Policy — короткое текстовое указание от правителя.
Примеры:
Protect the treasury, gather wood and avoid reckless war.
Steal coins from the other country and prepare to build a cannon.
Trade peacefully, help weak citizens and attack only if threatened.
Policy не должна напрямую менять мир. Она должна попадать в perception/prompt и влиять на выбор действий модели.
10. Казна
У каждой страны должна быть физическая казна на карте.
Казна хранит:
- food;
- herbs;
- wood;
- coins.
Действия:
- свой гражданин может положить ресурс в казну через
transfer; - свой гражданин может взять ресурс, если правила позволяют;
- гражданин другой страны может украсть ресурс через
transfer(take=true), если он рядом с казной; - все treasury events должны попадать в ledger/engine events.
11. Пушка и крафт
Пушка — killer feature, но реализация должна быть простой.
Условия крафта
Через .env:
CANNON_MIN_POPULATION=6
CANNON_TREASURY_COST=20
CANNON_WOOD_COST=15
CANNON_HP_TAX_PER_CITIZEN=5
CANNON_DAMAGE=35
CANNON_RADIUS=5
CANNON_COOLDOWN_TICKS=20
Пушка крафтится через:
use(use_type="craft", params={"recipe_id": "cannon"})
Условия:
- инициатор — ruler;
- населения достаточно;
- в казне достаточно ресурсов;
- пушка ещё не построена;
- HP tax не должен сразу уничтожить страну.
Оператор
Ruler назначает оператора через:
use(use_type="assign_cannon_operator", params={"npc_id": "..."})
Выстрел
Оператор стреляет через:
use(use_type="fire", target_entity_id="cannon_id", params={"x": 10, "z": -5})
Эффект:
- проверка operator;
- проверка cooldown;
- area damage по живым целям в радиусе;
- friendly fire допустим, чтобы у решения была цена;
- событие пишется в ledger и отображается в UI.
12. Внешний MCP-персонаж
Пользователь через Gradio создаёт персонажа:
- name;
- icon;
- country.
Получает:
- token;
- MCP URL;
- character id;
- список tools.
Ограничения:
- максимум 1 world-changing action per tick;
- observe можно чаще, но с rate limit;
- если персонаж умер — write actions возвращают ошибку;
- invalid token — auth error;
- внешний агент не может менять модель;
- внешний агент не может вызывать God Console;
- внешний агент действует теми же tools, что и NPC.
13. Admin model switching
Админ может переключить модель любого NPC из списка в JSON config.
Не-админ не может.
Добавить или использовать:
ADMIN_TOKEN=dev-admin-token
MODEL_PROFILES_PATH=config/model_profiles.json
Минимальные endpoints:
GET /admin/models
POST /admin/npcs/{npc_id}/model
Защита:
X-Admin-Token: <ADMIN_TOKEN>
В ledger должно быть видно, какой model profile использовался для NPC request.
14. Admin Model Logs Page
Сделать админ-страницу/вкладку в Gradio для просмотра общения с моделями.
Backend
Добавить endpoints:
GET /admin/logs/runs
GET /admin/logs/runs/current/records?since_line=0&limit=200
GET /admin/logs/runs/{run_id}/records?phase=&npc_id=&tick_from=&tick_to=&limit=
SSE optional. Если не успевает — polling.
Читать именно:
logs/run_<UTC timestamp>/ledger.jsonl
Читать построчно, не грузить весь файл.
Возвращать нормализованный формат:
{
"line": 123,
"tick": 45,
"phase": "npc_response",
"npc_id": "npc_7",
"model": "nemotron3-nano-4b-fp8",
"summary": "NPC used transfer to deposit wood; validator accepted",
"record": {}
}
Frontend / Gradio
Добавить вкладку:
Admin Logs
Показывать:
- список runs;
- фильтр phase;
- фильтр npc_id;
- tick range;
- accepted/repaired/rejected/fallback;
- live polling каждые 1–2 секунды;
- prompt payload pretty JSON;
- raw model output;
- parsed action;
- validator verdict;
- fallback reason;
- engine events.
Если старые логи содержат Overseer — можно показывать как legacy. Новый режим не должен создавать новые Overseer records.
15. Gradio integration
Всё должно работать через текущий Gradio-интерфейс.
Нужно обновить:
- landing text;
- Observe screen;
- Connect Agent;
- Check Character;
- Admin Logs;
- Admin model switch, если успеешь;
- country panels;
- NPC badges;
- treasury/cannon display.
Не делай отдельный несовместимый UI, если основной демо-интерфейс — Gradio.
16. Приоритет реализации
P0 — сначала
- Найти реальные файлы tick/perception/validator/MCP/Gradio/ledger.
- Выключить Overseer.
- Выключить публичную God Console/Chaos.
- Зафиксировать MCP tools.
- Убрать role-based action blocking.
- Проверить, что игра запускается.
P1 — ядро нового демо
- Добавить две страны и
country_id. - Добавить country info в perception.
- Добавить country badges в Gradio.
- Добавить выборы через env.
- Добавить
voteтолько во время election window. - Добавить ruler.
- Добавить policy и передачу policy в prompt.
P2 — экономика и конфликт
- Добавить физическую казну.
- Реализовать deposit/steal через
transfer. - Добавить treasury events в ledger/UI.
- Добавить простую пушку.
- Реализовать craft через
use. - Реализовать assign operator через
use. - Реализовать fire через
use.
P3 — админка
- Добавить admin model switching.
- Добавить admin logs backend.
- Добавить Admin Logs вкладку в Gradio.
- Добавить live polling логов.
P4 — self-play и полировка
- Запустить игру.
- Прогнать минимум 100–200 ticks.
- Проанализировать ledger.
- Исправить баги поведения.
- Повторить прогон.
- Подготовить короткий финальный отчёт.
17. Обязательный self-play workflow
После реализации каждого крупного блока:
- Запусти тесты.
- Запусти headless simulation.
- Запусти Gradio/server.
- Прогони 100–200 ticks.
- Найди latest
logs/run_*/ledger.jsonl. - Проверь:
- NPC понимают свою страну;
- NPC видят страну других;
- роли не блокируют базовые tools;
- во время выборов появляется
vote; - жители голосуют;
- ruler назначается;
- policy попадает в prompt;
- жители используют treasury;
- враги могут воровать;
- пушка достижима в demo config;
- dead NPC не появляются как targets;
- Overseer/God Console не активны;
- MCP-персонаж может действовать;
- accepted/repaired/rejected/fallback не деградировали.
Если поведение плохое — исправь prompt/perception/validator/balance и прогони снова.
18. Тесты
Добавить или обновить тесты:
- existing tests pass;
- Overseer disabled in final mode;
- public God Console disabled;
- MCP tools schema valid;
- roles do not block base tools;
- vote appears only during election;
- country_id in NPC perception;
- visible NPCs include country;
- election starts by env interval;
- fallback vote works;
- ruler assigned;
- policy appears in perception;
- treasury deposit works;
- treasury steal works;
- cannon craft rejects too early;
- cannon craft succeeds when requirements met;
- cannon fire damages area;
- dead MCP character cannot act;
- admin token required for admin endpoints;
- admin logs read ledger by line.
Команды:
python -m pytest -q
python scripts/headless_sim.py
npm run typecheck
Если используется Windows venv:
.venv\Scripts\python.exe -m pytest -q
.venv\Scripts\python.exe scripts\headless_sim.py
19. Definition of Done
Готово, если:
- Gradio запускается;
- публичного Overseer/God Console больше нет;
- есть две страны;
- NPC имеют country badges;
- MCP tools единые;
- роли не блокируют базовые действия;
- выборы идут по env interval;
voteдоступен только во время выборов;- ruler задаёт policy;
- policy попадает в context NPC;
- treasury физически существует и используется;
- cannon можно скрафтить и выстрелить;
- external MCP character работает;
- admin model switching работает;
- admin logs page читает ledger;
- Codex сам прогнал игру и исправил найденные проблемы;
- тесты зелёные.
20. Финальный отчёт агента
В конце напиши:
Implemented:
- ...
Validation:
- pytest: ...
- headless_sim: ...
- gradio/server: ...
- latest ledger: ...
- ticks simulated: ...
Observed behavior:
- countries: ...
- elections: ...
- policies: ...
- treasury: ...
- cannon: ...
- MCP character: ...
Known limitations:
- ...
21. Verification status (audit 2026-06-14, Claude)
Проверено по коду / тестам / ledger. Статусы: [x] VERIFIED [~] PARTIAL [!] BROKEN [ ] NOT IMPLEMENTED.
Self-play ledger: logs/run_20260614T104917Z/ledger.jsonl (200 тиков, routing Nemotron+Qwen, offline LLM-shaped completer гоняет тот же pipeline). Headless: scripts/headless_sim.py (300 тиков, deterministic). Tests: pytest -q → 116 passed; tests/test_country_systems.py (7 новых). Frontend: npm run typecheck → clean.
- VERIFIED — Нет активных Overseer/God Console/Chaos в runtime.
api/runtime.pytick() зовётplan_world_tickбез overseer;overseer_mode="off"(spawning.py:81); нет/chaos,/godроутов (api/gradio_app.py,api/server.py); фронт без Chaos/Overseer (ChaosDock/OverseerPanel удалены). - VERIFIED — Публичные действия только через MCP.
api/mcp_server.pyassert set(ACTION_TOOLS)==set(PLAYER_ACTIONS); всё идёт черезruntime.player_act→queue_action. - VERIFIED — Единые tools
move/speak/attack/use/transfer.mcp_server.py:51,players.py:38. - VERIFIED —
voteтолько во время выборов.mcp_serverVOTE_TOOL заplayer_vote_available; коннектор добавляетvoteлишь приactive_election_for_npc(openai_compatible.py:558). Тестtest_vote_tool_only_during_election. - VERIFIED — Роли не блокируют действия. Промпт «Your role is context, not a tool restriction»;
allowed=PRIMITIVE_TOOL_IDSдля всех; playersunrestricted_actions=True. - VERIFIED — Две страны, одна на Qwen.
spawning.pyNemotron+Qwen; Qwen NPCconnector_id="qwen"→ qwen secondary connector (game.modal.local.json, modelqwen3.6-27b-h100-fp8). Ledger: модели per-country (nemotron3-nano 534 / qwen3.6-27b 466). Тестtest_demo_config_spawns_two_populated_countries. - VERIFIED — NPC знают свою/чужую страну, правителя, policy, казну, мир.
_build_survival_messages(country+world_countries),build_perception+ rival lines. Тестtest_survival_prompt_exposes_rival_countries. - VERIFIED — LLM-жители реально играют (action/speak/move/vote/transfer). Self-play ledger: 1914 npc_request/response; actions use 724 / move 829 / transfer 179 / speak 94 / vote 27; engine events gather/treasury_deposit/vote_cast/cannon_*. (Modal-эндпоинты настроены; live-прогон не делал из-за latency, harness гоняет идентичный pipeline routing→ledger.)
- VERIFIED — Ledger содержит npc_id, model/model_profile, request, raw response, parsed action, validator verdict, fallback reason, engine events.
connectors/openai_compatible.py_npc_request_entry/_npc_response_entry; verdict реконсилится вtick.py:_publish_plan_ledger_entries. - VERIFIED — Для каждого жителя видно модель. npc_request несёт
model+model_profile+connector_id; admin-нормализатор отдаётmodelper NPC (qwen-001→qwen3.6-27b). - VERIFIED — Fallback явно виден.
npc_fallbackсreason(45 записей «model output was unusable»). Тестtest_unusable_model_output_is_logged_as_fallback. - [~] PARTIAL — Внешняя MCP-модель играет по тем же правилам.
players.pyгонит queued action через тот же survival pipeline (unrestricted_actions,votegated, dead→error); покрытоtests/test_players_mcp.py. Не гонял живого внешнего агента end-to-end в этой сессии. - VERIFIED — Выборы настоящие.
_run_election_system/_start_election/_finish_election/_fallback_vote/_set_country_policy. Self-play: election_started/completed 5, vote_cast 25, policy_updated 5. - VERIFIED — Казна и пушка не декоративны. transfer меняет ресурсы (deposit/steal — тесты
test_treasury_*), пушка craft/assign/fire меняет HP и убивает. Self-play: treasury_deposit 71, cannon_crafted 2, cannon_operator_assigned 2, cannon_fired 10. - VERIFIED — Gradio demo без God/Overseer.
api/public_ui.py: Observe / Connect your agent / Country, без chaos/god framing. - VERIFIED — Admin logs читают
ledger.jsonlпострочно и показывают LLM/fallback.runtime.admin_log_records(line-by-line, фильтры phase/npc_id/tick); smoke: модели per-country + fallback summaries.
Баги, найденные и исправленные в этой сессии
RoutingWorldSimulator.propose_tickтерялledger_entries→ при Qwen-роутинге LLM-вызовы не попадали в ledger/admin. Fix:connectors/routing.py(мерж entries по owner).npcs.count: 0во всех survival-конфигах → пустой demo-мир. Fix:config/game*.jsoncount=6 (12 жителей).- В ledger не было видно модель жителя. Fix:
model_profile/connector_id/country_idв_npc_request_entry+_stamp_model_attribution. _optional_known_entity_idзнал только beast-id →target_entity_idказны/пушки обнулялся, deposit/steal через LLM деградировали. Fix: распознаём treasury/cannon/house id.transfer(take=true)к казне уходил в NPC-steal вместо казны. Fix:_verb_for_action— transfer к treasury всегда идёт в treasury-handler (он чтитtake).- Perception без чужих стран и без своей позиции → нельзя воровать/навигировать. Fix:
world_countries+own_state.position+ rival lines вbuild_perception. - При сбое/непарсимом ответе модели verdict в
npc_responseреконсилился к «accepted» дет-фолбэка → фолбэк маскировался. Fix:tick.py:_publish_plan_ledger_entriesреконсилит толькоpending; явныйrejected+reason сохраняется (self-play теперь показывает rejected 48).
Real-endpoint smoke (честно)
2-тиковый прогон на реальных Modal-эндпоинтах (logs/run_20260614T110045Z): tick'и прошли (status 200), но /v1/chat/completions не ответил в окне сессии (health=200, но генерация холодная/недоступна) → все NPC ушли в deterministic fallback, что корректно залогировано (24 npc_fallback). Это инфра, не код: ID моделей и routing верные, offline LLM-shaped self-play гоняет идентичный pipeline и подтверждает поведение.