Всем привет!

Меня зовут Дмитрий Бутенко, я эксперт-разработчик банка Уралсиб.

Всё началось с рабочего созвона в первой половине февраля, на котором упомянули возможность применять модели ИИ прямо внутри инфраструктуры банка. Поначалу речь шла о работе через JetBrains AI Assistant — точечные запросы, ревью, вопросы по проекту. Вопросов, кстати, было много. Но довольно скоро случился переход на OpenCode, и это открыло принципиально другой уровень: не отдельные запросы к модели, а полноценные агенты, которые сами читают файлы, вносят изменения, итерируются по результату.

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

Два важных уточнения:

  1. Этот текст — о пользовательском опыте применения конкретных инструментов для конкретных задач. Вопрос о целесообразности выбора именно этих инструментов остаётся за рамками;

  2. Инструменты преднастроены — системные промпты, команды, субагенты. Это влияет на результат, но насколько сильно — отдельная тема, в тексте не рассматривается.

Стек

OpenCode версии 1.2.19 на момент написания

  • Модели: gpt-oss:120b и qwen3.5-27b-claude-4.6-Opus

  • MCP-интеграции: Confluence, GitLab, Jira, TestIT

  • Языки программирования на проектах и фреймворки: Scala/Java/Play framework, PHP/Laravel, PHP/Symfony

Инициализация проекта

Первое, с чем пришлось столкнуться — инициализация проекта командой /init-project. Кастомная команда инициализации, аналог «стандартной» /init, которая создает md-файл в корне проекта с его описанием и освновными деталями.  На scala-проекте поведение оказалось неожиданным: при первом запуске модель полностью игнорировала весь scala/java-код, если на это явно не указать вторым сообщением. Со временем и после нескольких обновлений проблема была устранена. PHP-проекты инициализировались без серьёзных затруднений, хотя здесь обнаружился другой нюанс: после анализа проекта модель иногда предлагала создать markdown-файл и зафиксировать изменения, но не показывала, какие именно. Содержимое приходилось запрашивать отдельно.

Рисунок 1: Модель не выводит .md, если об этом явно не попросить
Рисунок 1: Модель не выводит .md, если об этом явно не попросить

CRUD на Scala: две модели, разное поведение

На задаче создания нового маршрута со связанными контроллером и middleware обе модели справились, но по-разному.

Примеры промтов:

  1. Добавь новый маршрут в файл conf/routes: /v1/packet/:packet_code/options, название метода и контроллер OptionsController.getOptionsByPacketCode, параметры (packet_code: String, promo_code: String). Добавь класс контроллера в src\app\smart\controllers, класс опиши по аналогии с контроллерами рядом;

  2. В методе getOptionsByPacketCode контроллера app\smart\controllers\OptionsController добавь следующее: описание бизнес-логики действия.

qwen3.5-27b-claude-4.6-Opus следовал структуре, которая уже была в проекте, и при реализации похожих паттернов требовал меньше уточнений — прослеживалось понимание сложившейся архитектуры.

gpt-oss:120b с задачей тоже справился, но потребовал больше направляющих уточнений. На некоторых задачах с постановкой вида «реализуй новый роут с определенными параметрами» модель рисковала надолго уйти в раздумья — если явно не указать, где находится файл настроек роутов, какой контроллер создать и какие middleware подключить.

Отдельно выделяется задача, которая не поддалась ни одной из моделей: генерация класса на основе java.net.http.HttpClient для отправки формы с multipart/form-data. Несмотря на корректную последовательность вызовов, модели не справились с корректным расчётом размера тела запроса и корректное добавление boundary — код не работал, и никакие итерации его не спасли. В итоге этот фрагмент пришлось переписывать вручную.

Исправление ошибок

Для простых ошибок вроде NPE с полным стек-трейсом или четким упоминанием класса как правило проблем не возникает.

Пример: объясни, с чем может быть связана ошибка и напиши, как можно исправить [вставлен стек-трейс]

Рисунок 2: Пример объяснения и исправления NPE
Рисунок 2: Пример объяснения и исправления NPE

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

Рисунок 3: Пример анализа без результата
Рисунок 3: Пример анализа без результата

Изменение поведения API на Laravel: модель делает больше, чем нужно

Задача была такая: упростить алгоритм, который добавляет в архив файлы документов и их подписанные копии, полученные из внешнего сервиса. Оригиналы и подписанные файлы располагались в разных каталогах, при этом пути подписанных документов зависели от путей оригиналов. Суть упрощения: перестать учитывать эту зависимость и просто класть подписанные файлы в свой каталог как есть. Итог — три метода, два цикла, никаких архитектурных изменений.

Представить задачу можно следующим псевдокодом (строки, которые необходимо изменить, отделены // от остального кода)

zip = new Zip("/path/file.zip");
for (doc: documents) {
    zip->add(getFilePathByDocument(doc), doc->getData())
}
for (i = 0; i < signedDocuments.lenght; i++) {
    signedDoc = signedDocuments[i]
    //
    doc = documents[i]
    zip->add(getFilePathBySignedDocument(doc, signedDoc), signedDoc->getData())
    //
}
zip->save()

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

Рисунок 4: Пример лишних изменений
Рисунок 4: Пример лишних изменений

Добавление вызовов API на Symfony: аккуратно, но с оговорками

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

Dockerfile и Docker Compose: каркас есть, на этом все

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

Отдельно стоит отметить: при уточняющих вопросах о конкретном образе для одного из stage модель могла неверно интерпретировать запрос. Помогал более детальный вопрос с самого начала.

Исправление падающего теста на Laravel: модель меняет тест вместо кода

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

Рисунок 5: Фейковые объекты для теста
Рисунок 5: Фейковые объекты для теста

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

Помощник

Очень хорошо модели показали себя в режиме "планирования": модель работает в режиме только на чтение, как правило анализируя проект и отвечая на вопросы по нему, или на какие-то более общие вещи. Например, объяснение синтаксических конструкций, scala в моем случае, вывод связей классов в приложении, примеры использования архитектурных паттернов с привязкой к фреймворку и т.д. Разработчику, который не знаком с какими-то особенностями проекта это существенно снизит время на онбординг в проект.

Рисунок 6: Пример объяснения, как можно пометить участки кода как deprecated
Рисунок 6: Пример объяснения, как можно пометить участки кода как deprecated

Недостатки самого инструмента

Несколько слов об использовании OpenCode в Windows. При запуске в PowerShell или CMD недоступна вставка из буфера обмена. В MSYS2 OpenCode не запускается вовсе, поэтому единственным рабочим вариантом остаётся терминал внутри IDE — который приходится откреплять в отдельное окно. Вставка медиаконтента в промпт не работает, распознавание изображений тоже недоступно. И иногда появляются артефакты, похожие на те, что на изображении ниже.

Рисунок 7: Пример артефактов в терминале
Рисунок 7: Пример артефактов в терминале

Итоги

Модели уверенно справляются с рутинными задачами: создание новых эндпоинтов, генерация миграций, описание моделей по бизнес-требованиям. По мере усложнения задачи растёт и необходимое описание желаемого результата — и вот здесь начинаются проблемы. Модель может уйти в ложном направлении или изменить больше кода, чем нужно. В итоге время тратится не на решение, а на исправление результата уточняющими промптами или откатами с повтором. Буквально приходится подводить модель к решению: сначала обозначить рамки, потом требования, потом общий алгоритм. Иногда проще решить задачу самостоятельно, чем всё это расписывать.

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

Собрав собственные кейсы, стало интересно, каким возможным опытом делились коллеги из других компаний. И буквально здесь же нашлась пара похожих примеров: один и два. Из чего-то более масштабного с более формализованной методологией есть исследование IBM Research на 669 сотрудниках: наиболее стабильный прирост от AI-ассистента дают задачи на понимание чужого кода.

Наблюдается один и тот же паттерн: локальные модели берут рутину, помогают разобраться в незнакомом коде и служат удобным собеседником для проектирования. Сложные многошаговые задачи с размытыми границами — если для «сервисных» моделей обычная задача, то для локальных пока зона риска, которую можно с разной степенью эффективности закрывать сменой модели, системных промптов, дополнительным обогащением, через, например, RAG.

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


  1. Timmek
    29.06.2026 15:28

    В Уралсибе не могут выделить ~16 млн ₽ (одна квартира) на одну ноду с 4-мя B200, дабы захостить MiniMax 2.7 / GLM 5.2 ?

    Ну какой GPT OSS, ну какой 3.5-27b


    1. ToniDoni
      29.06.2026 15:28

      И сколько одновременно пользователей обслужит такая нода без деградации?


  1. SlavaVSLK
    29.06.2026 15:28

    Маюсь немного схожей фигней, сразу скажу один rag вам тут не поможет. Я делаю целый цикл на локальных моделях. RAG там присутствует, но только как маленький, незначительный слой предварительной помощи в навигации. Пайплайн строит сырую карту отсеивая мусорные пути, потом наиболее вероятнее кандидаты отправляются узкому спициалисту llm на проверку approve / reject, маленькими скоупами и пока я добился качества карты не выше 0.8, а нужно хотя-бы 0.95, потому что от качества карты маршрутов сильно зависят дальнейшие шаги. И это проектоориентированный пайплайн, я боюсь представить, что было бы на универсализации)) маленькие локальные модели, помещающиеся в 16 гб vram, очень тупенькие.


    1. d3d11
      29.06.2026 15:28

      А можно, подробнее, что за карта каких маршрутов?


      1. SlavaVSLK
        29.06.2026 15:28

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

        Но пока что это даётся с трудом. Буду рад идеям.

        Железо: ryzen 9 5900x, 64gb ram, rtx 4080 16gb vram. Модель на данный момент использую qwen2.5-coder-14b-instruct, max ctx примерно 50к


        1. DVamp1r3 Автор
          29.06.2026 15:28

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

          В отрыве от агентской разработки у меня есть кейс генерации кода через LLM, код валидируется сначала синтаксически, затем через тесты к сгенерированному коду. Если какой-то из этапов не пройден, то весь конвейер запускается сналала. Но это не исключает, например, ситуаций, когда корректный код совсем не про то, что в исходном промте :) Это как пример, на практике там все сложнее: код соответствует в общих чертах, но в деталях могут быть проблемы. Про ваш опыт (как я понимаю, схожий) именно в контексте агентов можно где-то ознакомиться?

          А под картой вы имеете в виду context precision или что-то свое?


  1. ToniDoni
    29.06.2026 15:28

    А на каком железе крутилась модель и как с latency и со скоростью генерации?


    1. SlavaVSLK
      29.06.2026 15:28

      Железо описал выше. Скорость генерации 39-54 t/s, не отслеживаю от чего зависит, в одной и той же сессии вот такой разлет. В целом комфортно. Latency в данном контексте не понимаю о чем речь


      1. ToniDoni
        29.06.2026 15:28

        Latency - time to first token при контексте реально заполненном до 50К (ну или 25К хотя бы), при первом запросе, и втором (когда должен работать префикс кеш) - вот это интересно.


    1. DVamp1r3 Автор
      29.06.2026 15:28

      По железу информацию давать не могу (и сомневаюсь, что она мне известна в достаточной степени, чтобы с уверенностью ответить), а вот что касается остального, насчет latency, тут и правда непонятно, что имелось в виду, но ответ от сервиса в виде лога рассуждений получаю примерно сразу же :). Что касается скорости генерации, у opencode, насколько мне известно, нет отображения именно этой метрики. И не особо это интересовало, т.к. больше внимания уделяю "качеству" сгенерированного. Если все-таки дать какие-то численные метрики, то "инициализация" проекта с кодовой базов ~8Мб (~1200 файлов) занимает ~6 минут и выдает описание на 2500 символов. По текущему опыту это довольно долгая задача. Дольше был только пример из материала, когда модель не справилась с поиском причины ошибки, по-моему за 10 минут тогда перевалило. В среднем режим планирования (поиск цепочки зависимостей в проекте, объяснение, составление плана изменений и прочие задачи, которые требуют не точечного погружения в участок проекта, а переходы по файлов) это 2-3 минуты. Фикс синтаксических ошибок, предупреждений внутри файла решается за секунды.