Як я, знаючи лише IF, створив CivicSurvival мод для Cities: Skylines II на 150K+ рядків коду, RAG-сервер і 300 аналізаторів

Мене звати Валентин Курченко-Гай, зараз я викладаю англійську, кодінг для мене — це хоббі. Мій програмістський бекграунд вкладається в одну фразу: я не знаю синтаксису, але написання моду для Cities: Skylines II — це не мій перший ШІ-проєкт. Рік тому я почав «вайбкодити» без бекграунду в IT. До CivicSurvival був великий Python/FastAPI сервер (особистий ШІ-асистент з liveKIT-інтеграцією), на якому я зрозумів, що головне — це не дозволити ШІ зламати архітектуру. Це принципово важливо для контексту цієї статті — я не починав з нуля. Я починав, маючи піврічний досвід роботи із ШІ як основним інструментом.

Але DOTS, ECS, Burst, ComponentLookup, sync point, EntityCommandBuffer, Harmony postfix patch, Coherent UI, Roslyn analyzer — це все було набором слів, кожне з яких я б не зміг визначити без Google. DOTS (Data-Oriented Technology Stack) — це архітектура Unity для дуже швидкого коду, але вона специфічна й значно менш поблажлива до помилок, ніж класичне ООП. C# до цього в руках не тримав. Синтаксис C# я й зараз не знаю.

CivicSurvival стартував 26 грудня 2025. Календарно — 4,5 місяця до бети. Але квітень я майже повністю пропустив, бо була пауза на інший проєкт — тому реально це ~3 місяці чистого часу. За ці три місяці зібрали:

МетрикаЗначення
C# коду158 583 рядки з 700 000 згенерованого
TypeScript/React31 219 рядків
C# систем (ECS)542
UI-компонентів369
Власних Roslyn-аналізаторів300+
Комітів у Git2 503
MCP-сервер для навігації по кодбазівласний (CivicRAG)
Окремий діагностичний модVanillaProfiler
Рекордний місяцьберезень 2026, 895 комітів
Календарний час4,5 місяця
Чистий час розробки~3 місяці (квітень — пауза на інший проєкт)

Спойлер: мод зараз на стадії закритої бети. Якщо ви любите ламати складні системи в Cities: Skylines II, наприкінці статті я поясню, кого саме шукаю для тестування.

Стек вийшов не маленький. Сам мод — це C# поверх Unity DOTS/ECS, Burst, Harmony-патчів і штатних систем Cities: Skylines II. Інтерфейс — TypeScript/React і Coherent UI. Навколо мода живе Python-шар: CivicRAG, MCP-сервер, індексація кодової бази, SQLite з векторним і повнотекстовим пошуком, скрипти експорту моделей, генератори звітів і допоміжні інструменти для аудитів. Плюс VPS-сервер для RAG, Roslyn-аналізатори для C#, ESLint для фронтенду, Git як журнал усієї історії. Тобто це не один файл мода, а невелика екосистема навколо гри.

Я дуже добре пам’ятаю, з чого починав: з людини, яка вміє пояснити «якщо це — зроби те», але не вміє з голови написати нормальний ECS-код і не знає синтаксис достатньо, щоб чесно назвати це ручним програмуванням. Починав без Git, із класичними папками «Final», «working».

Стаття про інше: як виглядає чистий вайбкодинг, якщо не романтизувати його, а обкласти ШІ системою обмежень, перевірок і планування. Що працює, що не працює, і чому без живих бета-тестерів цей процес зараз заходить у глухий кут.

Якщо зовсім коротко: ШІ не замінив мені програміста. Він став дуже швидким виконавцем, а моя роль — намагатися тримати задум, контекст, правила, перевірки й реальну поведінку гри. Я не пишу код у класичному сенсі. Я радше тримаю процес так, щоб система не втрачала сенс і не розсипалася.

Про що мод CivicSurvival

CivicSurvival — total conversion мод для Cities: Skylines II (далі — CS2). Якщо коротко: у мирний містобудівний симулятор я хотів додати речі, які не треба довго пояснювати — відключення світла, генератори, корупцію, паніку, телемарафон, дрони, укриття, втому людей і постійний вибір між «правильно» та «хоч якось пережити ніч».

За жанром це суміш міського симулятора, survival-стратегії та соціальної сатири. Орієнтири: Frostpunk, This War of Mine, Papers Please, Beholder. Але важливо: я не хотів робити ще одну гру, де війна — це тільки вибухи. Мене цікавила інша сторона: як місто живе, коли війна стає фоном, а головний gameplay — це не стріляти, а вирішувати, кому сьогодні вистачить електрики, хто повірить пропаганді, де чиновник украде бюджет і скільки моральних компромісів витримає система.

Контекст для українця знайомий: ДТЕК у локалізації — Power Company, бомбосховище — Пункт Незламності, дрон на підльоті — Shahed (але тільки в українській локалізації). Це не «гра про війну» в прямому сенсі. Це симуляція цивільного побуту, коли війна — фоновий шум, а рішення треба приймати про те, який район лишити без світла на ніч і чи ставити батарею ППО.

Слоган проєкту: «Corruption is the cause. Blackout is the consequence.»

Геймплейний трикутник:

Тип виживанняДоменПитання гравцю
ФізичнеPower Grid«Чи буде світло цієї ночі?»
МентальнеCognitive Warfare«Що є правдою?»
МоральнеCorruption«Всі крадуть. Чому б і мені?»

Типовий момент у грі виглядає приблизно так. Увечері мережа перевантажена, частина міста вже сидить на генераторах, у Chirper зростає паніка, а розвідка попереджає про Shahed. Можна перекинути живлення на лікарню, але тоді житловий район піде в блекаут. Можна купити дешеве обладнання через мутний тендер, але потім це вилізе аваріями. Можна прикрити проблему красивим телемарафонним повідомленням, але довіра людей не безкінечна. Мені хотілося, щоб гравець не просто оптимізував цифри, а весь час відчував: будь-яке рішення має політичну, технічну й людську ціну.

На рівні систем мод складається з 15 доменів. Power Grid відповідає за електрику, блекаути, ремонт і перевантаження мережі. Air Defense — за загрози з повітря, ППО, Shahed-атаки й наслідки влучань. Cognitive Warfare — за інформаційний шум, телемарафон, довіру, паніку й те, як люди реагують на те, що їм розповідають. Corruption — за відкати, схеми, тендери й ситуації, коли погане рішення короткостроково виглядає дуже вигідним.

Навколо цього є сценарії, туторіали, UI, локалізація, балансні таблиці, діагностика, профайлери, власний мод для вимірювання продуктивності й купа дрібних речей, які гравець не бачить напряму, але без них усе розсипається. Наприклад, 469 саркастичних Chirper-повідомлень — це не просто жарти заради жартів. Це спосіб показати, як місто коментує твої рішення: іноді смішно, іноді зло, іноді так, що хочеться перезавантажити сейв.

Як виглядає робочий день з ШІ

Набір інструментів простий: Claude Code як основний інтерфейс, плюс кілька спеціалізованих інструментів навколо нього.

Перед кодом майже завжди йде план. Але це не детермінований ритуал з п’яти кроків і не документ у стилі «ось архітектура на місяць вперед». Масштаб планування залежить від задачі. Маленький фікс може пройти за один короткий раунд. Нова механіка, рефакторинг домену або баг на стику систем можуть вимагати кілька прогонів одного й того самого документа.

Головне — повністю занурити модель у контекст. Не просто сказати «зроби фічу», а змусити її прочитати існуючі системи, знайти схожі патерни, зрозуміти, хто пише стан, хто його читає, які події вже є, які аналізатори можуть спрацювати, де межі доменів і що не можна ламати. Дуже часто перший план після такого читання виявляється наївним. І це нормально: його задача не бути фінальним, а витягнути приховані обмеження.

Далі починається дискусія про патерн. Модель може запропонувати три варіанти: швидкий хак, локальний фікс або нормальну інтеграцію в існуючу архітектуру. Моє завдання — не писати C# замість неї, а вибрати правильну форму рішення: чи це має бути новий компонент, розширення існуючої системи, окрема подія, зміна порядку виконання, UI-зв’язка, міграція сейву або взагалі заборона на зміну, бо вона ламає інваріант.

Після цього план переписується. Іноді один раз, іноді три. На великих задачах я можу прогнати один документ до п’яти разів: кожного разу додати новий шар контексту, прибрати зайву імпровізацію, уточнити ризики, розбити роботу на фази й змусити модель пояснити, чому саме цей патерн відповідає існуючій кодовій базі.

Окрема важлива річ: майже ніколи не виходить написати план «для всього» з першого разу. Це дуже схоже на те, як люди програмують без ШІ. Спочатку накидається функціональність: кнопки, стани, події, реакції, перші робочі зв’язки. Потім починається архітектурна дисципліна: де тут відповідальність, де залежності, де межі, де принципи SOLID або їхній аналог у DOTS. І вже після цього йде точковий рефакторинг: у Python це були окремі класи й сервіси, у CivicSurvival — окремі ECS-системи, компоненти й стики між доменами.

Тобто планування з ШІ — це не спроба одразу вгадати ідеальну архітектуру. Це керований рух від «воно має якось працювати» до «воно працює в правильному місці, правильним патерном і не ламає сусідів».

Фінальний план зазвичай уже не схожий на загальну ідею. Це може бути кілька фаз: спочатку компоненти й дані, потім системи, потім UI, потім локалізація, потім аналізатор або тестовий лог, потім перевірка сейву. У плані мають бути конкретні файли, заборонені дії, очікувана поведінка, ризики й критерій, за яким я зрозумію, що ШІ справді зробив те, що треба.

Тільки після цього ШІ редагує файли. І після реалізації обов’язково йде звірка: що планували, що реально зроблено, що не зроблено, які тести або білди пройшли, які ризики залишились. Я не сприймаю відповідь «done» як результат. Результат — це коли є диф, лог збірки й короткий список відхилень від плану, який я можу оцінити на рівні поведінки системи, а не на рівні «я особисто розумію кожен синтаксичний нюанс».

Це звучить повільно, але на практиці швидше, ніж розгрібати хаос після одного широкого промпта. Більшість помилок ШІ народжується не в коді, а в моменті, коли йому дозволили занадто багато додумати.

1. Claude Code — мій IDE-замінник

Я не пишу код руками. Я описую намір, обмеження й очікувану поведінку. ШІ читає файли через Read, шукає патерни через Grep, редагує через Edit. Я не перечитую кожен диф перед застосуванням, але можу відмовити, можу попросити ШІ переробити. І це не означає, що я сиджу й програмую C# у класичному сенсі.

Ключове: я намагаюся не делегувати відповідальність за сенс. Я дуже рідко пишу «прочитай ось це і виправ». Часто це конкретика, яку ми перед цим разом з ШІ витягнули з логів, grep-у, декомпіляції або попереднього невдалого фіксу.

2. Глобальні правила в CLAUDE.md

CLAUDE.md — це локальний файл з правилами, який ШІ читає на старті сесії. Але важливо: це не магічний контракт, який модель завжди виконує. Це радше набір парканів, які зменшують кількість дурних рішень.

Туди можна додавати скільки завгодно правил. Наприклад: не створюй новий sync point, якщо він може погіршити FPS. Не роби domain → domain imports. Не видаляй «мертвий код» без окремого підтвердження. Не користуйся git restore / git checkout, бо можна випадково стерти роботу іншого агента. Не виправляй 50 warning-ів одним махом. Не переписуй архітектуру, якщо задача була на один метод.

Це справді допомагає, але не скасовує контроль. Модель часто не порушує правило прямо. Вона підходить до нього збоку. Наприклад, їй заборонено робити новий sync point — і вона не пише EntityManager.ToEntityArray() у лоб, але пропонує «зручний helper», який усередині робить те саме. Формально ніби не порушила. По факту — майбутній FPS-баг.

Тому частина моєї роботи — читати не тільки код, а й напрям думки моделі. Якщо я бачу, що вона почала виправдовувати зайвий рефакторинг, створює новий abstraction layer без потреби, хоче «тимчасово» обійти аналізатор або використовує Git-команду, яку краще не чіпати, я намагаюся зупинити це ще до імплементації. Це дуже схоже на парне програмування з кимось, хто пише швидко й часто якісно, але не дочитав сусідню систему.

І тут є різниця між інструментами. Claude Code зазвичай показує мені намір і хід думок достатньо читабельно: я бачу, куди він рухається, і можу вчасно зупинити модель. З Codex мені в цьому гірше: він часто додає зміни в код великими вставками, а я фізично не можу 2 000-5 000 рядків на день, які здатен видати ШІ. У цьому немає сенсу: я все ще людина, яка знає тільки if. Моя роль не в тому, щоб вручну перечитати кожен рядок, а в тому, щоб тримати рамку задачі, помічати момент, коли модель починає їхати не туди, і мати автоматичні перевірки там, де людська увага закінчується.

3. Власний MCP-сервер: CivicRAG

Звичайний grep не показує зв’язки. Якщо я питаю «хто читає BlackoutState?» — мені треба прочитати десятки файлів, бо ComponentLookup<BlackoutState> ховається в полях класів.

Тому я попросив створити MCP-сервер на Python + SQLite. Метадані 542 систем, 369 UI-компонентів, 1427 крос-доменних зв’язків індексуються один раз і доступні через інструменти:

  • rag_query("blackout permanence") — семантичний пошук.
  • rag_styk("AirDefense", "Threats") — аналіз стику двох доменів: потік даних, фрагмент коду, точки синхронізації, ризики.
  • rag_component("BlackoutState") — карта компонента: хто пише, хто читає, який порядок виконання.
  • rag_system("AirDefenseOrchestrator") — повний профіль системи.

4. 300+ власних Roslyn-аналізаторів

Ось тут найважливіше.

DOTS-код легко зламати. Це не знання, з яким я прийшов у проєкт, а те, що довелося засвоїти через ШІ, логи, баги й аналізатори. Якщо ШІ зробив EntityManager.SetComponentData всередині OnUpdate — у тебе тихо з’являється потенційна точка синхронізації на 20 мс. Якщо ComponentLookup<T> не оновив через .Update(this) — помилка під час виконання. Якщо EntityQuery створив у OnUpdate замість OnCreate — витік. Усе це не ловиться компілятором C#.

Тому я попросив згенерувати 300+ власних Roslyn-аналізаторів, які ШІ знаходив на прикладах власного аналізу. Це не лінтер на стиль — це примусове виконання архітектурних правил на етапі компіляції:

  • CIVIC051 — заборонено операції запису через EntityManager в OnUpdate. Тільки ComponentLookup або ECB.
  • CIVIC310 — magic numbers (тільки через GameRate.SECONDS_PER_HOUR, не 3600f).
  • CIVIC324 — singleton naming convention.
  • CIVIC361 — заборонено domain → domain імпорти.
  • CIVIC382-385 — нові правила з аудитів.

Кожен білд з --no-incremental запускає всі аналізатори. Якщо ШІ згенерував код, що порушує архітектуру — він просто не збереться. Я бачу помилку, кажу ШІ «це порушує CIVIC051», і ШІ переписує. Мені не треба пам’ятати весь синтаксис API аналізаторів. Мені треба розуміти, яке правило проєкту порушено.

Це найпотужніший примусовий промпт у проєкті. ШІ може помилятися в плані чи логіці, але частину небезпечних змін компілятор і аналізатори просто не пропускають. Рев’ю коду може забути правило, аналізатор — ні.

Де ШІ не витягнув: два тижні з рендерингом дронів

Щоб не звучало так, ніби ШІ — це чарівна кнопка, треба окремо розповісти про найболючіший шматок проєкту. Рендеринг дронів.

Це була майже двотижнева епопея на стику одразу кількох речей: Unity ECS, DOTS, штатний рендер-пайплайн CS2, BatchRendererGroup, HDRP, вектори руху, часове згладжування, Coherent UI для діагностичних панелей, власні .cok-моделі, недокументований код гри й перевірка на око. І саме тут ШІ був найслабший.

Тому що проблема була не в одному інтерфейсі й не в одному файлі. Код міг бути формально правильний, журнали могли показувати чисті дані, ECS-компоненти могли збігатися зі штатними об’єктами гри, а на екрані дрон все одно пульсував при 2x/3x швидкості. Це той клас помилок, де ШІ дуже впевнено пояснює тобі п’ять правдоподібних причин, але жодна з них не є першопричиною.

Якщо відкрити історію змін у Git цього шматка, там видно не «одну помилку й одне виправлення», а нормальну таку воронку болю. Спочатку у сховищі з’являється AttackDrone.cok — перша реальна модель дрона. Потім майже одразу починаються документи про рендер-пайплайн, бо стає зрозуміло: просто намалювати сітку мало. Дрон має жити у тому самому штатному конвеєрі гри, де живуть машини, літаки й вертольоти CS2.

Далі історія розгалужується. Один напрям — зворотне тремтіння: дрон ніби рухається вперед, але камера або рендер іноді показує його з відкатом назад. Там у записах Git видно десятки спроб: TransformFrame, UpdateFrame, ObjectInterpolateSystem, діагностичні системи, запис журналів, порівняння зі штатними об’єктами. У якийсь момент це закрили прямим записом рендер-компонентів у TMS.

Але після цього лишився інший симптом — тремтіння на прискоренні й пульсація розмиття руху. І він був гірший, бо код уже виглядав правильним. У сховищі це видно по документах розслідування: спроби 25-32 (тобто було написано 32 різні варіанти рендерингу), порівняльна перевірка зі штатною заготовкою вертольота, порівняння зі штатною машиною, заміри OTW delta на рівні групи BRG, перевірки PreviousM, ознак зміни й перевиділень пам’яті. Тобто ми не просто «підкрутили параметр». Ми намагалися пройти весь шлях від ECS-компонента до GPU-матриці й зрозуміти, де саме штатна гра поводиться інакше.

Паралельно в основне сховище переїхав Model/. До цього моделі жили окремо в D:\Model, а потім стало очевидно, що без FBX, текстур, сценаріїв експорту й нотаток щодо ресурсів CS2 неможливо нормально розслідувати помилки рендерингу. AttackDrone, Bofors, LADS — це вже не «картинки десь збоку», а частина технічної картини проєкту.

Фінальне виправлення виявилося не героїчним, а прагматичним. Ми перестали битися з прапорцями векторів руху в окремих рендер-групах, бо в CS2 1.5.5 шлях через GetMotionVectorsEnabled() фактично не давав потрібного контролю. Замість цього ШІ згенерував латку, яка на швидкості вище 1x вимикає розмиття руху через примусове перевизначення HDRP Volume (MotionBlur.intensity = 0), а при поверненні на 1x відновлює нормальний стан. Не академічно красиво, зате дрон перестав пульсувати.

Окрема важлива річ, яку видно по сховищу: ми зробили собі локальну довідкову папку з декомпільованим Game.dll (_decompiled\Game\). Це не було «давайте вкрадемо код». Це було «без карти штатного конвеєра гри ми взагалі не розуміємо, куди дивитися». З цього виросла ціла папка Rendering: VANILLA_RENDER_PIPELINE.md, VANILLA_RENDER_MESH.md, VANILLA_RENDER_ENTITIES.md, VANILLA_VFX_PIPELINE.md. На створення цих документів я витратив 100 мільйонів токенів. Там уже описано, що роблять ObjectInterpolateSystem, UpdateGroupSystem, PreCullingSystem, BatchInstanceSystem, BatchDataSystem, як живуть Created/Updated/Applied, як працює кільцевий буфер TransformFrame, CullingInfo, вектори руху й ланцюжок визначення матеріалу.

Без цієї довідкової папки ми б просто йшли навпомацки. Офіційної документації на це немає. У створенні модів для CS2 часто відповідь виглядає так: відкрий декомпільований код, знайди штатний шлях, повтори його настільки близько, наскільки дозволяє мод.

Найсмішніше: у якийсь момент дані з боку процесора були ідентичні між нашим дроном і штатними об’єктами. У документах розслідування є таблиці, де OTW delta у дрона й штатної машини мають однакові координати. Ознак зміни немає, перевиділення пам’яті нічого не пояснює, OIS читає ті самі слоти, InterpolatedTransform нормальний. Тобто все, що ми могли поміряти з C#, виглядало правильно. Але на екрані дрон пульсував.

Ось тут межа ШІ стала дуже видимою. ШІ добре генерував діагностичні системи, швидко робив Harmony-латки, допомагав декомпілювати й структурувати знання. Але він не бачив екран. Він не міг сказати: «оце не математичне тремтіння, це саме пульсація розмиття руху». Він плутав схожі симптоми: зворотний рух, запізнення камери, тремтіння render clock, артефакт часового згладжування, вектори руху. Людина дивиться на дрон у грі й за секунду каже: «це не те». ШІ без візуального контролю вірить тільки журналам, а журнали тут не показували першопричину.

Фінальний висновок був прагматичний: ми не виправляємо шлях GPU/HDRP самої гри з мода. Ми довели, що наші дані з боку процесора чисті, знайшли, що артефакт живе нижче рівня доступного C#-коду, і зробили робочий обхід: вимикати розмиття руху на прискоренні. Це не красива академічна першопричина. Зате це працює.

Цей епізод сильно протверезив. ШІ блискучий, коли задача текстова, структурна або статично аналізована. Але на стику недокументованого коду рушія, рендерингу, поведінки шейдерів і людського ока він стає не автопілотом, а дуже швидким помічником, якому все одно потрібен пілот. (До речі, оригінальна гра має ту саму проблему)

Як виглядає аудит з ШІ — парадокс вільного пошуку

DOTS-код важко покривати юніт-тестами. Створення тестового ECS World, заповнення архетипів, мокання EntityManager — усе це часто коштує більше, ніж дає. Тому ми пішли іншим шляхом: ШІ-агенти як армія статичних тестувальників.

Масштаб

Важливий масштаб. Це не «я запустив 10 агентів і написав висновок». В AUDIT_HISTORY.md зараз зафіксовано приблизно 9700+ заявлених знахідок і 5500+ виправлених по всіх методах аудиту. І це лише те, що потрапило в звіти. Ще приблизно такий самий шар згорів раніше: на рівні компіляції, Roslyn-аналізаторів, ESLint, збірок після рефакторингу й дрібних фіксів, які ніхто не оформлював як окремий аудит. Тому чесніше говорити не про «десять тисяч перевірок», а про десятки тисяч сигналів, з яких у документах залишилася нижня межа.

Сам Free-звіт за березень — це ~1500 звітів агентів-дебагерів, ~2600 унікальних знахідок. Якщо рахувати всі агентські перевірки різних типів за проєкт — це вже не сотні, а тисячі агентських прогонів, і навіть ця цифра не включає все, що ловилося компілятором та аналізаторами під час звичайної роботи.

Інакше кажучи, це вже не «ШІ іноді допомагає знайти баг». Це окремий виробничий контур: агент знаходить підозру, інший агент її перевіряє, фікс проходить білд, а повторюваний клас помилки потім перетворюється на аналізатор.

Еволюція

  • Спочатку працювали відомі категорії. Ручні аудити в стилі «перевір серіалізацію», «перевір час», «перевір Entity.Index», «перевір мертвий код» давали добрий сигнал, коли категорія була конкретна. Потім були теоретичні списки патернів: сотні гіпотез у стилі «а раптом десь є такий клас помилки». Це швидко вичерпалося. У документах видно стелю: після 760 теоретичних патернів останній фокусний прохід дав 0 підтверджених багів. Модель вигадувала правдоподібні ризики, але код їх уже не містив.
  • Потім ми перейшли до стиків систем. STM — семантичне моделювання загроз — дає агенту не просто список файлів, а контекст стику: хто пише, хто читає, який порядок виконання, де події, де стан, де збереження. Це відкрило новий клас помилок: гонки між системами, порядок кадрів N/N+1, події, які приходять до ініціалізації, switch без нового case. Це не ловиться лінтером. Але STM теж має межу: після кількох проходів він починає пережовувати одні й ті самі контракти.
  • А потім з’явився парадокс вільного пошуку. Найефективнішим виявився не найрозумніший чеклист, а майже вільний промпт: «ось вузький набір файлів, шукай будь-які баги виконання, які реально можуть зламати гру; доведи кожну знахідку кодом». Без списку категорій. Без «перевір тільки серіалізацію». Без «шукай тільки точки синхронізації». Це парадоксально, бо здається, що чим конкретніше промпт, тим краще. Але надто конкретний промпт створює ефект ліхтаря: агент бачить тільки те, що його попросили шукати.

Правильна формула виявилась така: вільний промпт + вузький обсяг + правильний контекст. Коли агенту давали цілий домен на 4-6K рядків, він ковзав поверхнею. Коли давали пару взаємодіючих систем і спільні компоненти на ~2K рядків, він починав моделювати виконання: «а що буде, якщо A спрацює до B? а якщо після? а якщо збереження й завантаження між ними?». У W13-cross такий підхід дав 2,8 знахідки на агента — найвищу віддачу в історії аудитів. Та сама вузька область із чеклистом давала приблизно 0,9 знахідки на агента.

CODE і RAG

CODE і RAG виявилися різними очима. У free-методі кожна сесія часто запускалася парою: один агент читає локальний код напряму, другий перед цим бере контекст із RAG-сервера плюс код. Вони не ділять роботу, а незалежно перевіряють той самий список файлів. CODE-агент краще бачить локальну арифметику, крайові випадки, мертвий код, серіалізаційні дрібниці. RAG-агент краще бачить порядок виконання, зв’язки між системами, післязавантажувальну гідратацію, хто кого перезаписує.

Цифри це підтвердили. У базовому FREE CODE давав більше знахідок за обсягом — приблизно 60%. Але RAG частіше приносив вищу критичність: у CRIT2 RAG знайшов усі 4 CRITICAL, які CODE не побачив. Висновок не «RAG краще за код» і не «код краще за RAG». Висновок: RAG — це не заміна читання коду, а навігація й підсвітка зв’язків. Найкраще працює пара: RAG підказує, куди дивитися, а код підтверджує або спростовує.

Дебати моделей

Для складних фіксів я змушував моделі сперечатися. Коли було незрозуміло, як виправляти складний баг, я іноді запускав не «одного мудрого агента», а дебати: умовно GPT пропонує фікс, Claude критикує, інший агент шукає регресії, ще один захищає мінімальний варіант. Потім я дивлюся не на те, хто звучить впевненіше, а на те, де є конкретний доказ: файл, рядок, порядок виконання, лог, ризик для сусідньої системи. Це корисно саме для виправлень, бо агент, який знайшов баг, дуже часто пропонує занадто широкий або небезпечний фікс.

Межа методу

Межа методу — шум і виснаження. Повторні хвилі неминуче деградують: частка хибних спрацювань може рости з 15% до 90%, особливо коли код уже вичищений. Агенти починають вигадувати проблеми потокобезпеки там, де весь код іде в головному потоці, або перебільшувати застарівання стану на один кадр у системах з обмеженою частотою. Тому закон простий: один метод вичерпується за 3-4 проходи. Якщо нових HIGH немає — треба міняти метод або область, а не запускати ще 100 агентів з тим самим запитом.

І все одно навіть 826 агентів не замінюють ігрове тестування. Вони можуть знайти Random(0), забутий clamp, перезапис стану, неправильний порядок систем, витік NativeContainer, відсутнє очищення. Але вони не скажуть, чи цікаво грати, чи зрозумілий туторіал, чи сценарій має драматургію, чи Telemarathon змушує сумніватися, а не просто натискати кнопку. Це вже не аудит коду. Це живі люди.

П’ять речей, які я зрозумів за рік вайбкодингу і три місяці CivicSurvival

1. ШІ без запобіжників — це генератор технічного боргу

Це я зрозумів ще на Python-сервері рік тому — там кодбаза перетворилася на мішанину з трьох різних патернів за два тижні, бо ШІ пам’ятає тільки контекст однієї сесії. На CivicSurvival приходив уже з висновком: треба зовнішня пам’ять. Аксіоми в CLAUDE.md, аналізатори, RAG-сервер — це формалізована пам’ять про правила, яка змушує ШІ бути послідовним між сесіями.

2. Конкретика > абстракція

«Полагодь баг» — погано. «Ось симптом, ось лог, ось файл, який підозрюємо, ось правило, яке не можна порушити, ось очікувана поведінка» — добре. Іноді після кількох раундів діагностики це доходить до рівня «у файлі X на рядку Y заміни API W на API V», але це не тому, що я раптом став C#-розробником. Це тому, що ШІ, логи й grep допомогли звузити простір пошуку до одного місця. Чим точніше я формулюю, тим менше ШІ імпровізує. Імпровізація ШІ — джерело багів.

3. Реальна поведінка + логи

Аксіома 1 проєкту народилася з 30-годинного розслідування лютневого бага. Я витратив півдня, переконуючи ШІ, що «фікс не працює, я бачу баг».

З того часу будь-яка теорія перевіряється двома речами: реальною поведінкою системи в грі і логом, який пояснює, чому вона так поводиться. Лог сам по собі не істина. Якщо в грі район вимкнувся, люди зникли або UI показує не те — це теж факт. Але для ШІ цього факту замало: він не бачить екран, не відчуває темп гри й не може сам зрозуміти, що саме відбулося між двома кадрами.

Тому логування у нас часто максимально детальне, іноді майже на рівні методів: хто викликався, з якими даними, у якому кадрі, що було до, що стало після — це спосіб дати ШІ очі. Коли модель бачить тільки код, вона вгадує поведінку. Коли є реальна поведінка плюс детальний лог, вона може нарешті відновити, як код працює насправді.

4. Власні аналізатори > одноразове рев’ю коду

ШІ-рев’ю коду я не викидаю. Навпаки, часто беру його як перший шар аналізу: модель швидко бачить підозрілі місця, незвичні патерни, повтори й потенційні регресії. Але один прохід ШІ — це не істина. Я змушую перевіряти висновки кілька разів, порівнювати їх з реальним кодом, логами, поведінкою гри й історією Git: коли з’явився файл, що змінювали поруч, який фікс уже пробували, які коміти могли породити симптом. Git тут працює не як архів, а як карта причинності.

Аналізатор — це наступний рівень після такого рев’ю. Якщо баг або архітектурне правило повторюється, його треба не просто «запам’ятати», а формалізувати. Аналізатор працює без людської втоми й неуважності та ловить той самий клас помилок щоразу при збірці.

Кожен раз, коли я під час тестування або ШІ під час аудиту ловить баг, який мав би ловитися автоматично, я прошу ШІ сформулювати нове правило для аналізатора. Код аналізатора генерує ШІ, потім ми ганяємо його на реальній кодбазі, прибираємо хибні спрацювання і доводимо до стану, де він ловить клас помилок назавжди. За 4,5 місяця — 300+.

5. Я не знаю синтаксис, але намагаюся зрозуміти поведінку

Я навряд чи зможу з нуля написати IJobChunk без ШІ. Я не знаю синтаксис, не знаю API і не пройду співбесіду на джуніор Unity DOTS-розробника. Але я намагаюся зрозуміти, як має поводитися мій проєкт: які домени не мають напряму залежати один від одного, які стани повинні зберігатися в сейві, де не можна створювати точку синхронізації, який ігровий ефект має дати система.

Тобто я не читаю 5 000 рядків ШІ-коду як C#-інженер. Я читаю план, логи, помилки аналізаторів і поведінку в грі. Якщо система має вимкнути район, а вимикає пів міста — я це помічаю. Якщо ШІ хоче зробити новий обхід через інший домен — я намагаюся зупинити це ще на рівні плану. Якщо білд падає на CIVIC051 — я не пишу API аналізатора з пам’яті, але розумію: модель знову намагається зробити запис там, де не можна.

Це окремо цікаве питання — чи я взагалі програміст у класичному сенсі? Ні, скоріше ні. Я чистий вайбкодер, який тримає продукт, правила, перевірки й фідбек-цикл.

Чому статистичний аналіз вичерпав себе

Тут найцікавіше. Чому 4,5 місяця працювало — і чому зараз не працює.

На етапах 1-3 (грудень-лютий) — ШІ-агенти знаходили реальні баги десятками. Кожна хвиля аудиту давала 30-50 знахідок, з яких 10-20 — справжні.

На етапі 4 (березень) — сигнал почав слабшати. Хвилі повторювали попередні знахідки. Знаходили дрібниці — неймінг, мертвий код, відсутній [Persist] на полі, яке ніхто не використовує. Реальних ігрових багів — нуль. Це збігається з рекордними 895 комітами березня — багато роботи, але вже полірування, а не пошук фундаментальних проблем.

Квітень — пауза на інший проєкт. Повернувся в кінці місяця.

Зараз (травень): статистичний аналіз ШІ технічно вичерпав себе. Хочеться вірити, що всі точки синхронізації усунуті та прогалини серіалізації закриті. Аналізатори ловлять регресії автоматично.

Те, що залишилося — це цілісність ігрового досвіду. Чи цікаво грати. Чи зрозумілий туторіал. Чи балансні цифри, чи модальне вікно «Перший Shahed» з’являється у правильний момент. Які баги ще залишилися (думаю, що 50% ШІ просто не зміг знайти). Чи Telemarathon-механіка змушує вагатись, а не просто натискати кнопку.

Це вже погано ловиться агентами. Це мають знаходити живі люди, які грають годинами.

Кого я зараз шукаю

Бета-тестери на закриту фазу.

Люди, готові грати 5-10 годин на одному сейві — частина механік розкривається на довгій дистанції (мобілізація, Telemarathon, корупційні схеми). Готові давати фідбек у структурованій формі. Платформа для бети — Discord.

Чому тут. Бо DOU читають люди, які вже трохи бачили розробку з ШІ. Якщо ця стаття вам зайшла — ймовірно, ви тип людини, який зрозуміє, що мод за 3 місяці чистого часу силами однієї людини + ШІ — це не вайбкодинг у поганому сенсі. Так, рік тому я починав саме з нього. Але вайбкодинг без запобіжників ламається на 5 000 рядків коду. На 158 000 рядків живе тільки процес з 300 аналізаторами, RAG-сервером, аксіомами і аудитами. Ваш фідбек я зможу обробити правильно.

P. S. — технічна примітка

Епізод, який добре показує межі процесу:

  • Один Shahed знищив місто за 80 секунд. У лютневому багу місто буквально вмирало за 30-80 секунд: житлові будинки масово ламалися, люди зникали з нормального циклу життя, а ШІ латав систему за системою, бо симптом виглядав як каскадний збій гри. У хибній гілці розслідування ШІ замінив посилання на сутності в 11 компонентах і перевірив 15 систем на окремі модові сутності; якщо рахувати всі зачеплені системні, службові й UI-файли в комітах міграції — 32 файли. А причина була в одному рядку з CopyFromComponentDataArray: назва методу звучить так, ніби він «копіює компоненти в масив», і ШІ кілька разів читав її саме так. Насправді метод записував нульові Transform назад у житлові будинки. Тридцять годин розслідування, два дні пошуку в Game.dll, а помилка була в згенерованому ШІ коді.

CivicSurvival — скріншот моду

CivicSurvival — скріншот моду

CivicSurvival — скріншот моду

CivicSurvival — скріншот моду

CivicSurvival — скріншот моду

CivicSurvival — скріншот моду

CivicSurvival — скріншот моду

Підписуйтеся на Telegram-канал @gamedev_dou, щоб не пропустити найважливіші статті і новини про геймдев

👍ПодобаєтьсяСподобалось6
До обраногоВ обраному2
LinkedIn
Ctrl + Enter
Ctrl + Enter

Стаття топ. Лайк,підписка.
Сам з ШІ граюсь в уе5 розробника трішк, але Ваш рівень занурення набагато глибший.
Хотів би помацати ваш мод,але не впевненний що зможу правлиьно тест документацію відправити

Нічого не треба відправляти, мод сам все відправить на пайтон сервер.

Підписатись на коментарі