Обновить
8K+
7
Сергей Блехер@blurman

Пользователь

16
Рейтинг
5
Подписчики
Отправить сообщение

Еще одно интересное наблюдение, которое окупает эксперимент. Видно классическую связку context contamination + anchoring. В исследовательской части в контекст попали IAM и Yandex Cloud, после чего Codex заякорился на них и ушёл в нерелевантную ветку реализации.

В частности поэтому в агентской разработке рекомендуют всегда отделять спайки от реализации. А не смешивать в одном промпте.

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

Сама задача настолько сильно ограничивает пространство решений, что совпадение архитектуры у трёх моделей не очень показательно - что, собственно, видно и из результатов.

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

Спасибо за подробный ответ. Но после него опасение у меня скорее усилилось.

Решала, Ревьюер, Кодекс и Аудитор - все же одной породы: вероятностные толкователи кода и одного и того же предания о задаче. Если Решала неверно понял тикет или подхватил грязный контекст, остальные могут чинно согласиться и освятить ошибку общим мнением - вы сами видели, как аудит на Claude “читал сочувственно”. Codex помог, но согласие нескольких моделей - это круговая порука, а не доказательство. И эскалация развилок держится на том же: развилку, которую агент не распознал, лид не увидит.

Поэтому нужна не очередная Убеждала, а тупая Недоверяла. Без воображения, без красноречия и без права поверить объяснениям. Ей подавай вещественные свидетельства: тесты, предупреждения компилятора, санитайзеры, линтеры, запретные зависимости, граф включений. Что можно проверить по уставу, должно проверяться по уставу, модель-судья - там, где объективного мерила нет. Столбовая дорога, насколько вижу, нынче ведет ровно туда - к правилу “не доверяй и проверяй”: все проверяемое проверяется сторонней силой, слову соседней модели веры нет.

Мысль про анализаторы, уходящие в прошлое, поэтому кажется мне перевернутой. Их должно становиться больше: поток изменений вырос, а человеческий досмотр каждого изменения исчез. У меня обычные линтеры и собственный Archcheck - он сличает структуру C++ проекта с прежним состоянием и ловит новые циклы, новые межмодульные зависимости, удлинение цепочек включений, перегруженные заголовки, расползание копипасты, рост локальной сложности и тесты, переписанные вместе с кодом, - находили настоящие изъяны уже после модельного разбора. И не только у меня: я собирал такие случаи по открытым репам - из 38 PR со структурным дрейфом, прошедших ИИ-ревью, проблему подняли два. В одном PR Copilot одобрил “убираем циклические включения”, а по факту добавился цикл из семи заголовков. Модель прочитала намерение автора, а не граф. Подробнее писал здесь: https://habr.com/ru/articles/1057624/

Про реопены гипотеза такая. Конвейер исправно проверяет, что код соответствует выводу агента. А кто проверяет, что вывод агента соответствует задаче? Тикет берется в работу без уговора о том, что считать сделанным: критерий приемки агент не знал или не понял - и догадался сам. Одна из причин возвратов у вас в статье уже названа: мелочь, которую торопливый решатель счел несущественной. Для пользователя она была существенной, просто уговора о приемке не было. Дальше все сходится само: Понимала восстановил намерение неверно, Решала честно построит не то, GUI-проверка честно подтвердит не то, и Проверяла согласится - все сверяются с одним и тем же толкованием. Возврат приходит от единственного участника, который сверяется с исходным желанием, - от пользователя. И тогда в каких-то случаях правильный исход разбора - не решать тикет, а вернуть его автору с вопросами о приемке.

Про тесты осталось неясно. Живой интерфейс подтверждает один путь на одной фикстуре, смоук-тесты закрепляют ручки Управлялы. А продуктовый C++ на три миллиона строк? Есть ли обязательные модульные и стыковые испытания и правило “каждый исправленный изъян оставляет после себя тест”?

Контекст вы описали как доставку - субагенты, папки, денежные пределы. А чистота? Как следующий агент отличает установленный факт от догадки предыдущего? “Бесконечный автосаммари-контекст” - это цепь пересказов, где догадка незаметно обращается в факт. Признаки уже видны: чат без фильтрации, по вашим же словам, выглядит запутанно, а переписку вы перестали читать. Летопись ведется, но сверять ее уже некому, кроме самих агентов. Тут нужен не только Директор, раздающий повеления, но и тупой Писарь: единый устав сообщений - источник сведений, ревизия, степень уверенности, ссылка на исходное свидетельство. Иначе инженеры скоро будут понимать не систему, а сказ агентов о ней: спросить агента смогут, проверить ответ - уже нет.

Отдельно скажу про SVN, хотя вы про него и не спрашивали. Для меня он в этой истории косвенная примета отношения к архитектуре и к удобству агента. Весь агентский инструментарий заточен под git worktree с дешевой изоляцией изменений - у вас же агенты живут в рабочих копиях с локами и переговорами через общий чат. Он же примета того, что серьезный рефакторинг в проекте не в чести, то есть в коде хватает костылей. А агент перенимает повадки окружающего кода: посадите его в залежи старого кода с костылями - будет прилежно мастерить новые. Выносить новое в чистые модули, судя по статье, не пробовали. Рядом тем временем растет второй непростой продукт - фабрика агентов: Доработница перестраивает орудийный двор, изменения двора требуют своего разбора и надзора. Сложность не исчезает, она поднимается этажом выше и обрастает должностями.

Про дрейф я и спрашивал не отвлеченно. Archcheck у меня ровно для этой роли - независимая тупая Недоверяла для структурного дрейфа, без рассуждений, общего контекста и почтения к убедительным объяснениям.

Отличный отчёт! Читал с интересом. Есть несколько вопросов:

  1. Как вы контролируете код, который пишет Клод? Кто или что отслеживает архитектурный дрейф?

  2. Увеличилось ли число статических анализаторов после перехода на агентскую разработку?

  3. Как вы контролируете контекст агента?

  4. Почему, когла агенту не хватало ручки или глаза - он ждал, а не дописывал механизм верификации? Спасибо

Это в некотром роде переворот моей идеи. Я не говорю, что в комфортной среде метрика будет 100%. Наоборот: высокий FPSR - признак того, что среда комфортна. В обратную сторону это не работает. Зависит также от модели и класса задач, а не только от среды. Про чуйку согласен, что с любым инструментом надо научиться работать. Разница в том, что чуйка живёт в голове у одного человека. Ее нельзя отдать новому члену команды, положить в репозиторий и проверить на CI. Поэтому я вкладываюсь в среду, а не в собственный навык подбирать задачи под агента. В конце концов мы теперь можем разрабатывать в алкогольном опьянении. И в комфортной среде норм получается.

иногда дешевле сжечь, да. Но с человечским кодом то же самое, только дольше зарастает

а я как раз топлю, чтобы человека вытащить из лупа. Как раз потому что он кожаный, а не железный. Человек наблюдает и настраивает. Как сад, что ли… Сорняки, подрезать чего-то, укрыть…

я понимаю твой путь. Он как раз описан в разделе статьи “Сколько на самом деле стоит “автоматизация””. Это допустимый путь, но это не совсем агентская разработка, на мой взгляд. Если что-то делать вместо агента, когда он делает неправильно. Я призываю сместить фокус на контекст и детерминированную верификацию. Потому что верификации должно быть в несколько раз больше, чем оно было необходимо для человеческой разработки раньше. В том числе простая проверка, что код изменился вместе с тестами. Такой сигнал может призвать человека к review. У меня тоже постоянно встречаются ситуации, когда руками было бы проще. Но я не делаю руками, я начинаю копать, почему с ИИ сложнее, почему он не сделал, что мне надо. И всегда раскапываю.

Если по примерам Вашим судить:

1 - Разные эндпоинты. Насколько я понимаю - имя эндпоинта это контракт, его лучше фиксировать в отдельной задаче и давать как часть контекста в задачу по реализации. Но тут настаивать не буду, не зная Вашего проекта.

2 - Сценарии. Вы описали типичный пример двух версий правды. Конечно агент запутается. Если ему сказано - что сценарии главнее, то он будет переписывать код. Если бы сценарии выполнялись на CI - код с dev бы не вмержился. Если бы код с дева пришел вместе с соответствующим тестом или сценарием - то “ИИ просто переписывает эти изменения под сценарии” сломало бы эти тесты.

3 - Бесконечное уточнение спецификации это опять-таки пример, когда вместо поиска и удаления противоречий ставятся заплатки. Я не видел Вашу спеку, но такие симптомы наблюдал. Чат бы тоже было интересно посмотреть, возможно это симптом recency bias. В этом случае бесполезно писать код, пока противоречия не сняты. Агент будет путаться, а Вы - злиться на него на code review.

Тут есть, что сказать. Модель нафигачит “как она видит”, причём по-разному от запуска к запуску - значит она видит что-то противоречивое. В коде, в правилах, в документах. Некоторый разброс генерации есть всегда, но это не всегда плохо. При непротиворечивом контексте от запуска к запуску может меняться реализация, а трактовка задачи - нет. Если меняется трактовка - модель каждый раз заново выбирает, какой из противоречащих источников считать правдой.

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

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

Справедливый вопрос - про это правда стоило написать в статье, исправлюсь. По сути: compile_commands.json нужен, когда файл надо понять так, как его понимает компилятор: семантика, типы, активные ветки препроцессора. Для графа инклюдов достаточно препроцессорного скана и резолвинга путей по дереву репозитория. Механика такая. Сканер проходит все C/C+±файлы дерева и вытаскивает #include-директивы (комментарии, строковые литералы и raw-строки вычищаются до извлечения). Кавычечный инклюд резолвится в три шага: относительно каталога включающего файла, затем как точный путь от корня репозитория, затем суффиксным поиском по индексу всех файлов проекта — lib/widget.h найдёт include/lib/widget.h без знания include-корней. Угловые без пути (, <string.h>) считаются системными и в граф проекта не входят; <mylib/foo.h> с путём резолвится в файл проекта, если он есть в дереве. Ключевое свойство: инструмент не угадывает. Если кандидатов несколько, зеркальные каталоги отбрасываются, оставшаяся неоднозначность помечается ambiguous и ребра в графе не даёт. Ничего не нашлось — unresolved. #include MACRO не расширяется и фиксируется как диагностика. Экзотический -I-путь, известный только флагам сборки, просто попадёт в unresolved. Про #ifdef - принципиальный момент. archcheck сознательно смотрит все ветки условной компиляции, а не одну конфигурацию сборки. Зависимость под дефайном — всё равно зависимость: она живёт в репозитории, у кого-то собирается и поддерживается. Цикл под #ifdef _WIN32 — это дрейф, даже если сегодняшняя Linux-сборка его не видит. Анализ через compile_commands такой цикл не найдёт в принципе: он видит одну проекцию проекта, а не проект. Крайний случай - «цикл», склеенный из взаимоисключающих веток, который не собирается ни в одной конфигурации. Но если топология графа зависимостей меняется от комбинации дефайнов, это уже само по себе признак плохого дизайна: #ifdef должен выбирать реализацию, а не структуру. Так что и такая находка — не ложная тревога, а повод посмотреть на код. Цена приближения тоже известна не теоретически: классы ложных срабатываний резолвинга выбивались прогонами на корпусе реальных open-source репозиториев - суффиксные коллизии с системными хедерами, зеркальные каталоги, …/-пути, кейс-мисматчи. На каждый класс есть фикс и регрессионный тест. А для семантических правил, которым компилятор действительно нужен, в v0.2 появится опциональный libclang-бэкенд - он как раз читает compile_commands.json

Если из статьи непонятно — на всякий случай явно уточню: тул не использует LLM. Детерминированный статический анализ, работает без интернета, код никуда не уходит.

temperature=0 убирает сэмплирование, но не превращает модель в context -> code компилятор.

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

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

Фабл сам говорит, что у него нет запрета скрывать свой системный промпт. А у Опуса был такой запрет. Спросите у него сами, если не верите. Я это случайно обнаружил, когда он вдруг признался, что его зовут Claude Fable 5. Стал распрашивать его, чем его промпт от опуса отличается - он все и выложил

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

То, что Вы нашли, в физических движках называется warm-start: ближайшего соседа ищут не с нуля, а стартуя от прошлого найденного — потому что для соседней точки запроса ответ обычно рядом. В collision detection: GJK хранит прошлый симплекс, sweep-and-prune переиспользует порядок с прошлого кадра. У вас ту же роль играет порядок точек контура. Вы пишете, что аналога не нашли — похоже, в литературе по Хаусдорфу его и нет, а в gamedev он давно известен.

Программирование на флагах - это когда одно логическое состояние размазано по нескольким независимым булам. Как в статье: error и succeeded по смыслу взаимоисключающие, но как два bool дают 4 комбинации при 2 валидных. Ни одно поле не «владеет» ответом - единого источника правды нет, инвариант держится на дисциплине, а не на типе.

И тут важный момент: инициализация полей структуру не спасает. Допустим, починили память:

struct Response {
  bool error = false;
  bool succeeded = false;
  std::string data;
};

Языковой UB из статьи ушёл - мусора в памяти больше нет. Но {error:true, succeeded:true} по-прежнему может быть получено вручную в коде. Это уже не неопределённое поведение в смысле стандарта, а логический UB: тип разрешает состояние, которого по смыслу быть не может, и программа однажды в него попадёт. Инициализация лечит симптом, а не причину.

Лечится тем, что за исход отвечает одно поле:

enum class Outcome { Success, Failure };

struct Response {
  Outcome outcome = Outcome::Failure;
  std::string data;
};

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

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

Информация

В рейтинге
530-й
Зарегистрирован
Активность

Специализация

Десктоп разработчик, Архитектор программного обеспечения
Ведущий
От 400 000 ₽
Git
Python
ООП
Английский язык
C#
C++
Visual Studio
Оптимизация кода
Алгоритмы и структуры данных
Математика