Article hero

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

Путь от простых промптов к собственной инженерной среде: 9B, 35B, агенты, scaffolds и контекст как валюта.

llm local-ai agents workflow qwen experiment

Я довольно долго проверял одну простую мысль: можно ли сделать локальную LLM полезной для разработки личных проектов. Не в формате разового чата, где модель пишет функцию или объясняет ошибку, а в более интересном режиме: у меня есть идея приложения, модель помогает разложить её на шаги, пишет код, проверяет себя, чинит ошибки и постепенно доводит проект до состояния, где им уже можно пользоваться.

Сначала я пытался понять сами модели. Потом генераторы. Потом агентные обвязки. Потом свои workflow с пакетами, слайсами, ревью, субагентами и полуавтоматической разработкой. В какой-то момент стало ясно, что я проверяю уже не одну модель, а всю маленькую инженерную среду вокруг неё.

Короткая карта экспериментов

ЭтапЧто я пробовалЧто стало понятно
Синтетические задачиТексты, сайты, ограничения, промпты вместе с ChatGPTМодель легко заполняет пустоты и звучит уверенно там, где у неё нет опоры
Генераторы и scaffoldsБыстрый старт Vue/Express, конфиги, шаблоны, первые заготовкиБез каркаса модель быстро уходит в случайные решения
9B + агентыQwen 9B, OpenCode, Pi.dev, Aider, первые роли planner/implementer/reviewerМаленькие задачи работают, длинная автономность быстро трещит
Субагенты и поискWebsearch, codesearch, explore/review-подзадачиСубагенты помогают, но легко раздувают контекст
Full-auto workflowbootstrap, choose-task, implement, stabilize, finish, repairПолная автоматика хрупкая, зато stabilize оказался очень полезным
Scaffolds как лаборатория10-ideal, Vue 3, Express 5, TS, ESLint, Prettier, API layerХорошая стартовая форма важнее длинных инструкций
35B локальноQwen3.6 35B A3B, длинный контекст, llama.cpp/VulkanВпервые появилось ощущение рабочего локального исполнителя
Текущий подходTemplates, small slices, checks, review packets, внешний reviewer по делуМодель лучше всего работает внутри подготовленного коридора

Сначала были синтетические эксперименты

Начиналось всё довольно мирно. Я гонял синтетические задачи, часто вместе с ChatGPT. Мы придумывали промпты, проверяли, как модель держит ограничения, как пишет тексты, как реагирует на задачу без готового материала.

Например, был заход про личный сайт разработчика-энтузиаста. Условия были специально неприятные: нельзя выдумывать репозитории, статьи, проекты, клиенты и достижения, но красивый сайт всё равно очень хочется. Хотелось понять, сможет ли модель сделать что-то убедительное без фейковой биографии.

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

Уже на этом уровне стало видно, что хороший результат редко рождается из одного волшебного промпта. Нужны маленькие правила, которые возвращают модель к реальности.

Первые правила были почти литературные:

• не выдумывать факты
• не прятать пустоту за красивыми словами
• не делать вид, что у автора уже есть портфолио
• писать проще
• оставлять только то, за что можно отвечать

Позже оказалось, что ровно такой же принцип работает и в коде. Только вместо красивых фраз модель начинает выдумывать архитектуру, правила, папки, конфиги и смысл там, где лучше было бы сделать маленький понятный шаг.

Потом пошли генераторы и первые scaffolds

После текстов захотелось большего: быстро поднимать основу приложения. Не просто попросить модель написать компонент, а дать ей задачу вроде: создай минимальный Vue + Express проект, настрой TypeScript, ESLint, Prettier, Tailwind, добавь пару команд, сделай так, чтобы всё запускалось.

И тут началась настоящая разработческая часть эксперимента.

Модель может впечатляюще быстро создать package.json, конфиги, папки, минимальный фронт и бэк. На маленьком демо это выглядит почти магически. Но стоит попросить свежую связку инструментов, и уверенность модели начинает мешать. Она помнит старые конфиги, путает новые версии, иногда чинит проблему таким способом, который через два шага создаёт новую.

Был совсем простой пример. Я просил сделать последний Vue с последним ESLint, только package.json, eslint.config.mjs, tsconfig.json и .gitignore. Без приложения. Всё на npm. В конце npm run lint должен запускаться. Казалось бы, задача маленькая. Но агент сразу полез в websearch за версиями и конфигами.

И тут нет простого ответа. Поиск правда нужен, потому что фронтенд-инструменты меняются быстро. Но бесконтрольный поиск быстро превращается в пожирателя контекста. Для локальной модели контекст стоит дорого. Его приходится беречь почти как оперативку.

ИдеяОжиданиеКак вышло в работе
Дать модели свежий frontend setupОна быстро соберёт актуальные конфигиМодель либо тащит старые знания, либо начинает много искать
Попросить только несколько файловКонтекст останется маленькимДаже маленькая задача может увести в websearch
Использовать генератор как стартПолучится быстрый чистый проектБез правил рядом появляются странные решения
Сразу сделать идеальный scaffoldОдин раз настроить, потом жить спокойноScaffold сам стал отдельной лабораторией

В этот момент я впервые начал думать не только о модели, а о форме задачи. Чем меньше модель придумывает вокруг, тем лучше. Чем понятнее границы, тем меньше мусора в результате.

9B: быстрые надежды и узкий коридор

Дальше я много экспериментировал с Qwen 9B. Она была заманчивой: легче, быстрее, проще помещается в память, не создаёт ощущения, что каждый запуск превращает компьютер в отдельную инфраструктуру. Для первых агентных экспериментов такой размер выглядел разумным.

9B может быть полезной. Если дать ей маленький, понятный кусок, она способна исправить файл, дописать простой компонент, пройтись по очевидной ошибке, собрать небольшой пакет изменений. Но как только задача требует удерживать архитектуру, свежие конфиги, длинную цепочку действий или несколько слоёв проекта, начинаются потери.

Она может сделать рабочий код, а рядом положить странную структуру. Может починить одно и сломать другое. Может уверенно править конфиг, который сама до конца не понимает.

Модель / режимКак ощущалосьГде годилосьГде быстро начинались проблемы
Qwen 9BЛёгкая, удобная для быстрых попытокМаленькие правки, простые компоненты, экспериментыАрхитектура, длинные задачи, свежие конфиги
Qwen 9B как implementerНормально, если дать короткий packetЛокальная реализация маленького sliceНачинает терять края, если пакет слишком широкий
Qwen 9B + remote planner/reviewerУже похоже на workflowПлан снаружи, маленькая реализация локальноПередача контекста и repair packets требуют дисциплины
Qwen 9B + full-auto идеяЗаманчиво на бумагеУчебные и полуавтоматические прогоныПолная автономность быстро становится хрупкой

Поэтому следующим шагом стали не новые промпты, а обвязки. Хотелось, чтобы модель работала не как один чат, а как участник процесса.

OpenCode, Pi.dev, Aider и идея ролей

Я пробовал разные инструменты и схемы: OpenCode, Pi.dev, Aider, Codex, локальные и удалённые модели, разные роли. Постепенно появилась схема с разделением обязанностей:

Workflow pipeline: planner → implementer → reviewer → repairer

Сначала это выглядело громоздко. Потом стало понятно, что в такой громоздкости есть смысл. Локальной модели трудно дать большую задачу целиком. Ей лучше передавать маленький кусок с понятными границами и конкретной проверкой.

Параллельно возникла идея локального оркестратора. Основной OpenCode-чат на локальной Qwen управляет субагентами: remote slice planner, local implementer, remote reviewer, local repairer. В идеале основной агент держит состояние, а подзадачи делают грязную работу. Реальность оказалась менее гладкой, но направление было полезным.

Мои workflow быстро начали обрастать бюрократией

После готовых инструментов я начал делать свои заготовки. Появились команды вроде bootstrap-project, choose-task, implement-slice, finish-packet, revert-packet. Потом start-packet, work-packet, stabilize-packet, inspect, abort-packet. Идея была понятная: каждая команда запускается в новой сессии, получает минимум нужного контекста, делает один этап и пишет артефакты.

Выглядело почти как маленькая фабрика приложений. На практике фабрика быстро начала требовать ухода.

Документы повторяли друг друга. README оказывался бесполезным. AGENTS.md разрастался и становился хуже. Модель путала workflow-файлы с продуктом. Внутренние папки вроде .workflow и .pi приходилось отдельно защищать от восприятия как части проекта. Команды с аргументами оказались не так уж полезны, потому что bootstrap и inspect всё равно сами задавали вопросы. Некоторые ограничения, которые сначала казались защитой, только мешали полуавтоматическому эксперименту.

Что я добавлял в workflowПочему казалось хорошей идеейЧто выяснилось
Много docs на стартеМодель будет лучше понимать проектДокументы начинают повторяться и стареть
Длинный AGENTS.mdБольше правил, меньше ошибокДлинные правила хуже читаются и хуже соблюдаются
Команды с аргументамиМожно запускать всё точнееВ реальности команды часто сами спрашивают нужное
Bootstrap всего проектаУ модели будет полная картинаПолная картина быстро превращается в лишний контекст
Subagents для проверокОсновной поток останется чистымСубагенты тоже могут сжечь много контекста
Stabilize passФинальная уборкаОдин из самых полезных этапов всего процесса

Самым полезным этапом оказался stabilize. Когда модель уже что-то сделала, ей проще посмотреть на конкретный результат, найти грубые ошибки и привести проект в порядок. Полная автономная разработка из одной идеи оставалась хрупкой, а цикл сделал кусок, проверил, стабилизировал, закоммитил выглядел намного ближе к реальной пользе.

Субагенты: хорошая идея, если они не съедают весь дом

Отдельно я много думал про субагентов. Хотелось разгрузить главный контекст. Пусть отдельный агент сходит в websearch, codesearch, explore, review, соберёт информацию и вернёт короткий вывод. Основной поток тогда не будет забит поиском, логами и промежуточным мусором.

В теории звучит отлично.

На практике я видел, как субагент набирал около 59 тысяч токенов контекста, делал несколько поисков, работал около двенадцати минут, а потом возникал неприятный вопрос: насколько всё это правда разгрузило основной процесс? Даже если технически подзадача завершается отдельно, её итоговый отчёт легко становится слишком длинным. А если отчёт длинный, основной контекст снова пухнет.

С websearch и codesearch похожая история. Я добавлял их в Pi agent, обсуждал, как этим может пользоваться OpenSpec, думал, стоит ли разрешать свободный поиск. В итоге свободный режим оказался опасным. Модель любит искать, потому что поиск похож на работу. Лучше работает лимит: сходить в интернет пару раз, когда правда нужны свежие версии, документация или реальные примеры. Для остального хватает локального контекста проекта, короткого пакета задачи и проверок.

Первые живые приложения и неприятные мелочи

Параллельно с workflow я пробовал реальные приложения. Иногда результат уже начинал радовать. Например, были автосгенерированные прототипы с dark liquid-glass UI, вкладками Active / Pinned / Trash, модальным редактором, confirm dialog, SQLite-сессиями, backup/restore-скриптами и LAN-friendly dev.

В какой-то момент локальная 35B смогла сгенерировать широкий рабочий прототип, потом сама же починить длинную TypeScript-проблему и вызвать explore-субагента. Это был важный сдвиг. До этого агентные схемы часто выглядели как борьба с инструментами. Тут впервые появилось ощущение, что локальная модель может быть исполнителем для реального личного проекта.

Но мелкие аварии никуда не делись.

В одном редакторе модель пыталась добавить атрибуты, чтобы отключить автокоррекцию на мобильной клавиатуре. Идея была нормальная: конкретный редактор сходил с ума от автозамены и начинал печатать справа куски слов. Но локальная модель перепутала скобки, и приложение упало на синтаксической ошибке в собранном JS. Фикс оказался простым, однако сам эпизод хорошо показывает уровень риска: модель может понимать замысел, делать почти правильную правку и всё равно ломать приложение одной тупой скобкой.

Другой пример был с Sovka. После редизайна под dark liquid glass интерфейс стал заметно лучше, но модель создала второй элемент с id=“app” внутри AppShell. Корневой app уже был в index.html, и второй такой id ломал приложение. Потом пришлось переименовывать внутренний shell в app-shell и добавлять guardrail: не создавать второй #app, внутренний контейнер должен называться #app-shell или классом.

Такие вещи трудно поймать красивым системным промптом. Они лучше превращаются в короткие правила, выросшие из настоящих аварий.

10-ideal: scaffold как лаборатория

Важным этапом стал 10-ideal. Я не воспринимал его как финальный notes app. Это была лаборатория для Qwen, Pi и OpenCode: pnpm workspace, Vue 3 + Vite + TypeScript на фронте, Express 5 + TypeScript на бэке, строгий TypeScript, ESLint, Prettier, Tailwind v4, Vue Router, API client layer, позже идеи Pinia, Zod, OpenAPI, SQLite, auth, тестов и CI.

Там постепенно появился важный вывод: локальной модели нужна хорошая стартовая среда. Не огромная архитектурная энциклопедия, а аккуратный scaffold с понятными папками, командами проверки и несколькими короткими правилами.

Я стал больше склоняться к type-first структуре, а не к feature-first. Для фронта что-то вроде:

src/api
src/stores
src/router
src/layouts
src/views
src/components/ui
src/components/<domain>
src/styles

Для бэка:

src/routes
src/services
src/repositories
src/validation
src/middleware
src/config
src/db
src/types

Модель лучше работает, когда ей не нужно каждый раз заново придумывать форму проекта. В пустой папке она фантазирует. В хорошем scaffold она чаще продолжает существующую мысль.

Scaffold as rails: chaotic empty folder → model + rails → organized project

На базе такого подхода стали появляться живые вещи. Один из примеров — лёгкий аналог pi-web для LAN/self-use: управление project folders, создание новых папок, список Pi-сессий через SDK, reader view, SSE events, session creation, guard от второго prompt в running session, статус running/idle через finally. Там ещё оставалось много работы, но ощущение было другим. Модель уже не лепила случайный Vue-компонент. Она работала внутри формы, которую можно продолжать.

Проверки, ревью и неожиданный урок про внешние модели

У меня был интересный эпизод с review-collect и внешним ревью. Локальная модель прошла задачу по notes scaffold, а внешний reviewer ошибочно отметил blocker на POST /api/notes, потому что не увидел express.json() в wiring context. После этого стало понятно, что diff-audit полезен, но пакет для ревью должен включать не только diff, а ещё важные куски wiring и untracked files.

Интересно, что локальная Qwen в том случае дала более полезное ревью, чем внешняя модель. Это не значит, что внешние модели бесполезны. Скорее они очень зависят от пакета, который им дали. Если пакет неполный, reviewer начинает уверенно ругаться на то, чего в проекте на самом деле нет.

После этого я стал больше ценить не саму модель-ревьюера, а качество пакета. Хороший packet иногда важнее, чем разница между моделями.

Переход к 35B

В какой-то момент стало ясно, что 9B для главной роли тесновата. Я пробовал разные варианты: Qwen Coder 30B, Qwen 27B, Qwen2.5 Coder 14B, разные кванты. Некоторые запускались, но были слишком медленными для нормального цикла. Qwen3 Coder 30B A3B Q3_K_L технически работала, но один audit-agent занимал около 700 секунд. Qwen3.5 27B на полном прогоне доходила до десятков минут. Модель могла быть умнее 9B, но реальный workflow убивает не только глупость, а ещё задержка.

Настоящий сдвиг произошёл с Qwen3.6 35B A3B в GGUF, особенно с UD-Q4_K_M. На моём сетапе с Ryzen 7 5700X, 32 GB RAM и видеокартой с 16 GB VRAM она стала первой большой локальной моделью, которую можно всерьёз встроить в разработку.

В хорошей конфигурации я видел примерно 22–25 токенов в секунду на генерации и около 280–318 токенов в секунду на обработке промпта. Иногда длинный prompt eval был ниже, но всё равно терпимо. При этом память сразу стала частью workflow: видеопамять может быть около 14 из 16 GB, оперативка около 22 из 32 GB, и каждый лишний браузер, VS Code, Docker или вторая сессия уже чувствуется.

Model practicality matrix: speed vs capability

С 35B изменился потолок. Модель лучше держит задачу, лучше понимает архитектурные замечания, спокойнее работает с большим проектом, увереннее чинит свои ошибки. Но старые проблемы не исчезли. Длинный контекст дорогой. Полный репроцессинг ощущается болезненно. Если агент начинает много читать, много искать и много писать в чат, скорость падает, а сессия становится тяжёлой.

Я думал, что буду выбирать модели. В итоге начал обсуждать batch, ubatch, KV cache, prompt cache, контекст на 65k или 81k, сколько RAM можно отжать у браузера, как не убить систему, что делать с Docker Desktop, сколько оставить под VS Code. Где-то на этом этапе локальная LLM-разработка перестала быть только про промпты.

Контекст оказался отдельной валютой

Длинный контекст был для меня принципиальной темой. Без него агентная разработка быстро разваливается: модель забывает документы, теряет текущий packet, не видит архитектуру, повторяет старые решения. Но большой контекст нельзя использовать как свалку.

Чем больше я экспериментировал, тем сильнее хотелось выносить подробности в файлы и артефакты, а в основной поток давать короткий пакет. Если модель пишет огромный отчёт в чат, она вредит следующему шагу. Если reviewer делает короткий repair packet, его можно передать локальному исполнителю без лишнего шума.

Context budget: tank with good and bad elements

Контекст, память и скорость постепенно стали частью одной системы. На бумаге можно сказать: просто возьми модель умнее. В работе приходится спрашивать иначе: сколько она ест, сколько генерирует, сколько держит, как часто можно запускать проверки, насколько больно перезапускать сессию, можно ли параллельно держать браузер и VS Code.

Где удалённые модели всё ещё полезны

После перехода к 35B удалённые модели не исчезли. Скорее их роль стала спокойнее. Бесплатная Laguna и другие OpenRouter-модели полезны как внешний планировщик, критик или reviewer. Особенно когда нужно посмотреть на решение со стороны.

Но отдавать им весь проект каждый раз неудобно. Лучше работает одноразовый внешний обзор: собрать review packet, отдать модели, получить конкретные замечания, вернуть локальному исполнителю короткий repair packet.

В итоге внешний reviewer стал не главным мозгом, а дополнительной парой глаз. Основная работа всё равно упирается в форму задачи.

Что поменялось в ожиданиях

Если смотреть назад, ожидания менялись несколько раз.

Сначала казалось, что нужен правильный промпт. Потом казалось, что нужен правильный агент. Потом правильный workflow. Потом стало понятно, что нужна среда: шаблоны, проверки, маленькие пакеты, понятные команды, короткие правила, нормальный способ переносить состояние между сессиями.

Мои текущие правила для локальной LLM-разработки

Что сейчас выглядит самым рабочим:

• маленький slice вместо большой задачи
• хороший scaffold вместо пустой папки
• короткий AGENTS.md вместо энциклопедии
• проверки после каждого шага
• stabilize pass перед следующим этапом
• websearch только по делу
• review packet вместо огромного dump
• форматирование только затронутых файлов
• детали в артефакты, не в чат
• один локальный тяжёлый исполнитель за раз

Ещё один вывод: шаблоны нужны разные. Один scaffold для Vue + Express приложения. Отдельный для полустатичного личного сайта. Отдельный для CLI. Возможно, отдельный для маленького backend-only сервиса. В каждом должны быть рабочие команды проверки, понятная структура, минимальные docs и несколько правил, родившихся из настоящих поломок.

Что в итоге

Самое приятное во всём этом опыте: прогресс правда есть. Ранние эксперименты часто выглядели как борьба с красивой болтовнёй. Потом началась борьба с конфигами, памятью, агентами, permission prompts, раздутыми документами и сессиями на десятки тысяч токенов. Сейчас локальная 35B уже может за несколько часов сделать кусок реального приложения на хорошем scaffold. С багами, с ревью, с человеческим надзором, но это уже рабочий материал.

Главный навык оказался не в том, чтобы написать идеальный промпт. Гораздо важнее научиться строить вокруг модели среду, где ей трудно делать глупости и легко делать полезную работу.

Локальная LLM в таком формате похожа на работника в личной мастерской. Ей нужны инструменты, верстак, понятные заготовки, короткая задача и проверка результата. Если бросить ей идею целиком, она легко устроит беспорядок. Если дать хороший scaffold, маленький slice и нормальный набор checks, она начинает приносить пользу.

Для меня это и стало главным выводом. Локальная модель уже может быть частью разработки. Но ценность появляется не от одной модели, а от всего вокруг: шаблонов, пакетов, ревью, проверок, ограниченного поиска, аккуратного контекста и честного отношения к её слабым местам.

← Назад к статьям