Выпуск 66 · подкаст «Тысяча фичей»

#66: Veai: БАЗА ПО КОДИНГ АГЕНТАМ

2:26:39
↓ скачать mp3

Александр Пахомов и Михаил Костицын (разработчик кодинг-агента veai, IDE-native агента для JetBrains IDE) по шагам «собирают» кодинг-агента: от ассистента-чата к агенту с инструментами — терминалом, чтением/записью файлов, поиском — и разбирают ключевой трейд-офф между свободой (один универсальный терминал) и контролем (набор узких, надёжных тулов), а также контекст-менеджмент как главный ресурс. Отталкиваясь от IntelliJ-платформы, они показывают, что даёт агенту IDE поверх терминала — LSP и PSI, инспекции, декомпиляцию, дебаггер и «формальные методы» (инструментация байткода, анализ моргающих тестов, символьное исполнение, SAST), — и обсуждают enterprise-спрос на локальные модели, бенчмарк-систему veai с верифайерами и LLM-судьёй, проблему бенчмаксинга и dogfooding.

Главное

  • Кодинг-агент = LLM плюс чат плюс инструменты: инструмент — это функция `input → output`, о которой модель узнаёт через специальный tool-call-токен и описание в системном промпте.
  • Каждый добавленный тул живёт в «горячем» контексте, который гоняется на каждом шаге; лишние тулы (и MCP, и скиллы) съедают контекст, а после ~80–85% заполнения модели начинают деградировать — проверяйте это через `/context`.
  • Главный трейд-офф — один универсальный терминал (безграничная свобода, но и безграничное пространство ошибок и риск безопасности) против набора узких, надёжных тулов (`read`, `write`, `search`, `ls`), которые контролируемы и экономят токены; практичное решение — совмещать, оставив терминал на крайний случай.
  • IDE — это готовая абстракция над language-specific туллингом: вместо отдельных тулов на каждый язык агенту дают высокоуровневые `build`/`test`/`fmt`, которые инкапсулируют язык внутри, а IntelliJ-платформа определяет язык проекта через включённые плагины.
  • `LSP` приносит в редактор синтаксис (лексер + парсер → AST) и базовый автокомплит; `PSI` (Program Structure Interface) в IntelliJ добавляет семантику — `findUsages`, различение использования и override — и потому куда лучше подходит как инфраструктура для агента, чем греп в терминале.
  • IDE-инспекции, декомпиляция и дебаггер — киллер-инструменты для агента: инспекции дают статический анализ без запуска, декомпилятор превращает `.class`/зависимости без документации в читаемые исходники, а встроенный дебаггер (breakpoint + evaluate expression) экономит время и токены против цикла «добавь принт → пересобери».
  • Ставка veai на IntelliJ обоснована enterprise-спросом: банки и финтех не готовы «сливать код в Anthropic», поэтому нужны локальные модели в контуре (`GLM`, `DLP`-шлюз), а мощные IDE-тулы позволяют слабее-догадливым моделям работать надёжно.
  • Качество агента veai мерят бенчмарк-системой с эмулятором пользователя, жёсткими верифайерами (сборка/тесты/покрытие) и LLM-судьёй; набор верифайеров работает как система сдержек и противовесов против бенчмаксинга, а поверх всего — ручное QA и dogfooding.

В выпуске

  • Михаил КостицынРазработчик кодинг-агента veai (IDE-native AI-агент для JetBrains IDE на базе IntelliJ-платформы); руководит несколькими командами, ранее делал агента для Rider (.NET). Специализируется на индексах, PSI и формальных методах (символьное исполнение, генерация тестов).
Расшифровка

[00:00] Александр: Дорогие слушатели, всем привет! В эфире подкаст «Тысяча фичей». У меня в гостях разработчик veai Михаил Костицын. Привет, Михаил!

[00:07] Михаил: Всем привет! Рад быть на подкасте.

[00:09] Александр: Да, рад быть на подкасте я тоже. Каждый раз, вот прямо каждый раз, когда я включаю запись, я внутри себя говорю: как же я рад быть на подкасте. Некоторые слушатели, думаю, заметят, что я гундошу — голос немного поменялся. У меня как-то был выпуск, по-моему, второй или третий выпуск «Тысячи фичей», это ещё года три с половиной назад. Он назывался «Гундосый спека», как сейчас помню. Я его как раз таким же голосом записывал — тоже был синусит, как сейчас. И я подумал: а это ведь не причина не записывать подкаст, потому что настолько нравится, настолько радостно всё это делать, что — ну куда деваться? Температуры нет, значит, нормально. Так что, слушатели, наслаждайтесь моим другим голосом. Я бываю разный.

[00:52] Александр: Ну да, сегодня мы не про меня — сегодня мы про кодинг-агентов, вообще говоря. Разговариваем, попытаемся прямо сейчас, что называется, собрать в голове своего кодинг-агента. Но вначале немного разберёмся, чем Михаил занимается, что такое veai и как этот кодинг-агент может нам быть полезен. А дальше уже начнём копать глубже, как мы в подкасте любим и за что его ценим. Кстати, если вам вообще нравится то, что я здесь делаю, дорогие слушатели, не поленитесь: поставьте реакцию в Телеграм-канале — мне приятно. А ещё лучше зайдите на YouTube и поставьте лайк, потому что на YouTube тоже всё выходит. Или если вы слушаете в Apple Podcasts или в Яндекс.Музыке — поставьте лайк, напишите отзыв, потому что это продвижение. Мне как автору это важно. Знаете, три с половиной года делать одно и то же — можно немного и выгореть. Так что, чтобы Саша не выгорел, поддержите меня.

[01:45] Михаил: Да, давайте поддержим.

[01:46] Александр: Не знаю, почему я никогда этого не говорю, но вот сейчас сказал. Ну да, сегодня мы не про меня — мы про кодинг-агентов.

[02:21] Александр: Так, Миш, не надо рассказывать всё подряд, но давай верхнеуровнево: из чего должен состоять мой минимальный кодинг-агент, что это вообще такое? Мы с тобой говорим «кодинг-агент», а что это? Давай начнём с этого.

[02:33] Михаил: Да, отличный вопрос. Я бы даже начал с ассистента, так, хронологически. Когда только-только произошёл бум LLM-ок и они в целом появились, люди сразу захотели каких-то длинных сессий с ними — какой-то чатик, в котором можно общаться, спрашивать про код, про проект, прикреплять туда сниппеты кода и как-то с этим работать. Вот это ассистент: LLM плюс чатик, в котором мы можем с ней общаться. И, в общем-то, больше ничего.

[03:02] Александр: То есть это времена, давай представим, наверное, три года назад. 23-й год, что-то такое.

[03:10] Михаил: Да, когда ChatGPT 3.5, наверное, вышел, и был вот этот чат — собственно, слово «чат».

[03:15] Александр: Да-да, оно там было. Мы открывали этот чат, и туда можно было написать. Первая моя интеракция была именно такой: я копировал кусок кода, вставлял и говорил — вот здесь что-то не так, или как поправить вот эту багу. Я получаю вот это, хочу вот то. Самая первая, древняя, знаешь, такая мезозойская эра. И тогда ещё Copilot был, вот этот умный автокомплит, я им тоже наслаждался. Такой челлендж слушателям: а на какой стадии развития в мире AI находитесь вы? Если вы начинаете узнавать себя в нашей хронологии — «о, а я вот так пользуюсь» — то поймёте примерно, где вы и куда вам двигаться. Сейчас мы в самой ранней точке: ChatGPT в вебе, скопировал, вставил, получил ответ, пошёл руками имплементировать.

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

[04:55] Михаил: Но чего-то в этой схеме не хватало. Напомню, у нас пока есть только LLM и чатик. Дальше следующий пойнт: а почему бы нам не завести специальные токены внутри токенов, которые порождает LLM, которые будут означать специализированный вызов некоторого инструмента? Вот здесь появляется понятие инструмента, которым может пользоваться сама моделька — уже агент, как только мы добавляем инструменты. Сама реализация агента в таком случае просто перечисляет этот набор токенов и говорит, что делают эти инструменты.

[05:30] Александр: Давай возьмём пример: как я говорю, пишем самого примитивного агента. Значит, есть у нас для начала просто моделька, есть какой-то чатик — допустим, чатик мы сделали. Всё, что нам нужно, чтобы превратить это в агента, — написать самый простой инструмент для модельки.

[05:50] Михаил: Это терминал. Просто взять терминал — возможность его запускать, отслеживать. Пусть будет у нас один тул, пока что самый примитивный. Что этот инструмент будет делать? Он будет принимать на вход некоторую строку и выдавать на выход тоже некоторую строку. Сам код инструмента будет представлен как-то так: когда нам подают какой-то аргумент на вход, мы запускаем сессию терминала, в которой команда, переданная в качестве аргумента, исполняется. Весь ответ этого терминала мы выдаём на выход. Вот мы написали такую функцию, умеем теперь терминал запускать. Но теперь надо как-то связать это с тем самым специфическим токеном для модельки, чтобы она могла автономно сама звать этот инструмент.

[06:40] Александр: Давай тут сделаем паузу. Я сейчас, как художник, расставлю акценты на полотне, на котором мы рисуем. Вначале мы сделали следующее: мы с Клодиком початились, сказали — давай сделаем чатик, эту мезозойскую эру сначала имплементируем. Первый шаг — сделаем какую-то интеракцию, окей, в виде веба. Делаем агента, у которого интерфейс пока что в вебе. Говорим: давай делай сервер — не знаю, Django, Spring Boot, какая разница, — сделай мне чатик в вебе, чтобы я туда писал и ответ от LLM приходил. Он имплементирует, это минуты за две приложение, в котором я могу написать «привет, кто ты». Приложение обработает моё сообщение, отправит его — я так понимаю, как разработчик, в какую-то LLM-апишку. Это может быть локальный доступ, у Anthropic или OpenAI-compatible API. По-хорошему, это выбор. Ну, допустим, я с OpenAI общаюсь, потому что сейчас с подписками в Anthropic сложно. Я купил себе подписку на Codex, и у меня есть доступ к GPT-5.5. Он дал мне свой subscription key, мы настроили аутентификацию — всё, мы аутентифицировались в API. И теперь я пишу вот эту веб-морду: «привет, ты кто?», нажимаю Enter. Мой веб-сервер обрабатывает сообщение, формирует запрос через библиотеку, которую он выбрал для интеракции с OpenAI-compatible API, отправляет POST-запрос в OpenAI, получает ответ, отображает мне его в веб-морде. Вот это примитивный чатик, который мы имплементируем. И он поддерживает продолжение диалога — то есть эту историю сообщений постоянно туда-сюда гоняет в OpenAI. Это мы, считай, сделали GPT-3.5.

[08:32] Александр: Следующим шагом мы говорим: вот он ничего не знает, у него только окно взаимодействия со мной — input и output text field. Собственно, всё. Этого недостаточно. Мы хотим, чтобы он что-то научился делать, что-то запускать, принимать какие-то решения. И мы добавляем концепцию tool-ов в нашего агента. И тул, который мы хотим заимплементировать, на самом деле очень амбициозный. Ты сказал «терминал», и я такой: господи, я как человек, который хочет своего агента, чтобы тот запускал терминал, делал в нём интеракцию с Claude по подписке и возвращал мне результат, — я был бы счастлив такой тул иметь. Но пока я его не написал. Так что терминал — это прямо сложно. Я бы, наверное, какой-нибудь shell просто взял, типа sh. Ну, просто «запусти shell-скрипт».

[09:24] Михаил: Процесс, да. И ты ему на вход даёшь shell-скрипт, код.

[09:29] Александр: Ты ему даёшь. Или он сам генерирует — тут уже надо подумать аккуратно.

[09:34] Михаил: И результат он тебе возвращает. То есть мы тул описываем как функцию в терминах языков программирования: input → output. Вот наш тул. И, по сути, так и есть — это просто функция, которая исполняется у нас на машине. То есть на веб-сервере появится какой-нибудь tool-registry, где все тулы сложены. Это код, который исполняется, описанный в терминах input → output. И дальше это просто код. Представьте, мы на сервер добавили новый сервис или статический метод. Вот он есть. Но раз он есть и о нём никто не знает — ни LLM, ни мы, — то работать он не будет. Сейчас мы на этом этапе.

[10:15] Михаил: Теперь мы хотим, чтобы про наш метод, который мы реализовали в коде, каким-то образом узнала OpenAI и смогла его вызывать. Как только код тула готов — это ещё далеко не всё. Потому что инструмент для LLM состоит из двух частей. Первая — та, что мы написали в виде кода. Вторая — некоторое пояснение для модельки, что этот тул вообще делает, зачем его звать и когда это нужно делать. Некоторое описание. Это мы добавляем через… импортируем библиотечку, точное название уже не вспомню, но у неё есть аннотации. Мы можем навесить аннотацию на наш тул и таким образом описать, что он будет делать.

[11:04] Михаил: Давайте даже проще. Неважно, как мы напишем это описание — пусть оно просто лежит где-то рядом, хоть в текстовом файлике. Самое главное — что мы теперь будем делать иначе, чем раньше. Во-первых, регистрировать специфический токен, назовём его, скажем, shell-tool. И теперь, ещё до того, как пользователь написал сообщение в чатик, мы отсылаем некоторый системный промпт, в котором говорим: «смотри, вот такой токен — если ты его напишешь, он вызовет наш тул. Тогда мы у себя на стороне пользователя запустим этот инструмент, получим результат и вернём его тебе». Что-то такое полетит в системный промпт. И с этого момента моделька понимает: ага, у неё появилась некоторая ручка, которой она может пойти и запустить какой-то скрипт, что-то посмотреть, получить ответ и понимать проект чуть лучше.

[12:07] Александр: То есть мы предоставляем модели, да, вот эту хорошую аналогию — ручку. И на самом деле этот специфичный токен — то, что разделяет большинство API LLM, которые я видел. У нас есть обычные токены, ну, текст — потому что задача Large Language Model в том, чтобы догенерировать токены из input. Ты дал «A плюс B равно» и отдаёшь модели — она тебе продолжает: «AB» или «C», зависит от того, что было в системном промпте. Она просто продолжает твою речь в виде токенов. И большинство токенов — это текст. Но модели на уровне API сейчас разделяют текстовые токены и tool call’ы. И если она возвращает этот специальный токен — тот особый, который ты вначале зарегистрировал, — то в интеракции твоего сервиса, твоего coding agent, и API есть контракт, и в ходе этого контракта ты обозначаешь список своих токенов, у которых есть просто идентификатор — в нашем случае shell_script или shell_script_tool, одна строчка через underscore, — и некоторый description. По сути, это описание сигнатуры на естественном языке для LLM: есть такая функция, у неё такой вход, такой выход, так она работает, вызывай её, когда хочешь сделать вот это. Ну, полное описание. Как будто мы Javadoc’ом описали, что делает функция у нас в сервисе. Отправив это где-то в system prompt или в специальном register tool — неважно, — моделька про это знает, и теперь она будет эмитить эти токены.

[14:00] Александр: И это будет прямо параллельно. В коде, скорее всего, будет не так, что ты слушаешь стрим токенов и — оп-оп-оп — тут какой-то специальный пришёл. Скорее всего, у тебя результат будет возвращаться, и у каждого результата будет тип: текстовый результат или tool call. И ты в коде такой: if tool call then — и дальше пошёл по-особому это обрабатывать. А если это текст — просто печатаешь его в морду и ничего не делаешь.

[14:31] Михаил: Вот, мы зарегистрировали этот shell-call — и что дальше происходит. На этом моменте я бы ещё немножко хотел остановиться и вспомнить одну интересную аналогию, которая мне понравилась. Помните, из «Черепашек-ниндзя» был такой злодей — Крэнг? По сути, это был мозг, который сидел в роботизированном костюме: у него были руки, он что-то делал. Я хочу развить метафору с агентами: представьте, что LLM — это, по сути, мозг, она сама по себе ничего делать не может. К ней мы добавляем такую трубку, или речевой аппарат, которым она с нами общается. Но, даже когда мы добавили речевой аппарат, она всё ещё ничего не может менять, трогать. А вот как только мы даём ей руки, которыми она может куда-то залезать, что-то рассматривать, менять, — вот это уже что-то более автономное. Получился такой Крэнг. Надеюсь, слушатели поностальгировали.

[15:32] Александр: Для тех, кто не смотрел «Черепашек-ниндзя» или совсем забыл этот реликт, дам аналогию посвежее. Это из «Рика и Морти», серия, где Рик превратил себя в огурец, смылся в канализацию и дальше с этим огурцом что-то делал: пришпандорил себе зубочистки, какие-то штуки, которыми начал взаимодействовать с реальным миром, и превратился в существо, которое может двигаться и решать свои проблемы. Хорошая аналогия. Я иногда, знаешь, ещё так это вижу: если мою картинку представить, то LLM в чистом, кристальном виде — это мозг в банке. Просто мозг в банке. Он умный, реально умный, он талантливее тебя, он точно умнее. Но он мозг в банке, чувак. А ты кожаный мешок, ты можешь ему дать какие-то интерфейсы. И ты должен понимать: если ты даёшь ему интерфейс, например, полный доступ к своему компьютеру — в случае, если ставишь Open-Close на MacBook, удачи, — то ты, по сути, мозгу даёшь доступ к своему мозгу. А ты готов на это? В этих терминах интересно мыслить. Это не что-то очень визионерское — это просто правильный ход мысли и понимание того, как штуки устроены, оно ставит на место. Так что аналогия принимается. Хорош.

[16:53] Михаил: Супер, отлично. Ну вот, как только мы такой if добавили в своего агента, теперь у нас есть разграничение на отдельные инструменты — дальше будем называть их тулами для простоты — и отдельно на текстовый результат, который мы показываем в чатике. Теперь нам остаётся добавлять этих инструментов нашему агенту, чтобы он был способен на всё больше и больше вещей, — добавлять ручек, которыми можно разные вещи сделать. Но здесь, в отличие от человеческих рук, которые достаточно универсальны, супер-универсальной ручки для агента — она в каком-то смысле есть, это как раз терминал, в котором можно писать любые команды. Об этом чуть подробнее позже, но к ней нужно доходить осторожно. В какой-то момент встаёт вопрос: а какие инструменты мы вообще хотим добавить в нашего агента?

[17:51] Михаил: И здесь важно сказать, что проектирование этих инструментов — что они будут делать — достаточно сложно, потому что мы должны балансировать между двумя стульями. Один стул: чем меньше инструментов, тем лучше, иначе оно запутается. Если мы добавим 500 разных ручек, моделька может смотреть и не понимать, чем третья отличается от 150-й, от 450-й. Это как раз проблема контекст-менеджмента. Очень важная проблема, которую каждый разработчик должен отчётливо понимать: добавляя новые тулы, ты добавляешь их куда? Ты добавляешь их в контекст. Причём в контекст, который постоянно гоняется туда-сюда, потому что на каждом шаге есть вызов модели. Модель должна знать, что у неё есть возможность вызвать этот тул, и ты ей постоянно об этом напоминаешь. И ты засоряешь свой горячий контекст.

[18:48] Александр: И если кому интересно поразбираться — просто посмотрите, какие у вас там MCP подключены, какие скиллы подключены, какой ещё контекст стоит для работы с агентом. Откройте Claude Code, сделайте /context и посмотрите, сколько там всего. И если тул спроектирован или набор тулов подобран так, что, скажем так, трейд-офф не соблюдён, то у вас может быть десятки процентов контекста заняты описанием ваших тулов. То есть мы говорим модели: помни всё это — вместо того, чтобы решать нашу задачу. Мы делаем её тупее.

[19:21] Михаил: Да, всё так. На самом деле может произойти даже такое, что у модельки контекст, допустим, 100 тысяч токенов, а у нас 500 тулов, и каждое описание тула, ну, не знаю, — и чем больше тулов, тем больше заранее забит контекст, свободного места всё меньше. А мы ещё даже не начали ничего писать: это просто описание тулов, мы ни одного запроса не добавили. Бывают вообще ситуации, когда моделька маленькая, а тулов очень много, и мы просто не можем написать ни одного запроса, потому что весь контекст забит. Но даже не это самое страшное. Самое страшное — то, о чём Саша сказал: когда тулов много, контекст не полностью забит, а где-то на 90%, но мы знаем, что модельки уже к 80–85% заполнения — сильно отличается от модели к модели — начинают деградировать. Ответы действительно становятся хуже. Поэтому здесь нужно соблюдать хороший баланс между возможностями, которые мы даём агенту, и количеством тулов, которые ему поставляем, чтобы всё это не переполнить.

[20:36] Михаил: И вот тут начинается самое интересное. А как нам спроектировать инструменты, какие точно необходимо дать агенту, чтобы он хоть что-то умел? Давайте начнём с самых простых. Дадим агенту возможность читать файл и писать файл. Допустим, начали с инструмента, который читает файл. Какие у него могут быть аргументы? Ну, он точно должен знать, во-первых, какой файл прочитать. Поэтому путь до файла будет нашим первым аргументом. Давайте представим, что вначале мы сделали совсем простой тул для чтения: он принимает только путь до файла и весь файл сразу погружает в контекст. И тут мы сразу нарвались на проблему: если файл большой, а агент захотел его прочитать, он себе целиком забил контекст. А что если контекст вообще закончился — он не сможет нам ответить никак. Как-то не очень.

[21:41] Михаил: Поэтому, учитывая, что файлы бывают очень большие, давайте дадим агенту самому решать, что он из файла хочет прочитать. Сделать это можно очень по-разному — возьмём самый простой способ. Вторым аргументом запросим start offset, где offset — это номер строчки. Третьим аргументом — end offset, тоже номер строчки. start offset всегда меньше или равен end offset, и end offset у нас включающий. Теперь мы можем в агенте попросить его прочитать какой-нибудь файл, поискать там сущность. Он уже может читать кусочками: прочитал начало, посмотрел — ага, того, что мне нужно, тут нет, читает следующий кусочек. Он может хоть бинарным поиском пользоваться. Так или иначе, он будет пытаться прочитать файл и найти нужную составляющую. Это уже хорошо, но всё ещё не идеально. Однако пока на этом остановимся с чтением и перейдём к записи.

[22:56] Михаил: Здесь нам сразу нужно либо два тула, либо один. Давайте пока остановимся на одном. Допустим, тул будет называться writeToFile. Первым аргументом — опять же путь до файла. Если этот файл есть, мы прямо в коде проверим, есть ли он: если есть — будем дописывать, если нет — создавать. Всё просто. Второй аргумент — это, например, какой-нибудь offset, на который нужно поместить текст, который будет третьим аргументом. И вот как только мы дали два таких инструмента агенту, он уже в целом может смотреть на ваш код, исследовать его. Правда, пока он может читать только файлы, и ему нужно догадываться, какие у нас директории, какие файлы вообще есть. То есть ему нужно, видимо, через shell-скрипт, который мы ввели вначале, узнавать о структуре проекта — какие пути передавать, как файлы называются. Ну, допустим, он со всем этим справился. В общем-то, он уже может что-то модифицировать, кодить, работать по нашей задаче. И вот здесь начинаются тонкости.

[24:09] Михаил: Те тонкости, про которые мы говорили. В чтении, например, агенту нужно перечитать так или иначе всё, чтобы найти нужное, а иногда это очень-очень много, прежде чем он найдёт. И получается, что нам в каком-то виде нужен ещё инструмент поиска. Давайте я его тоже напишу. С чего начнём инструмент поиска? Можем остановиться на одном аргументе — самый простой вариант. Фраза, какой-то текст будет единственным аргументом. Этот текст забирается внутри инструмента в его коде, и мы, уже имея доступ к проекту, к папке, просто выполняем какой-нибудь греп по этому тексту. В качестве результата выдаём набор путей до файлов и оффсеты в них по тому тексту, который нашли. С этого момента у агента сильно больше возможностей: он перестаёт читать файлы случайным образом, перестаёт угадывать название и структуру. Он может сделать поиск, посмотреть, где нужная часть, и после этого запустить чтение с путём и оффсетом, которые получил от предыдущего инструмента.

[25:49] Михаил: Однако и это не идеально, потому что, чтобы агенту на данном уровне что-то прочитать, ему обязательно нужно сначала что-нибудь поискать. А что, если мы попросили его в каком-нибудь совсем зачаточном приложении, где ничего искать не нужно, написать новый модуль? Для начала агенту нужно понять, какая структура у проекта, какие модули есть. Он может поискать по слову «модуль», но что, если не угадает с названием? Нам нужно предоставить ему доступ к директориям, к структуре файлов. Сделать это можно ещё одним способом. Давайте заведём тул, в котором аргументом будем передавать путь до текущей папки, структуру которой хотим получить. Скажем, что корень нашего проекта — это какой-нибудь слэш, чтобы агент не знал о путях помимо нашего проекта. И тогда этот тул, принимая путь до папочки, будет выдавать нам прямо в контекст всю структуру: какие файлы в ней находятся. Грубо говоря, результат ls. С этого момента агент уже более подробно получает информацию о том, какие в проекте составляющие, какие файлы, какие модули. У нас получился уже какой-то супер-базовый агент. Можно сказать, мы молодцы, что-то уже получилось. И что-то даже будет работать.

[27:32] Александр: Да, мне, знаешь, не отпускает аналогия с мозгом в банке. С точки зрения дизайна тулов она просто золото. Потому что ты сразу представляешь: ну вот представь, ты тот самый мозг в банке. Ты гениальный программист, который может решить любую задачу, но ты в банке, сидишь и ничего не видишь. Тебе комфортно, ты очень умный, богом себя ощущаешь. Но ты в банке, чувак. К тебе пришли и что-то бубнят через чат. Первый интерфейс — это «бубнёж»: люди на тебя смотрят и что-то от тебя хотят. Окей, ты научился их слышать — это наш text-input, text-output. Интерфейс есть. Человек приходит и говорит: слушай, пофикси багу. Ты сидишь, вокруг банка, и всё, что ты слышишь, — «пофикси багу». Ты такой: ну какую багу, где, что фиксить? Я же не телепат. Вот LLM не телепат. Ты говоришь: дай мне read-файл, чтобы я читал код, в котором бага. Он тебе: на, read-файл. А ты: какой файл мне читать? Ты хочешь, чтобы я сам поискал путь? Дай мне ls, чтобы я видел структуру. Тебе: вот ls. Ты: ага, вижу controllers, мне нужно посмотреть, какие контроллеры, — значит, find мне нужен, дай find по всем контроллерам с таким названием. Окей, мне дали find.

[29:13] Александр: Представьте себя вот этим мозгом в банке, который по SSH к серваку в голый docker-образ подключился и пытается там что-то попрограммировать. Вот это литерали вы — то есть агент. И вот мы ему дали ls, grep, find, всё это, и он уже смотрит. И с помощью этих тулов, так как он талантливый, ему — ну, может, сейчас выясним, что и нужна, — но ему не нужна IDE, чтобы запрограммировать. Он тот самый линуксоид, который в Vim может фигачить — да даже Vim ему не нужен, он тебе sed‘ом файлы поменяет, grep‘ами почитает, sed‘ами, cat‘ами всё сделает. Мы ему это дали. И это прикольно: сейчас наш агент может посмотреть сорцы, модифицировать сорцы, узнать структуру проекта по файловой системе и применить свой интеллект, чтобы это всё сделать. Базовый агент, который что-то может, будет работать.

[30:02] Александр: Но, продолжая аналогию: вот ты поправил файл — а что ты хочешь после того, как внёс изменения? Наверное, мы сейчас перейдём к тому, что хочется результат хотя бы скомпилировать, если это проект на компилируемом языке. А если не компилируемый — то как-то запустить, может, какие-то тесты, линтеры, форматтеры. То есть мы хотим больше туллинга. Давай про «больше туллинга» и поговорим. Что ещё надо дать нашему агенту, чтобы он быстро и правильно всё фиксил? Ваншот-промт был у нас, синглшот.

[30:48] Михаил: Здесь я бы хотел ещё такой момент озвучить. Мы можем сейчас добавлять инструменты для запуска билда, компиляции, для запуска тестов — обо всём этом обязательно поговорим. Но давайте на секунду окунёмся ещё в изначальную идею про терминал. Возможно, кто-то сидит и думает: а зачем нам давать отдельно ls, отдельно чтение, отдельно поиск — всё это отдельными ручками? Давайте просто терминал, пусть он любую команду пишет, которую знает, она исполняется в терминале, а мы output отдаём.

[31:31] Александр: То есть мы, как тот гениальный разработчик в банке, вместо того чтобы говорить «научи меня, дай мне тулы читать и писать файлы», говорим: дай мне SSH-доступ с root-правами, и я тебе всё пофикшу на сервере. Вот что мы на самом деле хотим. И это, наверное, самое эффективное. Нормальный вопрос: почему нет?

[31:50] Михаил: Да, всё так. И вот тут, когда мы, по сути, дали агенту доступ ко всему терминалу, — это здорово, потому что нам больше не нужно ничего выдумывать: люди всю свою работу по разработке выполняют в одном только терминале. Почему агент не может? Более того, он ещё на этом был обучен, и в терминале есть и команды билда, и запуска тестов — всё можно сообразить. Но в чём подвох? И есть ли он вообще? Если мы дали ему доступ из-под root, по SSH, — а где гарантия, что агент правильно понял задачу, а не додумал и не сгаллюцинировал, что нужно сделать rm -rf всего сервера? Мы дали ему абсолютную свободу, он может писать что угодно — но при этом он может что-то нам этим сломать. Как только мы дали такую свободу, мы перестали его, по сути, контролировать.

[32:46] Михаил: Но даже это не единственная проблема. Точнее, наша-то она единственная, но с этого момента появилась проблема у модельки. Она в том, что мы сказали: вот тебе окошечко с терминалом, можешь писать что угодно, — но при этом мы не даём отдельных тулов с описаниями, что вот этим ты должен читать, этим писать, а даём всё скопом: сама разберётся, если что, --help напишет. И тут интересный момент: если мы даём слишком большую свободу, особенно если моделька, может быть, не на пятёрку училась, а чуть пониже, послабее, — ну, какому-нибудь Sonnet-у дали мы это…

[33:34] Александр: Да я бы даже сказал, не Sonnet-у. Представим, что мы всё это дали Qwen’у, какому-нибудь Qwen Coder’у, ещё и маленькому, — совсем страшно.

[33:43] Михаил: А страшно почему? Потому что он может просто запутаться и писать команды в терминале, которые значат не совсем то, что он имел в виду. Из-за того, что нет явного разделения, подробного описания, он всё пишет как хочет. Эти проблемы можно как-то решать, их тоже затронем, но вот этот момент как раз возвращает нас к привычному стилю, где мы даём инструменты отдельно.

[34:17] Александр: Смотри, мне этот трейд-офф — что мы можем дать один инструмент, позволяющий всё, или набор инструментов, позволяющих что-то конкретное, — кажется очень важным. И он новый, знаешь: перед нами, как перед инженерами, таких вызовов раньше не стояло. Мы так не решали задачи никогда. На одной чаше весов — LLM сама по себе, её когнитивные возможности, то, как она может умно решать задачи. На эту же чашу накладывается, чем мы готовы делиться: например, некоторыми данными, которые через тулы LLM-провайдер может вытащить. Если мы даём рутовый доступ к серваку, SSH-ключи он может достать, если захочет; может какие-то другие ключи скрепить и послать себе на сервер. То есть безопасность здесь между нами, нашим сервером и LLM-провайдером отсутствует — мы доверяем друг другу, все трое, в этот момент. Тут ещё вопрос доверия перед нами встаёт.

[35:27] Александр: То есть безопасность, когнитивная нагрузка на модель — вывезет или не вывезет, — это на одной чаше весов, а на другой контроль. Если мы даём больше тулов, то мы как люди, как те, кто ставит задачу, как стейкхолдеры, больше понимаем, что модель делает, что ей можно, что нельзя, гаечки потуже затягиваем. Возможно, слабая модель в таких условиях лучше справится, потому что ей думать меньше надо: вот у неё пять ручек, дёргай одну из пяти, не знаешь — скажи «не знаю». С другой стороны, мы жертвуем своим ресурсом: мы теперь должны контролировать. А контроль, вообще говоря, не очень хорошая штука. Люди почему-то думают, что контролировать — это круто: «я хочу контроль». Чувак, ну поверь мне, ты не хочешь контроль, ты хочешь ощущение безопасности. Вот что ты хочешь. А контроль — это твой способ к этому ощущению приблизиться, как будто ты понимаешь, что происходит. Но на самом деле, во-первых, скорее всего, нет, а во-вторых, всё, что ты хочешь, — это решённая задача. С точки зрения эффективности ты хочешь потратить минимум времени и денег и получить максимально качественный результат. Это функция, которую мы оптимизируем как инженеры, а всё остальное — наши, как людей, страхи, между которыми мы пытаемся балансировать. Очень сложная штука. Мне интересно в этом времени ковыряться — что вот тут и наша внутренняя оптимизация, и всё, что мы хотим: инжиниринг и что-то новое.

[36:58] Михаил: Всё вместе, прикольно. Но я вот так скажу: я большинству своих агентов даю тот самый полный доступ, потому что я понимаю все эти риски, я их продумал. У меня все агенты бегут на изолированных инвайроментах, в которых нет ничего, кроме того, что я и сам могу отдать в интернет, условно, грепать. Господи, пожалуйста, — 10-долларовый лимитированный OpenAI-ключ. Я не обеднею, если кто-то его увидит и заиспользует: там лимиты стоят. Короче, я в этом плане очень хорошо продумал. И с точки зрения достижения результата это самая офигенная стратегия. Фронтир-модели типа GPT-5.5, GPT-5.5 Pro, Opus 4.7 — им сам Бог велел: дай ты ему доступ, и он всё сделает. Он настолько умный, что ты даже не догадаешься, что он там сделает на сервере, — в плане, как он это сделает.

[37:54] Михаил: У меня как-то был момент, когда я потерял доступ по SSH к своей VM-ке. Просто в клауде у меня было несколько VM-ок. Я что-то сделал в веб-морде, короче, сам лично убрал — я дурак. И пишу Клоду: блин, вот тут был доступ, ты же ходил на эту VM-ку, проверь, что-то доступа нет. Он идёт, такой: действительно, нет доступа к VM-ке, похоже, ты на уровне сети просто обрезал трафик. Я: ну, похоже, да. Он: что делать будем? Дай-ка я посмотрю, что тут можно сделать. Разобрался, нашёл обходной путь, восстановил доступ: перезагрузил, переписал routing, вернулся и говорит — ну всё, у тебя есть доступ. Я такой: господи, ну ты крут. И это произошло почему? Потому что у него был доступ. Но это степень ответственности, которую я на себя беру, когда предоставляю такое.

[38:55] Александр: Но мы говорим, что если мы пишем своего агента, то всё-таки не готовы читерить и говорить: пользуйтесь только фронтир-моделями и давайте полный доступ, и всё будет хорошо. Мы говорим: давайте мы Qwen’ом, стареньким Sonnet’ом, вот такими простенькими моделями хотим, чтобы всё работало. Поэтому мы дизайним тулы аккуратно — это то, что мы делаем. То есть, давая универсальный доступ, мы даём бесконечное количество возможностей. Но давайте сразу проговорим…

[39:25] Михаил: В целом, такой бесконечный доступ действительно для каких-нибудь моделек посильнее достаточен. Но что, если мы совместим оба подхода? Если выделим тулы, которыми нужно часто пользоваться, а терминал оставим для крайнего случая, для дополнительных возможностей, которых может не хватать остальным тулам. В таком случае мы получаем плюсы обоих подходов. Если вдруг агенту не хватит наших тулов, он всегда может запустить терминал и получить ответ. И тем не менее у него есть расписанные, задизайненные, хорошие тулы, которые умеют читать файл, искать, получать список файлов из директории, записывать файл. И он уже будет ими пользоваться без ошибок, без проблем.

[40:15] Александр: Кстати, знаешь, чем ещё лучше предоставить такие тулы? С точки зрения оптимизации костов, которые мы тратим на взаимодействие с моделью. Что мы оптимизируем? Количество токенов, которые гоняются туда-сюда. Ведь представьте: вот у тебя есть бесконечный доступ ко всему, но ты же ничего не помнишь. Каждый раз для тебя сессия новая. Если ты не позаботился о том, чтобы предоставить документацию к тем командам, которые есть — а, скорее всего, нет, — ты просто говоришь: вот на SSH там что-то лежит, упало, подними. И агент каждый раз в этой банке новый, он начинает изучать environment, в котором находится. Если вы посмотрите, как ведёт себя Claude, когда ему подобную задачу с Opus ставишь, он начинает смотреть: идёт в /usr/bin, смотрит, какие бинари установлены, изучает environment, потом тулы, доступ по сети, смотрит, установлена ли Java, если мы про Java, установлен ли Gradle. Это всё токены, которые просто сожгутся на то, чтобы каждый раз заново что-то изучить. Вот это самое тупое. Я раньше злился, сейчас уже нормально отношусь — к майнингу биткоина, думаю, — ну, мы просто греем планету. Вот когда агент занимается тем, что я описал, мы сейчас греем планету просто так.

[41:36] Александр: Это следующий next level. Мы могли бы просто дать ему нормальное описание environment, и я думаю, мы к этому придём довольно быстро: у нас будут операционные системы, агентно-ориентированные. Точнее, это будет тот же Linux, просто agent-specific, где есть одна команда, которая возвращает ему всё, что нужно знать про систему, чтобы дальше эффективно работать. Может, такие уже есть — что-то я в последнее время не смотрел. По факту мы оптимизируем токены, и ладно токены — погрели там сервера где-то в Америке, что нам с этого? Мы время оптимизируем. Он просто быстрее будет решать задачу, если у него качественный tooling с качественным описанием: он не будет тратить время на этот research. Если вы работали с кодом, вы знаете, что задачи решаются не мгновенно. Хотелось бы: Enter нажал, 2 секунды — результат. Токены-то машина генерирует быстро. Но на изучение environment уходит большая часть времени. Поэтому мы оптимизируем время и токены. Так что даже с фронтир-моделями адекватный tooling имеет смысл.

[42:44] Михаил: При этом, немножко отходя в сторону, скажу: мы довольно много взаимодействуем с фронтир-моделями, и они тоже ошибаются. Причём степень вероятности их ошибки напрямую зависит от их свободы. Чем больше мы им даём пространства для возможностей, тем больше это пространство является также пространством для ошибок: где-то можно написать не так, символ забыть, аргумент не тот — и всё пойдёт наперекосяк. Но, тем не менее, терминал мы убирать не будем. Считаем, что мы его добавили в тулы нашего агента, но в описании написали: смотри, это терминал, будь очень осторожен, пытайся сделать в нём только то, чего тебе не хватает из остальных инструментов. Таким образом мы приоритизировали более простые, контролируемые вещи для частых действий. Но всё ещё не закрыли самые частые действия, которые нужно делать в кодинг-агентах. Мы заранее немножко говорили про билд-систему, про компиляцию, про то, что тесты нужно уметь запускать. Сейчас как раз к этому и перейдём. Но, опять же, надо помнить: чем больше тулов, тем больше мы перегружаем агента, поэтому нужно держать джентльменский набор, который мы сейчас составляем.

[44:00] Михаил: Вспомним, что на данный момент умеет агент: читать, смотреть в директорию (ls), искать, записывать или создавать файлы. Плюс терминал. Итак, нам нужен в каком-то виде билд. Чтобы сделать этот инструмент, надо продумать, какие у него будут аргументы и будут ли вообще. Можем в самом простом виде завести инструмент билда с ровно нулём аргументов: он запускается в корне проекта, в который мы агента впустили, и выполняет захардкоженную команду, что-нибудь типа gradle -v assemble. Весь его код в этом и состоит. Но тут вы скажете: ну какой gradle? А если у нас не Java? А если .NET или Python? Действительно. И здесь появляется потребность, чтобы language-specific вещи, такие как билд, корректно работали в агенте.

[45:13] Михаил: Здесь у нас есть два пути. Мы либо предоставляем агенту информацию, на каком языке пишется проект — а когда про тесты начнём говорить, ещё веселее: какой там фреймворк для тестирования, чтобы правильно команду подбирать, — и под всё это будут отдельные тулы. Это вообще никуда не годится. Потому что а что, если агент ошибётся? А что, если у нас проект с несколькими языками — каждую часть нужно билдить отдельно, а тулы работают только для корня? Всё сложно, проблемно. У нас, по сути, та же развилка, тот же трейд-офф. Либо сказать: хочешь сбилдить — используй терминал. Либо второй вариант — использовать какую-то инфраструктуру, которая уже знает о вашем проекте, в которой уже есть опишка для билда, которая сама понимает язык. У неё есть готовые инструментарии, которые можно просто подключить. И как вы думаете, что это за инфраструктура? Как думаешь, Саш?

[46:33] Александр: Так, я пока аккуратно отложу ответ на этот вопрос, потому что у меня есть много мыслей на этот счёт. Когда ты начал описывать проблематику, я же как инженер мыслю: блин, а как реально решать? Вот если я мозгу в банке отправляю постоянно огромный набор тулов и говорю «пофикси мне багу», и я, допустим, хочу агента, который работает, например, с пятью языками. Я как бэкендер люблю Rust, обожаю — там точно будет Rust; будет Java, потому что куда от неё пока денешься, Java — это хлеб; будет Go как лучший язык для программирования с LLM; TypeScript тоже — язык, который просто надо писать; и Python для автоматизации. Вот мой джентльменский набор, я хочу, чтобы агент с ним работал. И что, я должен предоставить ему тулы для работы со всеми этими языками? Вопрос: когда я прихожу в Java-проект и говорю «пофикси багу», он её фиксит — я ему набор тулов про Go, Rust, TypeScript и Python даю на вход или не даю? Если даю, но решаю проблему в Java-проекте, я просто засоряю контекст мусором.

[47:49] Александр: Если мы думаем, что контекст — это самое важное и его нужно оптимально строить, то я как инженер, который создал кодинг-агент, при работе с Java-проектом всегда имеющий на входе набор тулов TypeScript, — я провалился как инженер. Ну всё, вон из профессии, как говорит Умпутун. И тут: блин, так нельзя. Надо как-то иерархично, древовидно строить набор тулов и в зависимости от проекта предоставлять проектоспецифичный набор. Но здесь стоит другой вопрос. Слушай, а ты уверен, что способен написать такую умную иерархию, чтобы любой проект — который, кстати, может быть мультиязычным — работал? Когда у тебя несколько языков в монорепе, как ты будешь эту иерархию строить? Ну удачи тебе решить эту задачу. Это вообще сложная задача. И я сразу такой: блин, а что делать реально? Ну, думаешь: всегда есть терминал. Если что не получится — он с терминалом: увидит, что там есть Go, пойдёт в нужный директорий. То есть всегда есть соблазн, ещё одно измерение — просто отдать всё агенту наотмашь: если не справился, сказать «ну, короче, не получилось». Интересная задача. И ты подвёл к вопросу: а как бы нам найти такой туллинг — может, он уже существует, — который предоставляет некоторый слой абстракции от языкоспецифичного туллинга в виде билда?

[49:15] Александр: Ведь, по сути, если посмотреть на языки программирования — и это, мне кажется, интересное наблюдение, — я сейчас понимаю, что нам, как инженерам, мне уже не надо знать синтаксис Go или как работает borrow checker в Rust, чтобы исправить конкретную проблему с borrow checker’ом, потому что даже Sonnet до 4.5 проблемы с borrow checker’ами у меня в Rust решал, хотя это вроде не самая тривиальная задача. То есть мне, как инженеру, эти знания не актуальны в современном мире. А какие знания актуальны? Что должен знать каждый инженер? Вот база инженерная. Мне кажется, жизненный цикл разработки ПО во всех языках очень сильно пересекается. В большинстве языков — ладно. Если мы особенно про компилируемые языки, то там у всех всё как под копирку. Есть компиляция — самое первое; есть форматтер, есть линтер, есть checkstyle, есть SpotBugs, аналог типа на дебаге, статический анализ; есть тесты; и есть сборка и публикация. Это такое везде, жизненный цикл программы плюс-минус одинаковый, если в детали не углубляться.

[50:28] Александр: И вот как бы нам найти такую платформу, такую абстракцию, которая предоставляет нам жизненный цикл разработки софта для большинства языков, но не углубляется в детали — что типа там gofmt, а в Java это…

[50:44] Михаил: В Java нет форматтера, сорри.

[50:47] Александр: По-больному. Ну, есть, конечно, но никто им не пользуется, скажем, там — идею попросить.

[50:53] Михаил: Да, вот.

[50:55] Александр: Ну ладно, там типа rustfmt, да. Вот где типа gofmt и rustfmt — я не хочу выбирать между этими двумя тулами, я хочу сказать fmt, просто fmt, а инфраструктура сама решит. Наверное, мы сейчас на этом пороге и находимся. И есть ли такая абстракция? Мне на самом деле интересно — что ты скажешь?

[51:10] Михаил: Ну, я думаю, что есть, и более того — большинство из нас, разработчиков, ей каждый день пользуется в том или ином виде. У кого-то это среда разработки.

[51:25] Александр: Ага, да, я сразу же раскрыл все секреты. Так, представьте, что вы всё этого не слышали.

[51:31] Михаил: Значит, это как раз инфраструктура — это и есть среда разработки, действительно. Кто-то пользуется VS Code, там тоже есть свои плагины; допустим, мы поставили плагины, которые позволяют сделать билд, и тогда мы можем поставить агентский инструмент, который будет просто называться build и сам будет разруливать всё необходимое: подтягивать нужные конфигурации, смотреть в настройки, в переменные, которые нужно настроить, чтобы билд хорошо прошёл. Всё это будет уже внутри, он это в себе инкапсулирует. С одной стороны, мы забираем немножко свободы у агента, потому что не даём ему запустить билд с какими-нибудь параметрами, которых у нас нет в проекте, — там не assemble, а что-нибудь другое. Но, с другой стороны, нужна ли ему такая свобода, которая даёт возможность ошибиться? Одна из позиций в том, что, может быть, и не нужна. Может, достаточно высокоуровневых тулов, которые в себе инкапсулируют всё необходимое и просто делают красиво.

[52:43] Михаил: Вообще, вот эта фраза — «сделай красиво» — это то, чего на самом деле хочет пользователь. Взять проект, написать в чатик «сделай красиво», и чтобы агент сам всё это сделал. Тут мы, по сути, как раз и даём эти маленькие для агента инструменты, которые просто называются build, run test, ещё что-нибудь, но которые под капотом имеют много-много всего: обрабатывают ошибки, прорабатывают разные варианты, адаптируются под настройки, которые в проекте уже сделаны. Более того, мы всё равно сидим в IDE-шках. Это мы плавно подводим к тому, а почему бы агента не посадить прямо внутрь вашей IDE, в которой вы сидите. В таком случае инструменты, которые мы описывали, можно было бы реализовать через API самой IDE. То есть чтение файла — буквально через открытие файла, через какую-нибудь виртуальную файловую систему, которая в каждой IDE-шке практически есть, и чтение из него тоже средствами IDE.

[53:47] Михаил: И таким образом мы приходим к тому: давайте в агенте, по сути, нажимать те кнопочки, на которые мы с вами обычно нажимаем. И вот тут начинается самое интересное. Агенты в IDE — это отдельная отрасль. Сейчас есть разделение на CLI-агентов, которые живут в консольке, — в частности, Claude Code, — и агентов, которые живут в вашей IDE-шке. Их вы тоже все знаете: Cursor, в частности; veai тоже живёт в вашей IDE-шке — про veai мы ещё поговорим подробнее позже. И есть ещё отдельная группа агентов, про них скажем вскользь, — автономные агенты, которые живут, по сути, тоже в консольке, но где-то на удалённом сервере и как-то автономно работают с задачами. Ну, это, например, OpenClaw, Hermes и подобные.

[54:45] Михаил: И теперь как раз перейдём к IDE-агентам. Представим, что мы взяли весь наш написанный код, встроили его как плагин для IDE-шки, которой даётся возможность вызывать API IDE, и начали переписывать все тулы, которые уже написали, через API самой IDE. Допустим, мы всё это сделали. Замечательно, но мы всё ещё не решили до конца проблему билда. В общем-то, решаем её довольно просто: берём API, которая позволяет сделать билд, и выдаём её в качестве инструмента. Но здесь все тонкости начинаются в аргументах. Что принимать? Принимать ли путь? Принимать ли какую-то конфигурацию запуска, или кастомные аргументы, или environment-переменные? Что-то нужно навернуть. И вот в этом проектировании — какие будут аргументы, какой результат, какой формат, какие возможности даны агенту — и заключается сложность проектирования современных агентов. Она именно с тулов начинается. Вот то, во что мы сегодня погружаемся.

[55:55] Александр: Отлично. Значит, теперь давай к чему перейдём — я немножко затупил. Смотри: из трёх видов агентов, которых ты описал, мы откидываем из нашего поля автономных агентов, потому что мы про них пока не говорим — может, в конце затронем. Мы не говорим про CLI-агентов: OpenClaw… то есть Claude Code, Codex — я их бесконечно могу называть. Пока тоже откинули. И концентрируемся на агентах, которые живут в IDE. В частности, например, берём IntelliJ IDEA и подключаем плагинчик в виде агента. Вот одного из таких, veai, разрабатываешь ты — собственно, поэтому я тебя и позвал на подкаст, потому что интересно послушать, как это всё разрабатывается и какие там вызовы. И вот сейчас в разрезе этого агента думаем: перед нами есть некоторые тулы. Во-первых, хорошо бы пройтись по тем ручкам — не тулам с точки зрения агента, а вот таким просто ручкам, — какие есть в IntelliJ IDEA-платформе или в другой IDE плюс-минус, из которых мы могли бы собрать тул и предоставить агенту. Пришли мы в этот мир из дискуссии про то, что хотелось бы немножко не быть языкоспецифичным, а быть таким language-agnostic: когда мы можем сказать «запусти тест», «запусти форматтер, поправь ошибки форматирования». Это к языку особо отношения не имеет. То есть мы не говорим «возьми output rustfmt, распарси, найди rule violations и пофикси в каждом файле через rust fix, потом вызови заново» — это слишком специфично. Мы хотим сказать «вызови и пофикси форматтер». Я не разрабатывал подобного агента конкретно в IntelliJ IDEA, поэтому фантазирую — а ты можешь мои фантазии скорректировать с реальностью.

[57:54] Михаил: Я бы здесь, перед тем как окунаться в область IntelliJ IDEA, вспомнил: у нас же в какой-то момент вставал выбор, как у разработчиков агента, которого мы сейчас кодим, — а какую IDE-шку вообще использовать? Их же много разных. Есть VS Code, есть JetBrains — точнее, JetBrains IDE и их API. Какие ещё IDE-шки знаешь, Саш?

[58:41] Александр: Так, ну именно если мы говорим про Integrated Development Environment, про эти сложные штуки, мне вспоминается Visual Studio — не Code, а просто Visual Studio, — для программирования на C# и F#, вот такая монструозная штука.

[58:56] Александр: Да, без Code. Вот это, господи, лучше нейминга не придумать: Visual Studio и Visual Studio Code. Ну, человек, который это сделал, — это фейл полный. Ну ладно. VS Code — то есть оригинальная, где вот на C#. Потом ещё Delphi — как же она называлась, по-моему, это называется Delphi, где ты открываешь, — мы в универе тоже это проходили, такая IDE-шка. Но если говорить всерьёз, то это, конечно, IntelliJ IDEA и VS Code — самый ближайший. А остальное я бы называл всё-таки редакторами кода плюс некоторые лёгкие интеграции типа LSP.

[59:30] Михаил: Отлично. И когда встаёт такой выбор, от чего вообще стоит отталкиваться, выбирая IDE? Можно отталкиваться как минимум от спроса на неё — зачем нам агент, который сидит в супер-древней среде разработки, на которую никто не кодит. А с другой стороны, хочется отталкиваться от мощности самой IDE, в которую хочется проинтегрироваться, потому что мы идём в эту IDE не для того, чтобы там просто чатик показывать — это можно и отдельно сделать, сильно проще. Мы идём туда, чтобы бесплатно получить некоторые инструменты, фактически готовые, которые можно отдать агенту, и знать, что они достаточно качественные, что ими можно пользоваться, они не будут баговать, агент не сойдёт с ума, и в целом они хорошо спроектированы. И здесь действительно как VS Code, так и JetBrains IDE в целом имеют место.

[1:00:28] Михаил: И главное отличие между этими IDE, которое сразу хочется подметить, — в том, что в VS Code парадигма построена на базе плагинов, которые вы ставите внутрь. Как только вы её скачали из коробки, она очень легковесная и очень быстрая, приятная. И вот мы поставили плагины для разработки на Java — допустим, мы такой типичный full-stack-разработчик: у нас backend на Java и frontend на TS. Поставили соответствующие плагины, у нас уже есть в каком-то виде build, запуск тестов. Но вот тут самое интересное начинается в том, как это работает под капотом. А что значит, что наша среда разработки, VS Code, поддерживает язык? Что она поддерживает Java, что вообще идёт внутри плагина, который мы ставим? И здесь в зависимости от IDE поставляются разные вещи. В случае с VS Code и плагином для Java, например, это какой-нибудь LSP-сервер Java. И тут нужно коротко остановиться на том, что такое LSP-сервер. В общем-то, это некоторая ручка, которая позволяет IDE-шке понимать, есть ли у нас тут ошибки компиляции, заранее их подсвечивать, понимать некоторое AST, знать, что вот есть метод, что он не закончен. Обозначим как-то так LSP-серверы: по сути, добавляем в редактор кода поддержку некоторого языка — информацию про то, какие там элементы есть в виде AST, и проверки ошибок компиляции и различных ворнингов. Всё это уже дополнительно, по сути.

[1:02:24] Александр: Ну да, я бы здесь, кому не совсем понятно про LSP, такую ментальную модель подстроил. Представьте, вы просто редактор кода Vim, самый простой. Открываете Vim — даже на сервере vi. Любой файл, который вы откроете для чтения, для него просто текст. Просто набор байтов, который он интерпретирует как строку и вам отображает: набор байтов конвертирует, пробельчики, всё это закодировано. И для этого редактора кода он видит текстовый файл как блоб, позицию курсора, и надо ли что-то вставить в конкретную позицию — набор байтов или нет. Вот, по сути, всё, что делает vi с точки зрения такой ментальной модели. Там нет подсветки синтаксиса, потому что там нет синтаксиса — его не существует, это просто текстовый файл.

[1:03:18] Александр: Так вот, когда мы говорим, что наш язык программирования и наш проект — это не просто набор текстовых файлов, а набор файлов, написанных на языке программирования, а язык программирования — это набор некоторых конвенций вокруг того, какой текст мы пишем, который обрамлён синтаксисом. Мы приносим синтаксис в текст с помощью LSP. Мы говорим редактору кода: смотри, у этого файла есть синтаксис, а это значит — мы парсим. Синтаксис — это лексер плюс парсер. Когда мы распарсили, представили это AST, Abstract Syntax Tree, — в момент, когда у нас это дерево появляется как структура данных в представлении, мы говорим, что принесли в текстовый файл синтаксис. Этим парсингом, представлением и синхронизированием — мы же не AST изменяем напрямую как разработчики, а меняем файл, и это AST должно перестраиваться — вот всё это, поддержка и перестроение синтаксиса, предоставляется LSP-сервером в виде API. Теперь редактор кода может просто запросить эту структуру данных и как-то с ней работать. Например, подсвечивать синтаксис, потому что в этом AST у каждого токена, например for или if, есть атрибуты — он будет снабжён метаинформацией в дереве, что это ключевое слово языка. А ключевые слова редактор кода подсвечивает, например, жёлтеньким цветом в Darcula-теме. И это подсвечивание происходит именно потому, что у него есть эта структура данных в памяти. Так вот, LSP подаёт нам подсветку синтаксиса, если простыми словами.

[1:04:53] Александр: Он даёт, например, возможность некоторых ситуативных автокомплитов, потому что мы можем догадаться, что вот на этой позиции — если говорить с точки зрения парсинга, этих грамматик, контекстно-свободных грамматик, в них есть контекст, — и когда ты знаешь контекст, а человек начинает печатать: например, пустой файл SQL. Я не раз писал SQL-парсеры, знаю, как они работают. Берёшь SQL-файл, открываешь, расширение .sql. Мы понимаем, что нужно подключить SQL Language Server. Подключаем, я начинаю печатать S, E — и LSP-сервер может предоставить продолжение, потому что знает, что любой SQL-файл может начинаться с пяти ключевых слов: select, drop, create — вот условно. Это контекст, который он знает, исходя из синтаксиса языка, на который настроен, и он даёт автокомплит: select. Дальше, если это умный автокомплит, он может и имена колонок подсвечивать, но тогда ему нужен доступ к таблицам, к базе данных непосредственно — если мы хотим имена колонок, схему таблиц. Там может быть разное, но да, нам даётся автокомплит. Даются простенькие рефакторинги, потому что у кода теперь есть структура. Это не просто текстовый файл, где есть функция и вызов этой функции. Это два файла, написанные на одном языке, работающие в общем контексте: они компилируются вместе, один импортируется в другой, — и это всё известно, потому что есть контекст у LSP-сервера. Поэтому, когда ты делаешь рефакторинг по ренейму, например, имени функции, то в другом месте, где она вызывается, LSP-сервер тоже поменяет файл. Это уже не синтаксис, кстати, это больше про логическую структуру. В двух местах меняется — рефакторинги у нас появляются.

[1:06:52] Александр: В общем, какие-то такие штуки нам предоставляет LSP-сервер. Но стоит понимать, что избалованные рефакторинги Java-программиста в IntelliJ IDEA довольно сильно обломаются, если он начнёт использовать рефакторинги в опенсорсных LSP-серверах, потому что это огромное количество эвристик и огромное количество человеческого капитала, вложенного JetBrains в то, чтобы эти рефакторинги работали. Это непросто, это очень сложно. Поэтому они есть, но в ограниченном количестве. Если ренейм ещё как-то можно сделать в одном файле или в нескольких, то какие-то сложные — уже непонятно.

[1:07:29] Михаил: Да даже ренейм на самом деле не самый тривиальный, я вот сейчас думаю. А вот если ты в Javadoc’е ссылаешься на переименованное — то, наверное, да. А если у тебя тест написан, то там уже сложность. Поэтому да, есть, но работает so-so, я так думаю.

[1:07:45] Александр: По опыту LSP, кроме как для подсветки синтаксиса и самого простого автокомплита, ни на что не годится. По опыту — ну, я просто виммер, я программирую всё в Vim и использую command-line тулы. Я просто перестал надеяться на LSP как на что-то, чему я доверяю в любом языке. Я доверяю компилятору, сборщику и командной строке. И весь input-output, который я получаю о том, что написал, я беру оттуда и иду фиксить, вместо того чтобы видеть какие-то красные подсветки синтаксиса в редакторах кода, потому что это работает слабо. Мы, короче, здесь находимся. Что у нас дальше?

[1:08:28] Михаил: Да, как только мы заговорили про LSP и выяснили, что они нам дают, давайте заглянем: а что ещё бывает, что может быть лучше? Мы как раз про рефакторинг говорили, про избалованность IntelliJ. А почему в IntelliJ всё вот так хорошо, такие возможности с рефакторингом и всем этим? Причина в том, что в IntelliJ-платформе есть уже не совсем AST, а такая штука, как PSI — Program Structure Interface. Это более углублённая вещь, которая помимо синтаксиса привносит ещё и, если можно так назвать, семантику. Что имеется в виду? Например, PSI знает, что у вас есть функция Calculate, что она используется вот в том, том и том файлах. А также она переопределяется вот в таком классе, который наследуется от текущего. И это разное: там, где она используется, — это usage, использование, а там, где переопределяется, — это уже override. Таким образом PSI эти вещи разделяет. PSI просто более выразительный, чем AST, он больше информации поставляет. И, в частности, даёт API с этими PSI-элементами.

[1:09:56] Михаил: И вот тут самое хорошее место, чтобы посмотреть, что из себя представляет PSI. Вспомним, как AST выглядит. Допустим, у нас есть первая нода в абстрактном синтаксическом дереве, самая верхняя, которую назовём «файл». В файле есть ребёнок — это класс, написанный в этом файле. Представим что-то очень простое: класс A, у которого есть поля X, Y, Z и какой-нибудь метод M. Тогда наше дерево из файла идёт в элемент класса. У класса будет несколько детей: поля X, Y, Z и метод M. У полей X, Y, Z будет информация о том, что там какой-то тип лежит — какой именно, синтаксически непонятно, но мы его видим. Это AST. В методе будет отдельно имя метода, отдельно аргументы как дети этой ноды, и отдельно нода с блоком, которая называется типа code-block. Кстати, как раз вспомнил среду разработки Code::Blocks для Паскаля. Вернёмся. В этом код-блоке тоже будут дети — инструкции, которые в нём располагаются, в каком-то порядке. То есть мы просто разворачиваем весь код в файле на отдельные элементы, разбираем его.

[1:11:33] Михаил: Так вот, PSI будет, во-первых, всё, что мы описали, — это тоже будет PSI: он тоже, по сути, дерево, есть нода класса, нода полей, нода методов. Но сама информация, которую эта штука поставляет, чуть более подробная. Теперь у нод, которые являются полями, будет отдельный ребёнок, в котором будет уже тип. Написано, что это integer, что это числовой тип, value-тип — мы можем спросить на этой ноде и получить true. Таким образом мы получаем отсюда больше информации. И более того, помимо более подробного раскладывания информации, у нас есть API, которые мы можем дёрнуть. Например, у метода можем дёрнуть findUsages и сразу посмотреть на все файлы, в которых эти usages существуют. Или дёрнуть «найди мне переопределение этого метода» и тоже их найти. И вот это как раз преимущество PSI над AST: просто больше информации, больше возможностей, больше API можно вызвать. И это идеальная инфраструктура для агента.

[1:12:55] Михаил: Потому что теперь, если агенту нужно, например, найти использование метода, чтобы провести систематический рефакторинг во всём проекте, ему не нужно грепать в терминале. До этого в нашем агенте он никак иначе, кроме как через синтаксический поиск либо через греп в терминале, это не мог. Теперь мы даём ему в явном виде инструмент findUsages, который принимает имя метода. Так как мы инструменты теперь пишем поверх IntelliJ-платформы, мы уже сможем дёргать её API, находить эти элементы PSI, находить нужный метод по названию, просто посмотрев на все элементы PSI. На самом деле там очень эффективный поиск по PSI, и мы можем буквально этот API дёрнуть, результат как-то обработать — а может, и нет, просто отдать агенту. И таким образом он получит более точную информацию, чем если бы сделал просто синтаксический поиск. Как минимум будут выфильтрованы все переопределения, все элементы чисто из документации, где мы это описываем.

[1:14:13] Александр: Пауза. Здесь прям вопрос. Во-первых, когда ты начал рассказывать про PSI, я вспомнил ламповый выпуск — по-моему, два с половиной года назад записанный — с разработчиком IntelliJ IDEA-платформы, Даней Овчинниковым. Поэтому тем, кому интересно прямо осознать, — мы там лексили файл, потом его парсили, потом по нему всякие методы применялись. Я после этого выпуска понял, что такое PSI, так что слушайте. Заходите, IntelliJ — про редакторы кода мы тут говорить любим. Это первое. А второе — у меня вопрос как у инженера. Если мы говорим про PSI, и ты упомянул слово «класс», я сразу такой: опа, а вот, например, в Go и Rust классов нет — и что ж тогда?

[1:15:06] Михаил: Да, здесь всё интересно. Давайте расскажу подробнее, как IntelliJ PSI устроен. Есть некоторый набор базовых вещей — например, «тип», «метод», «поле» — и есть более специализированные типы внутри PSI, которые уже для конкретных языков существуют. Например, в Java будет класс, который наследуется от типа; в каком-нибудь .NET будет структура, которая тоже наследуется от типа, и класс тоже наследуется от типа. То есть здесь хороший момент: PSI на самом деле языкоспецифичен. Элементы в нём будут отличаться в зависимости от языка.

[1:15:56] Александр: Так и как же тогда оригинальная проблема, от которой я размышлял и фантазировал, к которой мы пришли, — что мы хотим предоставить универсальный тул агенту? У нас есть некоторая степень абстракции, на которую мы чуть-чуть поднимаемся выше, но всё ещё остаёмся языкоспецифичными.

[1:16:18] Михаил: На самом деле здесь мы внутри своего агента, которого проектируем, можем у самой платформы спросить, на каком языке написан этот проект. И она буквально через поле Language скажет, что это, например, Java. Мы такие: супер, берём набор джавовых инструментов, которые умеют делать джавовые штуки, и отдаём их агенту. А если язык не Java, то эти джавовые инструменты отдавать не нужно — лучше, например, для Python отдать питоновые, чтобы не засорять контекст.

[1:16:52] Александр: Окей, и платформа нормально ответит на вопрос, например, когда у нас монорепа для фронтенда и бэкенда, она нам вернёт два языка, получается?

[1:17:04] Михаил: Да. Ну, на самом деле, здесь я немножко упрощаю. В IntelliJ-платформе есть такая штука, как плагины внутри. Все JetBrains IDE строятся на базе некоего самого стандартного плагина, который называется IntelliJ IDEA Community. Вы прямо это в коде можете увидеть. И дальше, чтобы добавить эти PSI, эти возможности платформы, в разных IDE подключаются отдельные плагины: например, в Rider подключаются плагины, которые эти возможности дают, в Python — свой Python-плагин, а в каком-нибудь WebStorm это тоже просто надстроечка над IntelliJ IDEA Community со своим плагином. И нам при проектировании агента, когда мы делаем это поверх платформы, можно просто в конфигурации плагина, в специальной XML-ке, указать: если у нас есть плагин Python в запущенной IDE-шке — и этого, на самом деле, даже достаточно, — то уже можно включать функциональность агента, отвечающую за Python.

[1:18:19] Михаил: Но вы здесь можете возразить: как, у нас же может стоять плагин, но проект при этом может не содержать Python — мы просто взяли и понаставили плагинов. Это действительно так. Поэтому в этом тоже сложность агента: такой явной опишки, которая вернёт конкретно язык, на самом деле нет. И список тем более не вернёт. Поэтому чаще всего люди делают так: пишут высокоуровневые тулы для агента, которые инкапсулируют внутри себя задачу инструмента. Например, просто build, вне зависимости от языка. И уже инструмент внутри себя эти ситуации обрабатывает: смотрит, что вот запустился build, но если мы дёрнем ручку, которая запускает build для Java, она скажет, что такой конфигурации нет и делать нечего. И мы это просто не будем отдавать агенту, попробуем следующий язык. Пройдёмся по всем включённым плагинам, по этим языкам, попробуем применить инструмент. Как-то так строится. Либо как альтернативный вариант — уходить в эвристики и писать свои инструменты для определения языка проекта, но тут можно наделать ошибок.

[1:19:48] Александр: Понятно, окей. Работает, ясно. Есть ли какой-то, может, набор, не знаю, пять тулов при работе через IntelliJ IDEA-платформу для агента, которые просто работают восхитительно, и аналогов в природе не существует?

[1:20:03] Михаил: Хороший вопрос. Я бы сказал, что, во-первых, это точно инструмент, который позволяет запускать инспекции самой IDE. Инспекция IDE — это то, что запускается, как только вы перестали набирать текст, автоматически, в зависимости от настроек. У вас справа сверху в файлике в IntelliJ есть такая пометочка с проблемами: там ошибки компиляции, если они есть, подсвечиваются; если нет — ворнинги. Вы можете на них нажать, и будет окошечко с проблемами. А что если мы такой тул запуска инспекций отдадим агенту? Тогда мы начнём получать, помимо ошибок компиляции, ещё и результаты анализов самой IDE, которые она выполняет под капотом. Туда включены различные статические анализы, которые позволяют некорректное поведение заранее находить. Давайте самый простой возьмём: если вы принимаете ссылку, которая не nullable, и делаете проверку на null, он говорит, что эта проверка бесполезна — она никогда в true не отработает, ссылка не nullable. И для агента, который пишет код, это может быть очень полезно, потому что он получит более подробное исследование написанного кода и сразу будет отталкиваться от проблем, чтобы их править до того, как они проявятся, например, в рантайме — что было бы менее приятно, сложнее тестировать.

[1:21:44] Александр: Да, inspect — хороший пример. Это одна из интеллектуальных ценностей, которая в JetBrains IDE присутствует и которой не хватает частенько при работе без неё, потому что инспекции бывают действительно полезными. И здесь есть небольшая история про IntelliJ IDEA Inspect, я про это, по-моему, не говорил. Я уже давно на Claude Code программирую, больше года, full-time. И вы можете представить, какой код был год назад. В какой-то момент я начал весь свой код, который из-под меня уходит из Claude Code, и перестал вообще открывать IDE — за ненадобностью. Но пришёл с этим сайд-эффект. Какой? Открываю pull request, ревьюлю, смысл мне понятен, я согласен, тесты есть, я их позапускал, с агентом початился. Меня устраивает этот код, я прошу коллегу сделать код-ревью. А коллега довольно педантичный, он взял и открыл мой pull request у себя в IDE. И говорит: слушай, я твой pull request открыл, у меня инспекции просто десятки горят, всё жёлтым подсвечивается — вот эта ёлка в твоём pull request. Ты что, вообще не смотришь в IDE, что ли? Я говорю: я не смотрю в IDE, но это хороший момент. Я начал понимать: а, вот эти подсветки жёлтеньким — это как раз результат работы IntelliJ Inspect, это инспекция. Коллега их увидел, за что ему огромное спасибо, а я, работая без IntelliJ IDEA, их не увидел, и, соответственно, Claude Code, который писал код, тоже их не увидел — откуда ему про них знать?

[1:23:28] Александр: И я тогда задался вопросом: а как мне, как инженеру, эту проблему решить? Естественно, я не собираюсь открывать IDE и смотреть свой код — я не для этого инвестировал в Claude Code. Я думаю, как системно решить. Надо Claude дать input, точнее output этой тулы, IntelliJ IDEA Inspect. Вот что я начал решать. На тот момент был, и сейчас, думаю, есть, MCP-сервер, но мне сразу идея MCP не понравилась: я не собираюсь поднимать редактор, это слишком тяжёлая штука для меня, и она ещё и не завелась с двух попыток — я искренне пытался. Сейчас, наверное, проще с Opus, он разберётся. Но Sonnet тогда не разобрался, и я решил, что не хочу. Я погуглил, поресёрчил и обнаружил: оказывается, есть CLI-тула, называется idea inspect, ты передаёшь путь к директории, и она запускает инспект и в виде XML-файла возвращает результат. Я такой: о, ну всё, я нашёл. Запустил просто «давай, запускай». И оно всё повисло, потому что когда много файлов — эта CLI-команда не предназначена для работы в целом. Но если уж ты сильно хочешь, то должен давать узкий путь: поменял файл — запускай inspect на этом файле или модуле. Не надо по всему проекту гонять инспект, потому что он тупой — будет гонять по всему проекту, и ты не дождёшься. На большом проекте.

[1:24:49] Александр: Я настроил Claude так, чтобы он запускал idea inspect именно на тех файлах, на которых делает изменения, и эти инспекции правил, прежде чем пушить и завершать Software Developer Lifecycle. Таким способом я решил эту проблему. Но справедливости ради, это работает только у меня на машине: у других IntelliJ IDEA может лежать по другому пути, и вообще как её вызвать. А самое интересное — что для того, чтобы запустить IntelliJ IDEA Inspect, вы никогда не догадаетесь, что надо сделать. Я даже думаю, что ты, Миш, не знаешь. Надо закрыть IntelliJ IDEA. Потому что с открытым процессом IntelliJ IDEA у тебя инспект не отработает. Когда я это узнал, я просто такой: ну явно, что это не основной юзкейс.

[1:25:37] Михаил: Не тестировали, скажем так.

[1:25:39] Александр: Но, в общем, да, это интересная история слушателям про инспект. Возвращаясь к тулам, которые предоставляет платформа, точнее к ручкам, возможностям, — помимо инспекта, что ещё есть такое уникальное?

[1:25:52] Михаил: Да, возвращаясь к нашему вопросу, хотел ещё один маленький вопрос тебе задать. Подскажи, а ты когда эти инспекции включил в Claude, как ты просил его так гранулярно вызывать их на тех файлах, на которых нужно? Ты какой-то скилл подключил или как это было устроено? То есть как Claude звал эти инспекции?

[1:26:17] Александр: В тот момент скиллов ещё не было, и я это добавил как строчку в CLAUDE.md — ну, точнее, Claude сам добавил несколько строчек в CLAUDE.md: «перед каждым коммитом запускай idea inspect и типа module-name», где module-name — это модуль, который ты изменяешь. Примитивно так сделал. На самом деле сейчас мне надо вернуться к этому и посмотреть, как оно работает, потому что, думаю, оно требует улучшений: появились скиллы, и я понимаю контекст сейчас иначе. Но тогда это была просто строчка в CLAUDE.md.

[1:26:50] Михаил: Понял, понял. Ну, на самом деле это уже решало проблему, и это самое главное. Но вот тут интересный вопрос. У нас есть эти инспекции, про которые говорили. Мы можем отдать их агенту как отдельный тул и сказать: смотри, ты можешь запускать анализ. Но тем, что мы отдаём ему абстрактный тул, который можно запустить когда угодно, мы, даже если и говорим, в каких ситуациях его запускать, всё равно заставляем агента потратить отдельные токены, отдельное размышление на то, чтобы понять, что вот это тот самый момент, когда нужно вызвать инспекции. А что если — такой тоже трейд-офф закину — мы этот инструмент инспекции включим внутрь какого-нибудь уже существующего инструмента? Допустим, эдита, то есть записи в файл. Что если инспекции автоматически будем гонять после того, как агент промодифицировал хотя бы один файл, и в качестве результата вызова тула, который модифицирует файл, будем выдавать результат инспекции? Таким образом агент очень быстро, на лету, сможет понимать, что вот он прямо здесь и сейчас допустил ошибку. А вот тут файл написал хорошо, и инспекции перезапускать не нужно. Вот такой интересный трейд-офф. На самом деле бывают инструменты, анализы, подобные интеллектуальные штучки из платформы, которые иногда хочется отдельным тулом поставить, а иногда — прямо внутрь чего-то засунуть и даже агенту про это явно не говорить, а просто сразу выдавать результат: вот, смотри, тут ты накосячил.

[1:28:33] Александр: Да, вот это интересный момент. Помимо инспекций, я бы ещё подметил, пожалуй, декомпиляцию. У меня тут есть небольшая байка. Давайте я перед ней немножко расскажу.

[1:28:47] Михаил: Значит, что есть. Если у нас ситуация, когда есть какая-нибудь библиотечка, которую мы у себя подключаем, но она не общеизвестная, мы её сами написали — какая-нибудь помогательная, утилиты с коллекциями, — но при этом на неё нет документации. И в основном проекте, в котором мы с нашим агентиком работаем, мы хотим эту библиотечку заиспользовать. Она ещё не добавлена, но мы хотим заимпортить какую-нибудь коллекцию оттуда, вызвать у неё конструктор и поработать. Что в таком случае делать агенту? У него есть знание через файловую систему, что есть какая-то библиотечка. Давайте представим, что мы всё это время говорим про Java. Документации нету. Что ему делать? Первое, что он может, — спросить нас с вами: а какую опишку поставить? На что мы ответим: не ленись, придумай что-нибудь. Что ему остаётся? Конечно, у него всегда остаётся путь через терминал. Что он может сделать? Он может взять джарник, который был добавлен как зависимость, разархивировать его, посмотреть на .class-файлы в Java, декомпильнуть их через javap и таким образом посмотреть, какой там байткод, что там было. Но тут нюанс: если я всё правильно помню, javap ведь нам выдаёт именно байткод? Это вопрос.

[1:30:21] Александр: Ничего себе ты мне вопрос задал. Сейчас, подожди. javap выдаёт class-файлы, а class-файлы — это байткод, видимо, да? Но мы сейчас погуглим.

[1:30:31] Михаил: Нет-нет. У нас прямо внутри джарника будут лежать class-файлы, а javap — это уже штучка, которая работает поверх этих class-файлов.

[1:30:43] Александр: Она просто тебе вьюху даёт поверх этих class-файлов? Или она что-то ещё делает?

[1:30:45] Михаил: Она декомпилирует. Она прямо полноценно выдаёт тебе исходники. То есть те source-файлы, точнее, .java-расширения, что было. Она пытается в обратную сторону декомпилировать class-файлы в Java-файлы, но это не те сорцы, из которых было собрано, потому что сорцов как бы нет, если ты их не скачал.

[1:31:07] Александр: Всё-таки у меня внутреннее ощущение, ну не может декомпилятор быть встроен туда. Ну вот не может. Сейчас чекну. Это дизассемблер. Дизассемблер — это то, что я вижу. Да, это оно, выдаёт байткод всё-таки. Ладно, до этого неправильно сказал.

[1:31:26] Александр: Представим, что всего этого не было. Ура! Я как разработчик Java ещё состоялся. Я сказал, что там байткод, потом подумал, ну видимо, я забыл, но мы вернулись в истину: там действительно байткод. Так вот, агент получит этот байткод, вызвав команду javap на нашем .class-файлике. И ему, чтобы понять, что находится в этой библиотечке, во-первых, нужно будет перемолотить несколько таких .class-файлов. Или, допустим, ему нужно будет понимать байткод. То есть сразу думать и на уровне Java, и на уровне байткода. Для современных моделек это не то чтобы большая проблема. Но мы всё равно даём агенту некоторое низкоуровневое представление программы, в котором есть много тонкостей. Всё-таки Java в виде её исходников выглядит сильно проще. К чему это мы всё?

[1:32:20] Михаил: Я бы ещё сказал, что вряд ли большинство моделей так хорошо обучены на байткоде. Они всё-таки обучены на source-файлах. А source-файл — это Java class. Ой, Java-файл.

[1:32:34] Михаил: А что если мы теперь где-нибудь найдём какой-нибудь инструмент декомпиляции, который, конечно, можно скачать и для терминала и тоже запустить, но, тем не менее, что если мы найдём инструмент для декомпиляции, который будет просто-напросто принимать путь до библиотечки и название какого-нибудь класса или типа, который мы хотим декомпилировать и посмотреть на его внутренности? Здесь в первую очередь речь, конечно, идёт про виртуальные машины — про JVM-языки, про .NET-языки. Вот для них этот инструмент на самом деле является незаменимым. Это я говорю сейчас, что он незаменимый. Хотя на самом деле буквально три месяца назад я был человеком, который вместе со своей командой — я руковожу несколькими командами — разрабатывал агента для Rider. То есть для .NET-среды разработки, тоже на базе IntelliJ-платформы.

[1:33:38] Михаил: И у нас была проблема в том, что Rider, если немножко под капот заглянуть, представляет собой две разных части: frontend и backend. Но не совсем в том понимании, в котором мы привыкли это слышать. Frontend-часть здесь — это часть из IntelliJ IDEA Community. То, что мы с вами видим, как отображается редактор, — это всё frontend-часть в Rider. Есть backend-часть, которая отвечает за анализы, исследования, за PSI в том числе. И эта штука родилась из инструмента и фактически является им — инструмента, который пришёл ещё раньше в Visual Studio и был для неё плагином. Это ReSharper. ReSharper — это как раз тот самый движок, который даёт возможность Rider понимать, что за код там написан, какие ошибки есть. И пишется это тоже отдельно.

[1:34:42] Михаил: Если вдруг вы захотите когда-нибудь написать агента для Rider, то знайте: это путь мучений, потому что нужно отдельно написать frontend-часть, где будет чатик, и отдельно заморочиться по работе с PSI, анализами и всей внутрянкой самой IDE-шки через backend-часть, потому что вся содержательная логика находится там. Так вот, есть ещё одна дополнительная проблема: у backend-части Rider документации, ну как бы так сказать, не особо она и есть вообще. И, конечно, есть большое количество опишек, которые местами не очень хорошо поддерживаются. Давайте я не слишком долго буду ругать JetBrains. Но скажем так: документации там особо нет. И нужно написать агента. Я помню, мы тогда взялись как раз за вопрос: а какой нам очередной тул добавить, чтобы агент работал лучше. И в тот момент у нас уже был инструмент декомпиляции для Java, и мы видели, что он даёт в целом хороший результат, но я пока ещё не проникся тем, зачем он вообще нужен и действительно ли он даёт прирост эффективности агента.

[1:35:58] Михаил: И мы начали пытаться писать его для Rider, шерстить почву, насколько легко такое написать. И поняли, что нелегко: во-первых, в целом опишку найти, которая это делает, нелегко, а на самом деле этой опишки там и нету. Там есть целая цепь опишек, которую нужно вызвать, чтобы добиться результата. Так вот, чтобы найти эту цепь опишек, нам потребовался буквально месяц, без шуток. Потребовалась куча времени, чтобы разобраться, что там вообще находится, какие API есть, всё это декомпилировать, смотреть. Было очень сложно и тяжело. И в какой-то момент у нас образовался каркас этого инструмента для декомпиляции. Он работал ещё не очень хорошо: например, не мог декомпилировать файлы, которые не открыты в редакторе. Это уже что-то говорит про опишку. Но, тем не менее, он как-то работал. И вот с этого момента всю остальную часть мы сделали где-то дня за два. Потому что мы этот же инструмент декомпиляции для себя и применили: просто взяли и с его помощью исследовали API продукта, чтобы вычленить информацию, как всё-таки эту декомпиляцию автоматически и без ошибок запускать. И она довольно быстро нашлась.

[1:37:23] Михаил: И действительно, агент работал в районе трёх часов. Очень-очень много времени работал, кучу информации перемолотил, и в итоге нашёл нужные пути. Причём сказать, что эти пути были нетривиальными, — это ничего не сказать. Это тот самый момент, где нужно сказать: если вы думаете, что всё, что нужно, чтобы инструмент создать, — это вызвать правильную опишку, то вообще нет. Там бывает всё так заковыристо, что не разобраться так просто.

[1:37:54] Александр: Ну да.

[1:37:55] Михаил: Тем самым агент очень много времени сэкономил. И вот в этот момент я действительно прочувствовал, что задача такого исследования зависимости на предмет нужных, подходящих API — особенно когда документации нет — это вообще must-have вещь в агентах. Когда вы интегрируете какую-то большую систему, где часто с чем-то взаимодействуете и где много чего является вот именно такими библиотечками. Здесь кто-то может сказать: ну ладно, допустим, документации не было на ReSharper, но везде, в нормальных-то компаниях, есть документация. Нет. Нету. Далеко не всегда даже на внутренние приватные библиотечки она есть. Поэтому довольно полезный такой тулец.

[1:38:37] Александр: Ага. Смотри, я так понимаю, что тул, который предоставляет декомпилировать, — если мы про Java, — это когда jar и внутри class-файлы декомпилируются в Java-файлы, с которыми потом агент начинает работать более качественно, лучше их изучать. По сути, мы сами тоже можем представить. Вообще говоря, мало кто догадывался — вот я, особенно когда был джуниором, мидлом, декомпиляцией особо не увлекался, точнее увлекался, но мне не до этого было, — что когда я нажимаю go to implementation метода в Java, и это библиотечный метод, я вижу какую-то имплементацию. Она откуда взялась-то? Вот ты этот код видишь, Саша, тебе его кто показал? Ты думаешь, это код автора библиотеки, она сходила на GitHub и скачала? А если это библиотека без GitHub — откуда этот код? Это на самом деле вопрос, где ты такой: а-а-а, так, вообще говоря, это IntelliJ IDEA мне декомпилировала class-файл и сделала его читаемым для меня. Так что штучка прикольная, согласен.

[1:39:43] Александр: А что ещё? Я думаю, можно сейчас поговорить про дебаггеры. Это ещё такая киллер-фича IntelliJ IDEA, которая, ну, когда я, — а я давно в Vim программирую, — но я такой: господи, ну дебажить невозможно ни в чём, кроме IntelliJ IDEA. Ребята сделали круто. И здесь мне интересно для агентов: дебаггер вы уже подключили — удаётся ли решать сложные баги с агентом или пока ещё нет?

[1:40:13] Михаил: Очень классный вопрос, очень релевантный. Буквально недавно, где-то в районе двух недель назад, у нас как раз в veai-агенте вышел релиз, в котором мы втащили дебаггер внутрь агента — буквально в таком виде, как мы его воспринимаем как люди. То есть можно запустить run-конфигурацию IDE-шки в debug-режиме, дальше поставить на паузу либо breakpoint, сделать step-over, step-in — то, что мы как люди делаем, — всё это как инструменты мы поставили внутрь агента. И это довольно интересная задачка для агента — использование дебаггера, потому что все ведь реализуют по-разному. На самом деле задача дать агенту возможность отлаживать что-либо стоит очень давно и очень остро. Кто-то решает её просто принтами — давая агенту возможность модифицировать файл, добавлять принты, смотреть в логи. Но, во-первых, самая главная, наверное, вещь, которая во всём этом в какой-то момент начинает раздражать, — это то, что если агенту потребовалось чуть больше информации прямо в процессе, когда он уже запустил приложение или тест, ему нужно заново завершить исполнение, добавить ещё один лог, заново всё скомпилировать, снова запустить, снова понять, что какого-то лога не хватает, и опять пойти его добавлять. В то время как мы с вами, когда возникают похожие проблемы, просто ставим breakpoint пониже, до него продолжаем исполнение, берём какое-нибудь выражение с переменными и вычисляем его прямо внутри дебаггера, сразу получая быстрый ответ: а как будет, если мы вот такой код напишем или вот эту информацию подтянем.

[1:42:13] Михаил: В общем, явно в таком виде мы агенту и предоставим эти инструменты. И он буквально может запустить конфигурацию, поставить breakpoint, запаузить и зарезюмить, сделать step-over, step-into и может даже буквально вычислять выражение в этом вычислителе — evaluate expression. И благодаря этому задачи отладки стали решаться сильно быстрее, в первую очередь потому, что нет необходимости эти принты перерасставлять. Но и качественнее, потому что агент может больше контекста получить из того, что вычислит выражение. Так что это инструмент, наверное, в моём рейтинге идёт сразу после декомпиляции — просто потому что у меня такая животрепещущая история была с декомпиляцией.

[1:43:03] Александр: Интересно, да. Ещё, конечно, следующее после классного дебаггера, когда ты как junior-middle-разработчик в идее для себя открываешь, — такой «вау» — remote debugger. Вот когда ты понимаешь, что можно что-то подебажить в CI, когда тест падает только там, и это, типа, можно сделать. Ты такой: ё-моё, вот это, конечно, инструмент. Понимаешь, что можно посмотреть на всё это и прям сразу поправить. Это круто.

[1:43:35] Александр: Мне кажется, мы тут очень хорошо прошлись по тому, что предоставляет платформа: и про инспекции, и про PSI, и про дебаггеры, и про декомпайлер. Я думаю, ещё специально итог наберётся подобных киллер-фичей, которые предоставляет платформа IntelliJ, чтобы использовать и интегрировать туда агента. Думаю, здесь мы хорошо поговорили, и у меня внутренний такой, знаешь, шкурный интерес немного позарубаться на тему самого продукта.

[1:44:05] Александр: То есть я как человек открытый — мои мысли никогда не являются какими-то секретами, чаще всего. И в подкасте я много про это говорю, слушатели знают меня как виммера, адепта Claude Code, человека, который большинство свободного времени пишет на Rust, в последнее время частенько на Go, потому что LLM хорошо на нём шпарит, и как человека, который тоже разрабатывает софт на Java, просто постольку-поскольку. У меня Vision на индустрию сложившийся, и я им с тобой хочу поделиться, потому что он, мне кажется, имеет отношение к продукту, над которым ты работаешь. Я не питаю иллюзий, что ты аффилирован — что ты работаешь с этим, это часть тебя, как ни крути: когда ты фулл-тайм проводишь время с проектом, ты к нему начинаешь испытывать какие-то чувства, как бы крипово это ни звучало. И ты его будешь защищать, я к этому готов. Спорить я не люблю — слушатели знают, что в споры я не вступаю, я больше, знаешь, так, поразгонять: а что если?

[1:45:17] Александр: Как я вижу, что происходит и какой карьерный свитч год назад я для себя сделал и доволен. Я полностью перешёл на разработку с CLI-агентами — теми самыми, которых мы отложили на полочку в начале, потому что хотели обсудить агентов внутри IDE. А что если мы скажем, что существуют — и это так и есть — CLI-агенты в виде Claude Code, Codex, и существуют автономные агенты в виде OpenClaw? Я обоими пользуюсь активно для разработки разных вещей: автономными агентами вроде OpenClaw в том числе. Представляешь, ты просто идёшь по городу или едешь за рулём, нажимаешь голосовое, говоришь «поправь чеки и сделай деплой на тест, прогони e2e-тест», и через 10 минут у тебя задача решена, ты делаешь merge и едешь довольный дальше. Ну это же будущее. Как можно отказать себе, как энтузиасту-разработчику, в этом возрождении. Поэтому я, конечно, простенькие задачи с OpenClaw делаю, но если говорить про что-то серьёзное, то всё ещё это Claude Code, потому что это такая махина, которую я запускаю и там решаю действительно сложные задачи: дизайню, проектирую, строю. То есть для меня этот путь — я тоже в своём роде такой байест, у меня такой путь, и я вижу только в нём. Тут я понимаю, что если освоил молоток — главное, чтобы всё вокруг гвоздями не казалось, — но в случае с Claude Code молоток привлекательный. Что не решит Claude Code? Я прям, знаешь, когда мне говорят, я отвечаю: ну ты действительно считаешь, что Claude Code с этим не справится, если ты нормально заменеджишь контекст и у тебя есть опыт работы с ним? Ну, нет, решит просто — вопрос лишь во времени и твоём желании.

[1:47:04] Александр: Так вот, к тебе вопрос такой: как ты считаешь, нет ли у тебя, вообще говоря, чувства опасения, что ставка, сделанная на IntelliJ IDEA как платформу для агентов на рынок, в том числе с точки зрения продукта, очень рискованная? Ведь я, например, вижу, что мы уходим от разработки как программисты в платформах IntelliJ и приходим к разработке автономной. Для меня это движение ясно как день. И я хочу понять, какое у тебя видение на этот счёт и, если у тебя есть какие-то сомнения, как ты их себе объясняешь — что вот «нет, это всё-таки круто».

[1:47:43] Михаил: Я бы сказал так. Наверное, здесь стоит разделить. Мы, по сути, сравниваем сейчас агентов внутри IDE-шек, в частности veai, с CLI-агентами, в частности с Claude Code. Стоит разделить две важных вещи. Их отличие, во-первых, в том, что один просто в консольке, и мы можем с ним общаться именно там, а второе — в тех инструментах, которые ему доступны. То есть чем он может пользоваться. И, как мы рассуждали, эти инструменты, ручки для агента, и дают ему возможности, свободу, и именно они делают его такими классными агентами, как мы их видим сейчас. Поэтому я бы сказал, что интерфейс и то, где мы взаимодействуем — в консольке или в IDE-шке, — как по мне, меньше влияет, чем то, какие инструменты у агента есть. И вот здесь ты очень правильно заметил: разве есть такая задача, даже пусть большая, которую, если мы тщательно заменеджим контекст, Claude Code не сможет решить? Ну, я тоже считаю, что такая задача либо какая-то суперспецифичная, которую Claude, может быть, никогда не видел, или что-нибудь такого рода. Не будем совсем чрезмерно позитивно настроены, но большинство задач, думаю, даже очень-очень сложных, очень масштабных, он сможет решить.

[1:49:20] Михаил: Однако детали начинаются именно в том, а как мы будем менеджерить контекст. И вот из того, что мы говорили про дебаггер — даже, например, дебаггер в IntelliJ IDEA, то есть в AI-агенте, и дебаггер в Claude Code. Claude Code тоже, по сути, может запустить дебаггер в консольке. Почему нет? Поставить breakpoint, почитать файлики, поставить их на линиях. Но тут главная деталь в том, что Claude Code приходится сделать просто больше действий. Самой модельке приходится делать больше действий и больше подумать: что вот тут дебаггер вообще не установлен, его нужно сначала установить, вот тут настроить, вот это не забыть, и команды вообще писать. Много всего нужно сделать. Вместо буквально одноклик-решения, которое состоит в одном клике: запустить дебаггер, поставить breakpoint, evaluate expression — всё в виде каких-то маленьких тулов. Но ещё проще это на примере декомпиляции. И то, и то можно сделать и там, и там, просто в одном чуть больше действий, в другом чуть меньше.

[1:50:30] Михаил: И вот здесь я бы сказал, что главная вещь, на которую опирается CLI-агент, который с одной стороны имеет безграничные возможности, потому что живёт в консоли и может делать всё что угодно, а с другой стороны имеет обратную сторону этой свободы — ввиду того, что он всегда может ошибиться, ему иногда нужно делать очень много действий, составляя их из маленьких шажков, которые за него никто вместе не собрал. Поэтому я бы сказал финальный такой ответ: я на самом деле с удовольствием бы работал в Claude Code, если бы в нём были инструменты из veai-агента. Вот так скажу. Я считаю, что эта чрезмерная свобода в какой-то степени, по крайней мере на данном этапе развития моделек, мешает. Точнее, даже не столько мешает, сколько там больше токенов тратится, больше времени, нам приходится больше контролировать, больше прописывать скиллы, больше просматривать, описывать цепочки действий, чтобы он их знал, — по сути, тоже в каком-то смысле выполняя роль агрегации разных инструментов в шаги, которые нужно выполнять. Это вот про скиллы: собирая всё вместе за него, подсказывая ему. Поэтому главная тут вещь — именно в инструментах. И мне кажется, что на данном этапе всё-таки инструменты, эта мощь, выразительность и качество IntelliJ-платформы, лично для меня, перевешивают безграничные возможности в терминале. При том что терминал у нас, конечно, тоже есть.

[1:52:22] Михаил: Тут ещё важная вещь: veai-агент позиционируется не только для разработчиков, которые просто захотели, скачали, начали пользоваться. Помимо этого, он ещё нацелен на интеграции в компании. То есть полноценная B2B-разработка. B2C у нас, конечно, тоже есть, но есть некоторая нацеленность на B2B. И вот тут появляются нюансы, потому что такие модельки, как Opus 4.7, как GPT-5.5, они классные, очень умные, очень сообразительные и догадливые. Вот главная ключевая вещь — то, что они догадливые: они умеют провести эту цепочку — ага, мне нужен дебаггер, дебаггера как инструмента у меня нет, значит, нужно установить это, проверить то, — и всё это сделают, в целом без проблем. Но когда мы говорим про внедрение AI в компанию — а оно сейчас семимильными шагами идёт в разные-разные компании, — там начинаются вопросы. И вопросы в основном про безопасность. Так, подождите, в смысле — вы хотите весь наш код слить в Anthropic? Нет, не пойдёт. У нас так не принято. Нам нужно, чтобы весь код оставался внутри контура и вообще ничего не выходило.

[1:53:51] Михаил: И тогда у компании есть два варианта, даже не один. Первое послабление — всё ещё использовать Opus 4.7 или GPT-5.5, но прикрутить DLP. DLP — это такой специальный шлюз, который позволяет конфиденциальные данные из ваших запросов выкидывать: всякие пароли, логины — звёздочки, — и моделька их как бы не видит. И вроде как ничего не улетает. Для каких-то компаний это уже решение, это уже супер. Но для каких-то тоже не подойдёт, в частности для банкинга. У нас довольно много клиентов именно из финтех-области, и там это решение прям совсем не подходит. И нужно, чтобы тот мозг, который будет питать этого агента, был развёрнут прямо внутри контура компании.

[1:54:50] Михаил: И вот здесь, ну как бы, до недавнего времени — буквально месяца четыре назад — по моему мнению всё было чуть хуже, чем хотелось бы. Потому что модельки, которые на тот момент были, по моему субъективному мнению, достаточно хорошие, они задачки реализуют, но требуют много времени на то, чтобы контекст менеджерить, много усилий от самого человека. И такой автономии, опять же субъективно, не получалось добиться с теми модельками, что были тогда. Но сейчас всё начинает меняться. Выходят модельки, которые открытые, их можно развернуть локально, если у вас есть достаточно железа, — кто угодно это может сделать. И разворачиваются они не то чтобы очень сложно. Более того, появились механизмы, как эти модельки немножко урезать, чтобы они не такими большими были и нормальные железки вывозили. Много всего появилось, и сейчас есть разные модели. Мне, например, очень нравится GLM. Она достаточно большая. Но мне очень нравится, что по этой догадливости она потихоньку начинает приближаться к облачным моделям.

[1:55:56] Михаил: Приблизилась ли она сейчас к ним? Равна ли она им? Или хотя бы сопоставима? Да нет, нельзя так сказать, конечно. Они несопоставимы, а Opus сильно умнее, и GPT-5.5 умнее. Но подвижки в ту сторону довольно мощные, они есть. А это значит, что когда мы говорим про разницу более догадливых и менее догадливых моделек, то вся эта разница в догадливости переносится на пользователя. Ввиду того, что ему нужно что-то заменеджерить, написать скиллы, написать контекстные рулы, какие-нибудь правила для агента, чтобы он их соблюдал, никогда не нарушал. И вот тут есть две важные вещи. Первое: если мы дадим эту абсолютную свободу, как раньше говорили, которая также привносит вероятность ошибок менее догадливым моделькам, то ошибок будет просто больше. И это начнёт ощущаться как что-то не очень работающее: если модельки будут постоянно на каждом шагу, которых будет много, ошибаться, мы будем получать нестабильные результаты. Это с одной стороны. С другой стороны — дать некоторого контроля и большей простоты, но при этом выразительности и мощности самим инструментам. И меньше свободы самому агенту, чтобы он мог точечно, пусть даже чуть более тупой с точки зрения самой модельки, всё равно решать задачки. Просто мы за него заранее немножко побеспокоимся и напишем эти инструменты, которые ему позволят это решить.

[1:57:43] Михаил: А с другой стороны, ещё такой момент: когда речь про чуть более маленькие модельки, требуется больше человеческой валидации. Требуется просмотреть код, проверить, нет ли там проблем, и делать это очень часто, быстро, потому что модельки очень быстро выдают результаты. Они помодифицировали первый файл, тут же второй, тут же третий. И либо время в конце тратить на это, либо можно сразу смотреть на изменения. Кому как удобнее: кому-то удобнее быстрее контролировать и отслеживать в IDE, кому-то — в консоли. На самом деле и в компаниях мы встречали оба подхода. Кто-то реально полностью сидит на Vim и разрабатывает сугубо в консоли, но чаще встречаем, когда люди используют IDE. И, наверное, вот в этих двух вещах и кроется наш таргет, наш скоуп на данный момент как бизнеса: мы смотрим в сторону IntelliJ в силу её мощности, выразительности, и смотрим в сторону тоже IntelliJ, потому что это IDE, в которой разработчики в компании на рабочих ноутах привыкли работать, потому что там всё предустановлено. Наверное, главная фишка вот такая. Есть ещё одна минорная — как минорная, на самом деле важная — вещь, но про неё расскажу позже. Что думаешь, Саш?

[1:59:19] Александр: Да, я такой ответ и ожидал. В целом киллер-аргумент, с которым сложно хоть как-то разговаривать, — это то, что не все готовы отправлять свой код в Anthropic. Это железобетонное утверждение: если ты нацелен зарабатывать деньги на тех, кто не готов, то что-то придумывай.

[1:59:43] Михаил: Это именно оно.

[1:59:44] Александр: Всё остальное, мне кажется, можно обойти: можно взять, сесть на месяц, уйти и написать свою платформу — просто сказать, вот тебе IntelliJ open-source, давай убираем весь UI, делаем LSP-сервер поверх этого. Я думаю, за месяц сильный инженер напишет такую штуку, но он будет не вылезать. И предоставит все возможности IntelliJ, только без IntelliJ. Но я, кстати, думаю, что вы рано или поздно это должны будете делать, чтобы не лочиться на IDE и сделать автономного агента.

[2:00:19] Михаил: Тут на самом деле очень сложно, к сожалению. Мы прямо сейчас двигаемся в эту сторону одним интересным путём. Мы нацелены на то, чтобы все эти инструменты, которые мы годами нарабатывали, поставить в MCP. И тогда этот MCP можно будет хоть в Claude Code включить и получить ещё больше качества. Это, кстати, тот самый момент, где я бы хотел остановиться и сказать, что мы тестировали, в том числе, и большие модельки, и иногда замечали, что в силу того, что инструменты чуть больше, более инкапсулирующие, даже если мы говорим про облачные модельки, мы получаем просто более быстрое решение и, в частности, благодаря инспекциям, иногда более качественное — с отсутствующими там какими-то ошибками. Например, тот же Anthropic у нас внутри плагина. То есть нельзя сказать, что мы нацелены только на локальные модельки. Мы видим, что эти инструменты и самому Anthropic тоже помогают с облачными модельками хорошо решать задачки. Но, скажем так, это один из наших sales-поинтов — именно поддержка локальных моделей.

[2:01:35] Александр: И ты хотел ещё один интересный момент упомянуть.

[2:01:38] Михаил: Да, давай. Помимо этих плюшек, которые мы сразу получаем с интеллектуальной платформой ввиду работающих API, это вообще-то в целом очень даже неплохая инфраструктура для анализов. Мы до этого затрагивали запуск инспекций. Инспекция — это, по сути, анализы, которые встроены внутрь самой IDE. Это уже здорово: кто-то их собрал, подготовил, они хорошо стабильно работают, ещё и быстро. Но ведь этими анализами мир не ограничивается. Есть другие анализы. И вот тут стоит вспомнить, о чём была суть добавления этих инспекций в агента. То, чего мы в реальности хотели, — чтобы агент чуть больше понимал про программу. Чтобы у него в контексте была ещё информация о том, что вот тут ошибка компиляции или инспекция сработала, вот тут ещё какая-нибудь ошибка. Чтобы он это видел, лучше понимал, что в реальности происходит. Потому что по одному тексту ему приходится много догадываться.

[2:02:48] Михаил: И вот тут начинается самое интересное. Анализов в сущности очень много разных. Есть и динамические анализы, которые поднимают, запускают программу, смотрят на неё внутри, исследуют. И вот это второй наш таргет как компании, как veai-агента. У нас есть формальные методы — мы их так называем. Формальные методы — это на самом деле просто индексы, которые в JetBrains IDE, если вы её запускаете, у вас справа внизу пишется «indexing». Это как раз построение специальных индексов, которые позволяют, в частности, быстро по PSI уходить. Сам PSI, в общем-то, на базе этих индексов и построен. В силу того, что наша команда, до того как вся компания, вся veai создалась, буквально работала в JetBrains — буквально те же самые индексы, все эти API писала, — то на базе этих индексов мы поднимаемся и строим свои, которые позволяют проектную модель строить для агента. И помимо этих индексов, формальные методы — это ещё и анализы. Здесь у нас также есть ряд своих анализов, которыми мы занимались сильно задолго до того, как все эти агенты появились, и писали их всех ручками. Сейчас даже представить страшно. Их было много-много разных, с 2017 года мы этим занимаемся.

[2:04:14] Михаил: В частности, я, например, долгое время руководил проектом по разработке автоматической генерации тестов для .NET на базе символьного исполнения. Это такая очень специфическая технология. Суть в чём? Раз мы поняли, что инспекции дали такой профит с того, что агент чуть больше знает про программу и про исполнение, то можно и других анализов докинуть. Почему нет? В частности, это мы тоже, агенты, сделали. И получается довольно здорово. Например, у нас есть анализ на моргание тестов, который встроен прямо в инструмент запуска. Мы здесь идём по парадигме, что лучше анализы и подобные штуки встраивать прямо внутрь базовых инструментов, чтобы, во-первых, моделька их звала и не пугалась, что это какая-то страшная штука, и чтобы она не распылялась и не звала эти инструменты в неверные места, в неверное время, когда они будут бесполезны. Поэтому мы заранее об этом подумали и всё это собрали вместе, интернализировали, можно сказать.

[2:05:28] Михаил: Теперь чуть-чуть окунёмся в инструмент запуска конфигурации. Мы его тоже сравнительно недавно добавили. Это удобно, потому что у разработчиков на IntelliJ-платформе есть прям термин run configuration внутри IDE, в которой можно настроить, какие переменные будут, что перед этим нужно запустить. И они есть подготовленные и готовы. Агент всё это может запускать, и, помимо указания названия конфигурации, он также указывает флажок — а хотим ли мы запустить анализ на моргание тестов. В случае, если агент его ставит, происходит следующее. Всё исполнение запускается вместе с инструментацией. Здесь стоит остановиться на качестве такой инструментации. Это, по сути, возможность прямо в рантайме — когда у вас происходит JIT-компиляция в виртуальных машинах, а в случае интерпретируемых языков это когда исполняется одна инструкция в интерпретаторе, — есть возможность подсовывать дополнительные инструкции прямо внутрь байткода (в случае, если это виртуальная машина) или прямо внутрь сорцов (если это интерпретируемый язык). И делают это прямо в рантайме, в процессе исполнения.

[2:06:47] Александр: А в случае с JIT-компиляцией инструментация отменяет JIT-компиляцию как явление, или она в JIT-уемый код тоже что-то вставляет?

[2:06:57] Михаил: Там как раз всё на уровне JIT-уемого кода и происходит. Под капотом, грубо говоря, настраивается специальный флажочек, который позволяет получать ивенты о том, что вот сейчас произойдёт JIT-компиляция такого-то классика. И позволяет получать ивенты о том, когда она уже произошла. Ну и в случае с Java, например, нашу инструментацию мы делаем уже после того, как в байткод превратилось. Так просто удобнее, когда мы говорим про такие низкоуровневые анализы. Работаем так, что с байткодом, и прямо в байткод докидываем инструкции. Это то, что позволяет делать инструментация.

[2:07:39] Михаил: А вот дальше вопрос: а что докидывать? Что нам даст возможность реализовать анализ на моргание тестов? Там добавляется инструкция, грубо говоря, очень сильно упрощая, после каждой инструкции, которая говорит о том, что предыдущая инструкция была исполнена. Она вот только что исполнилась. И таким образом мы получаем порядок, в котором инструкции исполнялись. Также запоминаются снапшоты объектов и тоже куда-то складываются с помощью специальных инструкций. И всё это, грубо говоря, рассказывает про программу изнутри, показывая, как её исполнение происходило, что там были за детали. И внутри этого анализа дальше происходит следующее. Сравниваются трассы с запуском разных тестов. В смысле, нескольких разных запусков одного и того же теста. И как только агент видит разницу в поведении, которая прямо у него на глазах — он прямо видит каждую инструкцию, — он сразу понимает, что есть некоторый недетерминизм, и, значит, мы нашли какое-то моргающее поведение.

[2:08:49] Михаил: Здесь на самом деле очень долго можно говорить про то, какие у нас анализы интегрированы, в частности фаззинг, то же символьное исполнение, SAST как dataflow-движок, который ищет уязвимости. Всё это, если так вообще говорить, даёт просто больше информации агенту, позволяет ему меньше галлюцинировать и проверять качество из некоторых внешних анализов — как отдельных внешних сервисов, на которые агент не может повлиять. Он не может ошибиться в запуске этого анализа, потому что там для него только кнопка «старт», условно. И этот анализ выступает неким экспертом, который говорит: нет, ты здесь забыл, вот ты тут ошибся, всё-таки здесь есть бага, ты её не учёл. Или если мы тесты генерим — то вот нет, ты забыл здесь этот случай покрыть, у тебя там очень низкий branch coverage, и всё очень плохо. То есть возможность добавления этих различных анализов строится на базе IntelliJ-платформы, потому что, опять же, очень много опишек и PSI-возможностей несёт себе эта платформа, которая позволяет такие анализы надстраивать, переиспользуя, а не переписывая с нуля всю IntelliJ-платформу там, где оно надо.

[2:10:11] Александр: Вопрос, который меня интересует с самого начала, как человека, который любит думать стратегически, высокоуровнево, продуктово, но в то же время любит инжинирить глубокие штуки. Мне всё-таки интересно такой продуктовый вопрос задать: а что является для вас, собственно, метрикой успеха вашего продукта? Не в плане бизнеса и денег, заработанных контрактов, а с точки зрения качества вашего агента. Вот ты, например, добавил дебаггер. Он вообще нужен или нет? Мы с тобой вроде понимаем, что круто, но если продуктово взглянуть — а что, без него, словно там 100 тикетов он решил без него и 100 тикетов с ним, — он их быстрее решил, качественнее, или ничего не поменялось? Как валидировать вообще качество этого агента?

[2:11:08] Михаил: Блин, ты прям вглубь зришь, прям в точку. Действительно, этот вопрос супер-крайне важный, когда речь идёт о построении агентской системы, потому что, если мы не будем отслеживать, что на самом деле происходит при изменениях агента, мы не будем понимать, в какую сторону двигаться — было лучше или стало лучше сейчас. И непонятно, как определять, какую фичу дальше делать, какой инструмент завозить, чего агенту не хватает. Тут нужна некоторая аналитика поверх агента, и мы это сделали довольно интересным образом. Мы создали свою систему бенчмарков, которая отличается от классических тем, что, во-первых, там не только опенсорсные проекты — так как мы всё-таки компания, у нас есть большое количество партнёров, которые согласились и даже желали предоставить свои проекты для проверок.

[2:12:10] Михаил: Но что это за проверки? Как, имея просто проект, понять, хорошо ли на нём работает агент или нет? Непонятно, какую ему задачу дать, как её проверить и как это осуществлять. Мы здесь, по сути, построили такой полноценный эмулятор пользователя, который буквально берёт довольно большие наборы тикетов, задачек, которые прям в реальных проектах реально стояли и реально были там решены — и мы знаем даже, как они были решены, можем откатиться по коду, понять, какое было правильное решение. Есть такой набор задачек, и система бенчмарков работает так: запускается эмулятор пользователя, берёт очередную задачу, передаёт её агенту с набором специально описанных сценариев. Например, у нас разные режимы агента есть — переключить режим на, допустим, тестирование и передать задачку на тестирование. Так или иначе, он передаёт эту задачу. И помимо эмулирующего пользователя есть набор таких верифайеров, грубо говоря, проверок, которые позволяют точно сказать, решена ли была задача в итоге. Это либо какой-нибудь набор тестов, который покрывает всю функциональность фичи, или фикс, который агент делает. Это набор просто адекватных проверок из разряда: проверить, что оно билдится, что все тесты проходят. Либо более специфичные проверки на отдельные сценарии, например, если речь про формальные методы.

[2:13:51] Михаил: У нас один из уникальных сценариев есть — это повышение тестового покрытия как раз на базе всех этих формальных методов, инструментов анализа. И там метрикой того, что всё успешно, является буквально запустить проверку на покрытие, посмотреть, что она увеличилась, какие строчки покрылись, какие нет: есть некоторый трешхолд, либо есть точное знание, что вот эти строчки должны были быть покрыты при условии этой задачи. Есть, грубо говоря, большой набор точек в бенчмарке с этими сценариями. И у каждой точки есть жёсткий валидатор, который проверяет, решена ли она в итоге. Это уже звучит неплохо, даёт точную оценку, хорошо ли было решено. Но это ничего не говорит про то, как оно решалось. Насколько, например, агент сходил с ума? Может, он начал из-за наших тулов языки менять туда-сюда? Для этого всего у нас поверх эмулятора пользователя есть ещё некоторый джадж, то есть судья, который тоже является LLM-кой, который просто смотрит на всю историю чатика, подмечает важные моменты — он знает, что подмечать. У него есть тоже набор своих тулов, он всё это детально рассматривает и пытается вынести вердикт, хорошо ли отработал агент или нехорошо. Здесь много тонкостей: вроде как и пользователя, эмулирующего, надо оценивать, потому что он тоже может некорректно себя повести. Но всё это в итоге сходится в некоторый скор, который считается по набору формальных метрик — с запуском этих анализов и тестов — и по этому судье, который выносит вердикт и говорит: ну, на 80% хорошо.

[2:15:51] Михаил: А дальше просто берём, например, две версии агента, запуская на них одни и те же точки, и смотрим на сравнение: стало ли лучше или нет. Просим судью подробно расписать, насколько хорошо и понравилось ли ему использовать инструменты, насколько они были интуитивными. То есть это уже такая история с бенчмарками.

[2:16:17] Александр: Слушай, а такой вопрос. Понятно, что сейчас ты больше описал с точки зрения input, output и оценки результатов. А есть ли сбор и инструментация действий самого агента в какую-то аналитику, и является ли она тоже входом для оценки? Ну, условно, он там 10 раз запустил gradle check, а его новая версия 2 раза запустила gradle check. Что-то такое есть?

[2:16:43] Михаил: На самом деле, я бы сказал, что частично это как раз интегрировано в эти верифайеры, но в основном оно касается всё-таки исходного кода именно той задачи, той программы, в которой эта задача делается, нежели исходников самого агента. То есть инструментировать своего агента и смотреть, что там внутри происходит, какие инструкции он внутри себя исполняет, — это не то что прям супернужно, потому что у нас есть чат, вся история чата. И там видно прям, какие тулы вызывались, какие ответы были. Мы всё подробно знаем по действиям, по шагам, что делал этот агент. А то, насколько это оказалось успешно, какие были аргументы, точно ли всё корректно сошлось в итоге и насколько быстро, — вот это уже определяется этими верифайерами, валидациями и судьями.

[2:17:31] Александр: Да, это, конечно, сложная тема. Потому что, знаешь, в чём основная сложность? Я просто понимаю, как человек, который уже больше года фулл-таймит с LLM: он же может оптимизировать эти бенчмарки, если про них узнает. Та же самая метрика покрытия тестами. Вот здесь очень сложно: а мы агенту эту метрику даём как цель оптимизации, когда он решает задачу, или нет? Если мы не даём, то какого мы от него ожидаем выполнения этой метрики, если мы её ему не дали? Тут какая-то игра такая: а мы что хотим-то? Мы хотим шашечки или ехать? Если мы хотим конкретно качественное решение задачи, то мы, как инженеры, обязаны ему поставить одно из выполнений — это покрытие тестами по этой метрике. И тогда, если мы ему поставим, задача решится и будет покрыта. Но тогда бенчмарк теряет смысл. И здесь как бы — а что вообще так происходит, и что делать в этой ситуации?

[2:18:31] Михаил: Ну, смотри, здесь всё зависит от того, насколько высокоуровневая задачка. Если мы, конечно, просто скажем «добей покрытие до максимума» и будем мерить именно покрытие, то может произойти ситуация, что он будет его добивать искусственно, по сути, не создавая новые тест-кейсы, семантически полезные, а делая просто вот именно бенчмаксинг — просто чтобы его похвалили, сказали, что он молодец. Но здесь всё решается самими задачами. То есть есть у нас задача, например, написать какой-нибудь новый большой модуль, и у него есть, как и во многих компаниях, в Jira, критерий приёмки — и это действительно реальная задача, которая была создана компанией. В критериях приёмки прописано, что у нас есть трешхолд по тестам, что на новый код должно быть не менее, допустим, 50% line coverage и 50% branch coverage. И это хорошо, и проверять мы будем не только это, но и все остальные верифайеры, которые здесь включаются. И таким образом агент не сможет забенчмакситься, подогнаться под эти бенчмарки, потому что ему всё ещё нужно решить задачу. И не только его будут проверять на покрытие, а и всё остальное. Те тесты, которых до этого не было, которые мы напишем уже после того, как он код написал, которые просто подсовываются и проверяются, что там всё заработало, о которых он не знает. Вот этими множественными проверками эта вещь и решается.

[2:20:08] Михаил: Ну и плюс: что если он даже такую задачу, какую-то высокоуровневую, действительно сложную, забенчмаксился и сделал хорошее покрытие и хороший код — в чём проблема? Это же, в общем-то, этого и хотелось. Разумеется, мы ему не говорили, что именно вот это будет мериться, — он не знает, какие там верифайеры. Но если он из задачи понял, что раз тут стоит в критериях приёмки тестирование и некоторый трешхолд по каверджу, то, наверное, нужно сделать хороший кавердж, — так это ровно то, чего мы от него и хотим.

[2:20:41] Александр: Угу, угу. Ну, в общем, да. То есть я так ответ интерпретирую для себя в сухом остатке, что да, там одна метрика одного верифайера действительно может быть замакшена, но набор этих верифайеров — знаешь, как такая система сдержек и противовесов. Ты всегда выбираешь трейд-офф: особенно если у тебя есть время, ты там бенчмакситься бесконечно можешь, но, чувак, в какой-то момент ты должен остановиться. И это метрика, которая не позволяет везде всё максить. Это как, знаешь, бенчмакс — это треугольник, и ты в этом треугольнике двигаешь точку, и каждая точка тянет, но ты должен выбрать наилучшую, оптимальную позицию.

[2:21:20] Михаил: Прикольно.

[2:21:20] Александр: То есть количество верифайеров огромное — ну, не огромное, а разумное, большое — оно нивелирует бенчмаксинг как явление. Это прикольно. Я бы ещё… А добавляете ли вы в какой-нибудь из этапов человеческую оценку? Потому что вот только профессионал может сказать, хорошо задача решена или нет в конечном итоге.

[2:21:41] Михаил: Конечно, на самом деле. У нас есть целый отдел тестирования, который сидит, и всё, чем он занимается, — это берёт и решает какие-нибудь тоже задачи. Ну, похоже на действия бенчмарка, просто дополнительная человеческая проверка. Плюс ещё есть такая штука, как вайб-чек — быстро проверить, что что-то работает. Но на практике я могу сказать, что для достижения хорошего качества нужны две вещи. Это хороший отдел тестирования, который всё это проверяет, — эти бенчмарки как бы входят туда, давай скажем так. Но есть ещё одна вещь, которая круто помогает понять, насколько качественный инструмент мы делаем и насколько хорошо он действительно работает. Это дог-фудинг. Дог-фудинг — это простая парадигма, при которой мы сами кушаем тот корм для собак, который производим. То есть мы сами все пользуемся тем же самым агентом, которого создаём. И в том числе с помощью него решаем повседневные задачи. Таким образом видим, насколько действительно хорошо он работает, насколько годится для этих сложных повседневных задач.

[2:22:53] Александр: Да. Мне кажется, хорошая вообще мысль для финала подкаста — про дог-фудинг. Я тоже сам большой адепт всего этого, и все инструменты, которые я разрабатываю, я разрабатываю с помощью этих же инструментов. Но в какой-то момент, естественно, ты должен всё-таки вначале разработать первую версию инструмента, потом сильно страдаешь и на энтузиазме выгораешь, но в итоге доводишь инструмент до состояния, в котором другие люди начинают им пользоваться, — и такие: о, он работает. Так потому что я до этого страдал с ним и все баги, большинство бесячих штук, покрыл. Это, мне кажется, база, да, — дог-фудинг.

[2:23:32] Александр: Круто. Есть ли тебе что-то, что ты бы хотел, не знаю, пожелать? Я тебе даю просто микрофон слушателям, чтобы ты в конце, может, напутствие, мысль оставил, или оставить их в тишине — тебе слово.

[2:23:42] Михаил: Я бы сказал так: область AI развивается, есть разные в ней инструменты, поэтому надеюсь, что все найдут для себя свой инструмент. Потому что у нас у всех специфические задачи, свои особенные, у нас свои привычки, свои пожелания, свои удобства — что мы считаем удобным, что нет. Поэтому желаю вам как минимум попробовать. Если вы ещё на данный момент не пробовали ни одного AI-инструмента и думаете, что это просто пустой шум, — то, возможно, возможно, это пустой шум. Но попробуйте для начала. Посмотрите, как оно справляется с вашими задачами. Попробуйте что-то мощное для начала, например, какие-нибудь большие облачные модельки, вот тот же Claude Code. Или попробуйте veai-агента, тоже, в общем-то, помогает решать ваши задачи. Но так или иначе попробуйте, посмотрите, что для вас работает хорошо, чем стоит пользоваться, чем нет.

[2:24:43] Михаил: Там много-много тем, на самом деле, ещё по поводу агентов у нас остаётся сегодня out of scope нашего разговора. Мы сегодня в первую очередь на инструментах, углубились в них. Но есть ещё разные темы в виде скиллов, MCP, целых агентских систем — как можно друг с другом агентов интегрировать и налаживать их взаимодействие, чтобы это была прям какая-то AI-native компания. Всё это будущее уже приходит, оно уже прям очень близко, оно даже здесь, я бы сказал. Поэтому желаю всем притронуться к этому будущему. Надеюсь, что понравится. Ну и да, конечно, попробуйте veai-агента. Мы очень стараемся сделать его качественным, классным. Надеюсь, что вам тоже понравится.

[2:25:29] Александр: Как обычно, все ссылочки будут в описании, комментарии, вопросы, обсуждения можно задавать в YouTube. Ребят, если вы слушали этот выпуск, у вас возникали какие-то вопросы, вы хотели написать, но подумали «зачем», — напишите прямо сейчас. Мне будет интересно посмотреть, что вы вообще думаете. А алгоритмам будет понятно, что я делаю контент, который вызывает у людей какую-то эмоцию. Эмоции надо выражать, вообще в психологии это база. Не надо их в себе держать. Не держите, ребят, пишите. Да, а Мише большое спасибо за то, что пришёл, рассказал. Мне всегда нравятся эти hands-on, что называется, experience — не могу перевести. Вот с полей, как бы, да, горячий пик. Человек, который принимает решения, разрабатывает подобные вещи, — это всегда интересно. Как говорится, воздуханов мой подкаст не терпит. Всем пока!

[2:26:18] Михаил: Спасибо большое, Саш, спасибо.

[2:26:20] Александр: Всем пока.