TGViewer
Channel Public Channel
Стать специалистом по машинному обучению

Стать специалистом по машинному обучению

@tobeanmlspecialist

Канал о машинном обучении для людей

Учусь разбираться в терминах ML вместе с вами. Для разбора теории приглашаю профессионалов. Подкаст: https://mlpodcast.mave.digital

С вопросами и предложениями пишите @kmsint
Subscribers
10.9K
Photos
127
Videos
13
Links
622
Recent Posts 20 shown
Post #1207 855
Как я уже неоднократно отмечал - осень традиционно богата на техно-тусовки. Одна из тех, что я посещаю уже регулярно - Selectel ТехноДень. В этом году конференция юбилейная, десятая, обещают ещё больший масштаб, вот даже мини-серверную стойку прислали в качестве приглашения. Пройдёт 8 октября в Москве, в кластере "Ломоносов".

Программа разбита на четыре тематических направления: ИИ, инфраструктура, информационная безопасность и данные. Всего будет 20 докладов, можно выбрать и составить себе индивидуальную программу по своим интересам.

Из программы отдельно выделю доклад Антона Чунаева про то, как крупный бизнес строит безопасную ИИ-инфраструктуру - от хаоса теневых сервисов и расползающихся доступов к управляемой системе. Доклады Антона я всегда жду! Мне нравится, как и какой материал он подаёт, - без воды, на реальных сценариях. Тема сейчас болит примерно у всех, кто внедряет генеративный ИИ быстрее, чем успевает его осмыслить.

Кроме экспертных докладов, на мероприятии ждут интерактивные стенды, тихая зона для работы и кабинки для созвонов (приятная деталь для тех, кто вырывается на конференцию в рабочий день). Аудитория соберётся соответствующая - технические директора, руководители ИТ-команд, инженеры, разработчики, безопасники.

И ещё одна крутая научпоп-активность мероприятия, которую я жду отдельно, - живая запись подкаста "Вселенная Плюс", где Алексей Семихатов и Владимир Сурдин вместе с Борисом Штерном будут разбирать чёрные дыры и информационный парадокс. Как чёрная дыра умудряется уничтожать информацию о прошлом Вселенной, хотя по законам физики это невозможно. Два часа такого разговора после дня инфраструктурных докладов - очень хорошее переключение контекста! Говорю уверенно, вспоминая научпоп-лекцию на конференции в прошлом году. Отдельно отмечу, что подкаст и вечерняя программа будут доступны только для очных участников, в онлайн-трансляцию они не попадут. Ещё один повод присоединиться к конференции лично!

Участие бесплатное, нужно зарегистрироваться и дождаться одобрения.
  • 👍 5
  • 🔥 4
  • ❤ 2
  • 💋 2
  • 🤝 1
Post #1206 1.46K
Получил очередное подтверждение того, что стоит иногда запускать проекты с нуля, не таща в них предыдущие замороченные наработки. Я уже несколько месяцев как настроил себе довольно строгий флоу работы с проектами, в котором много всевозможных проверок (линтеры, тесты всех видов, форматтеры, хуки, CI, в общем, всё то, что позволяет держать кодовую базу консистентной и не даёт ей разваливаться с каждым новым изменением). И я помню, что каждую такую проверку мы проговаривали и настраивали явно, потому что по умолчанию Claude сразу после планирования писал код, не особенно задумываясь о поддержании его прочности.

А тут на днях я помогал настраивать другу Claude Code с нуля и мы сразу запустили реализацию одной из его идей. И, как по волшебству, там сразу же стали появляться и тесты, и линтеры, и хорошее разделение на слои... Я смотрел на это всё и меня не покидало ощущение, что агент работает по моим правилам, хотя мы их никак ему не подкладывали.

То ли ещё будет...
  • 🤔 11
  • ❤ 6
  • 🔥 4
  • 💋 3
Post #1205 1.39K
Что нужно знать, чтобы строить ML-модели в эпоху LLM и вайбкодинга?

Нужно знать, что нейросеть сама себя за вас не проверит, особенно если ее не попросить: какая метрика подходит под задачу (и подходит ли текущая), переобучается ли модель, верно ли выстроена схема валидации, сколько и как тестировать модель в продакшене.

❗️Заметить и исправить ошибки нейросетки сможет только тот, кто имеет крепкий фундамент знаний в ML 🤓

Получить его можно на курсе «База ML» от MLinside: 5 поток стартовал 23 сентября, но набор еще открыт – можно подключиться сейчас и начать обучение.

Курс основан на многолетнем опыте преподавания ML в МФТИ, ВШЭ и работы в топовых компаниях. Программа подойдет новичкам в ML, а также разработчикам и аналитикам, которые хотят разобраться в машинном обучении, узнать, как ML применяется в реальных продуктах, и перейти в эту сферу.

За время обучения вы:
▪️освоите ключевые алгоритмы машинного обучения;
▪️научитесь работать с данными и строить модели на Python;
▪️разберетесь в метриках и оценке качества моделей;
▪️познакомитесь с нейронными сетями;
▪️поймете, как устроены современные ML-системы;
▪️выполните практические задания и соберете проект для портфолио.

В новом потоке в программе появились:
▪️ работа с AI-инструментами в задачах ML-специалиста
▪️ основы ML System Design

А ещё в новом потоке в курс добавили разборы самых частых применений ML в бизнесе, стало больше практики и поддержки от кураторов.

Курс ведут практики с опытом работы в Яндексе, МТС и Сбере и преподавания в МФТИ и ВШЭ. Среди них Виктор Кантор, Никита Зелинский и Илья Ирхин, сами прошедшие путь от Data Scientist до CDO и понимающие все стадии развития в Data Science изнутри.

Оставить заявку можно по ссылке, менеджер свяжется с вами, поможет выбрать тариф и забронирует место.

Подробности о программе, формате обучения и тарифах — на сайте курса.
  • 👍 5
  • ❤ 4
  • 💋 2
  • 🤝 1
Post #1204 1.72K
Неделю назад я был на deep tech night - конференции Яндекса о технологических вызовах в эпоху AI, про анонс которой писал ранее. С уверенностью могу отметить, что это был один из лучших подобных ивентов года. И по плотности материала и по общению с умными людьми. Много старых приятных знакомств актуализировались.

Мне понравились разные доклады, и про каждый я мог бы написать подобный пост. Меня, впрочем, как наверняка и многих, очень зацепило выступление Мо Гавдата (ex-Chief Business Officer Google X). Причём не столько фактическим наполнением - для тех, кто следит за развитием агентных систем, Мо не сказал ничего радикально нового, - сколько уровнем работы с аудиторией. Это было особенно видно по Q&A-сессии, когда зрители один за другим отмечали, что речь получилась impressive. Очень содержательным был и доклад про автономный транспорт - подробный рассказ о том, как строится стек Physical AI и что происходит, когда он выезжает из симулятора в реальные условия. Показательна была дискуссия о том, как кодинг-ИИ-ассистенты активно входят во все бизнесы, в которых так или иначе присутствует разработка. И если раньше это были опасения, что агенты всё поломают без тотального контроля от человека, то теперь вопрос больше стоит в том, как нам самим эффективнее прикладывать свои навыки, чтобы помогать агентам, а не мешать.

Сейчас я хотел бы остановиться на докладе Алексея Гусакова "От классического ML к генеративным моделям: незаметная революция в RecSys". Пока все следят за гонкой больших языковых моделей и агентными системами, ровно та же трансформерная революция происходит в области, менее хайповой для рядового пользователя, - в рекомендательных системах, хотя внутри это один из самых нагруженных ML-контуров индустрии.

Небольшой контекст. Очевидно, что рекомендательные системы живут в десятках продуктов Яндекса, а самый сложный и нагруженный домен - рекламный. 500 тысяч запросов в секунду в пике и десятки миллиардов документов в базе. Новые же технологии обкатываются на музыке - домене попроще, где цена эксперимента ниже. Классическая архитектура рекомендаций при этом стадийная - сначала десятки генераторов кандидатов параллельно выбирают из огромной базы тысячи потенциально подходящих, потом лёгкие модели сужают их до сотен, и только в конце тяжёлое ранжирование выбирает финальную выдачу. За 10–20 лет доработок эта конструкция обросла сотнями ручных настроек.

Первое поколение трансформеров в рекомендациях (2023 год) было по мотивам поискового BERT, где токенами вместо слов стали пары "композиция + реакция пользователя" (лайк, дизлайк, скип), история пользователя сворачивалась в вектор, а близость к треку считалась косинусным расстоянием. Скромные по нынешним меркам 20 млн параметров, два месяца логов, история из 128 треков. Главный потолок этой схемы - стоимость обучения, потому что каждый префикс истории обрабатывался отдельно, и при квадратичной цене одного примера суммарная сложность для истории из n событий выходила кубической, O(n³). Удлинять историю и наращивать данные становилось неподъёмно дорого.

Дальше - "Аргус" (2025) и первый по-настоящему генеративный поворот - модель стала последовательно предсказывать следующий трек, как LLM предсказывает следующий токен. Асимптотика обучения упала до O(n²) и именно эта "скучная" оптимизация позволила нарастить масштабы. История выросла со 128 до 8000 треков, логи - с двух месяцев до двенадцати, модель - до 120 млн параметров. Но при этом остались два ограничения. Во-первых, вся история по-прежнему сжималась в один вектор, а близость к кандидату оценивалась примитивным косинусом - детали истории при оценке конкретного трека терялись. Во-вторых, никуда не делся каскад отбора - дорогие модели работали только с уже отобранными кандидатами, и поздний ранжировщик физически не мог вернуть трек, отброшенный на ранней стадии.

И вот SONA (2026) - модель, закрывающая оба ограничения разом. Ключевая инновация - Semantic ID. Вместо одного пространства из десятков миллионов взаимно бессмысленных идентификаторов каждый трек кодируется тройкой ID, каждый из словаря в 32 тысячи (32000³ с запасом хватает на всю базу). Получают их так: текст (название, автор, жанр) и аудио трека прогоняются через мультимодальную Qwen 2.5-Omni, затем refinement-трансформер, затем кластеризация RQ-KMeans. У этих ID есть физический смысл - меняя компоненты, можно наблюдать, как меняются жанры, инструменты или тембр голоса. Модель одновременно учится генерировать следующие треки и ранжировать кандидатов по ожидаемому отклику пользователя - единая нейросеть, никаких стадий, никакого ручного фиче-инжиниринга. На вопрос из зала "а не нагенерирует ли она несуществующих ID" ответ был довольно красивый: не нагенерирует по той же причине, по которой LLM не выдаёт случайный набор букв, - next token prediction сначала выучивает валидные комбинации, потом хорошие.

Про цифры. Прирост активных пользователей по поколениям: BERT-версии 2023 года давали инкременты 0,56–0,84%, Аргус в 2025-м - 1,93%, SONA в 2026-м - 4,53%. То есть один переход на end-to-end дал больше, чем все предыдущие улучшения вместе взятые. И достигнута эта цифра одновременно с радикальным упрощением архитектуры - по-моему, самая недооценённая часть истории. По сути, это первая в мире продакшн-рекомендательная система без ручных фич, обошедшая в A/B действующую систему музыкальных рекомендаций на умных колонках. Техрепорт, если что, выложен на arXiv. Отдельно Алексей показал, что рекомендательные трансформеры масштабируются по тем же законам, что LLM - больше параметров и данных - предсказуемо ниже loss.

Оставшиеся открытые вопросы. SONA пока обучена только на музыке - как получить совместную модель для Музыки, Кинопоиска, Рекламы и Маркета, ещё предстоит придумать. Дата-дрифт никуда не делся - музыка и тренды меняются, статичной модели нужно онлайн-обучение. Логичный следующий ход - раз LLM уже мультимодальны, сделать историю пользователя ещё одной модальностью внутри общей генеративной модели, чтобы рекомендации стали просто одной из её задач. А самый интересный вызов звучит так: генеративные модели, создающие музыку, и рекомендательные модели, её продвигающие, могут войти в симбиоз - системе проще оптимизировать метрики на искусственных треках, которые под неё же и сгенерированы.

Получается, RecSys повторяет путь NLP с лагом в несколько лет - от зоопарка рукотворных пайплайнов к одной большой модели, которая учится end-to-end.

В этот раз мне показалось, что мероприятие по уровню докладов приблизилось к Practical ML Conf, на которой я тоже побывал в субботу. Но про PML Conf расскажу как-нибудь в другой раз. Впечатлениям надо настояться.
  • ❤ 5
  • 🔥 4
  • 👍 1
  • 💋 1
  • 🤝 1
Post #1203 1.64K
  • 🔥 15
  • 👍 5
  • 🤣 4
  • ❤ 1
  • 💋 1
Post #1202 1.63K
Го
  • 🔥 13
  • ❤ 5
  • 🤣 1
  • 💋 1
Post #1201 2.19K
Таким образом, мержу по-прежнему всё сам, кроме контрактов. Хочется иметь возможность успеть вклиниться как тот самый man in the loop, если появится точечная необходимость. И код смотреть совсем тоже не перестал - смотрю периодически, в целом. Если глаз за что-то зацепится, мы это обговорим с агентом и закрепим принятые решения правилом.

Поживём в таком сетапе, посмотрю насколько стало удобнее и не появились ли новые проблемы.
  • 👍 3
  • 🤔 2
  • 💋 1
Post #1200 2.06K
Последнее время перестал смотреть глазами на код, который пишут агенты. Хотел написать, что "Не из лени, а просто арифметика перестала сходиться", но, если честно, то и из лени тоже. За последнюю неделю в семь репозиториев одного из проектов влилось 157 пулл-реквестов, - больше двадцати в день (у нас правило "один PR = один логический шаг, с тестами в том же PR", поэтому они мелкие, но их много). Вычитывая каждый, я стал тем самым боттлнеком - пока я читаю диф одной сессии, три-четыре других простаивают.

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

• CI на каждый PR - на своём железе, 5–11 минут: проверка типов, три линтера, проверка форматирования, отдельный линтер доступности, 321 файл юнит-тестов, сборка и бюджет размера бандла. Отдельным шагом CI выкачивает канон контрактов из соседнего репозитория и сверяет, что клиент зовёт эндпоинты, которые там реально описаны.
• Локальный прогон перед пушем, включающий то, чего в CI на PR нет: 89 браузерных спеков Playwright. Их пришлось убрать из PR-прогона - шесть раннеров живут на той же 12-ядерной машине, где идёт работа, и браузерные проверки съедали половину времени. То есть сейчас локальный прогон - единственное место, где браузер запускается до мержа.
• Проверка мутацией. Зелёные тесты значат либо "код верный", либо "никто не спросил", и различить это можно только одним способом - сломать в своей же правке то, ради чего тест написан, и убедиться, что он краснеет. У нас это одна команда. Она берёт строки, добавленные веткой, делает по одной неверной правке и смотрит, заметят ли тесты.
• Аудиты по факту - ручные прогоны на проде и разборы телеметрии. Тут и я смотрю глазами и агент анализирует.

Первое изменение. Теперь PR открывает агент. Раньше он готовил ветку, коммит, пуш и отдавал мне ссылку с предзаполненным заголовком и описанием, а PR открывал я, посмотрев код. Выигрыш в том, что пока я работаю с одной сессией, вторая уже открыла PR, на нём автоматически стартовал CI, и к моменту, когда я до неё дойду, прогон с большой вероятностью зелёный - останется только смержить. А если покраснеет, то агент сам разберётся, почему, и починит, не дожидаясь моего внимания. Раньше эти 5–11 минут начинали течь только после того, как я переключусь в сессию, которая прислала compare-ссылку.

Чтобы "готово" не приходилось выяснять по переписке, PR открывается черновиком и переводится в обычное состояние, когда CI зелёный. Сигнал живёт в самом пулл-реквесте: вижу готовый - мержу, вижу черновик - агент ещё работает.

Порядок при этом такой. Полный локальный прогон до пуша, а не параллельно с CI. Раннеры и работа делят 12 ядер, и под нагрузкой браузерный прогон растягивается вдвое и роняет 2–3 случайных теста, каждый раз разных.

Второе изменение. Контракты теперь агент мержит сам. У нас есть отдельный репозиторий-канон - там лежит описание REST-контракта (выгрузка OpenAPI), схемы событий шины данных и проза, объясняющая правила, которые по схемам не прочитать. Главное правило работы, появившееся с самых первых дней - "контракт первым". Сначала меняется канон, потом консьюмер, потом продюсер. А значит PR потребителя законно красный, пока контрактный не влит - его проверка читает канон из основной ветки в момент прогона. Получалось, что один мой человеческий шаг блокировал два машинных, и держал ровно столько, сколько я шёл до этой сессии.

Границу провели по тому, где есть механическое доказательство. Агент мержит сам генерируемое (выгрузку OpenAPI, JSON-схемы) и прозу, сопровождающую ту же правку формы. Форму можно доказать прогоном продюсера против ветки-канона. Мне остаются два случая, где ошибка молчаливая - это удаление поля из канона (ничего не падает - канон просто перестаёт описывать то, что живой сервис ещё читает) и правки архитектурных и процессных документов, то есть правил работы, которые агент сам себе утверждать не должен.
  • 👍 4
  • ❤ 3
  • 🤔 2
  • 💋 1
  • 🤝 1
Post #1199 2.32K
  • 😁 18
  • 👍 2
  • 💋 2
Post #1197 2.31K
В этом году уже в третий раз буду гостем Practical ML Conf в Москве. Приглашаю и вас присоединяться. В программе особенно отметил два доклада про инференс.

Владислав Носивской, руководитель группы инференса LLM в Yandex AI Studio, разберёт, чем агентский трафик отличается от обычных запросов к моделям. На примере публичных трасс, бенчмарков и опыта Yandex AI Studio он покажет, как снижать стоимость инференса: выбирать схему параллелизма, маршрутизировать запросы с учётом KV-кеша, разделять prefill и decode и использовать спекулятивное декодирование.

Александр Мисевич, руководитель Group of Efficient Runtime & Inference в AI Platform & Copilot Т-Банка, посмотрит на ту же задачу шире и расскажет, какие инженерные решения помогают превратить LLM и другие сложные AI-модели в доступный инструмент для бизнеса.

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

19 сентября, Москва + онлайн

→ Смотреть программу и регистрироваться

Реклама. ООО "ЯНДЕКС". ИНН: 7736207543. Erid: 2VtzqvL4A9A
Practical ML Conf 2026 Хардовая конференция для экспертов и практиков. Здесь будет всё о практическом применении ML: технические доклады ведущих специалистов отрасли, инженерные мастер-классы и много нетворкинга. Обсудим, как извлечь из машинного обучения реальную пользу для бизнеса.
  • 👍 2
  • 🔥 2
  • ❤ 1
  • 💋 1
Post #1196 2.38K
Опираясь на полученный опыт, вот, что могу посоветовать тем, кто задумывается о работе через несколько параллельных сессий. Не давайте им общее рабочее дерево - пусть каждая заводят свой worktree с первого дня. Как выяснилось - это одна команда и git предоставляет возможность из коробки. Пройдитесь по всему, что делится помимо файлов - порты, кэши, папки зависимостей, соседние чекауты, временные файлы с фиксированными именами - и разведите заранее. Относитесь к обходным путям, которые надо помнить, как к долгу с процентами - каждая новая сессия про него не знает и обязательно вляпается. И то, что должно соблюдаться всегда, выносите из инструкций в гард (предохранитель), который не даст сделать неправильно.
  • 👍 11
  • 🔥 4
  • 🤡 2
  • ❤ 1
  • 💋 1
Post #1195 2.24K
Первым выстрелил порт. Браузерные тесты поднимают дев-сервер и ходят в него, а playwright умеет переиспользовать уже запущенный сервер, если на нужном адресе кто-то отвечает. Само по себе это удобно и у меня включено до сих пор. Проблема была в адресе - сервер поднимался на обычном порту разработки, одном и том же для всех чекаутов. И прогон, запущенный из worktree, преспокойно тестировал приложение из главного чекаута, другой ветки. Он сообщил о дефекте, который в этой самой ветке был уже починен. Полчаса ушло на разбор несуществующего бага. Починка стала такой - каждому свой порт, а дев-серверу - флаг --strictPort (это флаг Vite, не Playwright), чтобы занятый порт означал ошибку, а не тихий переход на следующий свободный. Занятый порт должен кричать (сейчас на этом слове вдруг осознал, что понахватался терминологии от агентов, раньше я бы и представить не мог, что закрытый порт может "кричать" :)). Переиспользование при этом осталось, но теперь оно переиспользует только свой собственный сервер на своём собственном порту.

Иронично, что через три недели этот же флаг уронил чужой PR. CI у меня живёт на этой же машине, где работают агенты, на своих раннерах, и в какой-то момент чей-то прогон покраснел с сообщением "порт 5174 уже используется" - порт держал локальный прогон одной из сессий. К коду в том PR это не имело ни малейшего отношения. То есть сначала мы сделали порт строгим, а потом обнаружили, что строгий порт без изоляции сам становится источником конфликтов. Развели порты по ролям - для рабочей сессии один, для CI - другой, между двумя своими worktree - задавать явно.

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

Третьим выстрелило то, что каталог агента, в котором запускается фоновая команда, - не то состояние, на которое можно рассчитывать в следующей команде. У моего раннера после фоновой задачи каталог переднего плана возвращается туда, где был. Агент запускает длинный прогон в фоне, потом выполняет обычную команду - и правит файлы уже не в своём worktree, а в главном чекауте. Молча, без единой ошибки. Однажды за одну сессию такое случилось трижды. Механика та же, что и в первом инциденте, только виноват уже не сосед, а собственный дрейф. Лечится тем, что каждая команда начинается с перехода в свой worktree, а не только первая в цепочке. И, поскольку такое правило держится исключительно на внимательности, теперь стоит хук, который просто отказывает в записи в главный чекаут репозитория, если у того есть живое второе дерево. Чтение и уборку после мержа он пропускает, всё остальное - нет.

Последнее, о чём хочется рассказать про текущий флоу - то, как открываются пулл-реквесты. Агент у меня не открывает их сам, он готовит ветку, коммит и push, а дальше отдаёт мне ссылку на страницу сравнения веток с уже подставленными заголовком и описанием. Я открываю, при желании правлю прямо в форме и жму кнопку. Смысл в том, что создание PR - это точка, где я могу посмотреть на работу глазами перед тем, как она поедет в мастер. Честно признаюсь, что реально ревью я провожу всё реже и реже, больше полагаясь на выросшие вокруг процесса автоматические проверки. Плюс база всегда мастер - отдельное правило, появившееся после того, как GitHub однажды влил PR в другую feature-ветку, которая к тому моменту отстала, и работа просто не доехала.
  • 👍 4
  • ❤ 2
  • 😁 2
  • 💋 1
Post #1194 1.35K
Первое, что мы сделали - ввели новые правила. Перед созданием ветки смотреть git status, чтобы не унаследовать чужое. Перед каждым коммитом проверять, на какой ветке стоит HEAD. Правила хорошие, они и сейчас в силе. Но по-настоящему проблему они не решают. Между проверкой и действием всегда есть окно. Агент посмотрел git status, увидел чистое дерево, начал работу, а через двадцать минут соседняя сессия сделала свой checkout. Дисциплина уменьшает вероятность, но не устраняет причину, что дерево одно, а писателей несколько.

Настоящее решение оказалось встроено в Git и называется worktree. Век живи - век учись, как говорится, даже не знал до этого момента о такой фиче гита.

git worktree add _wt/задача-сессии-N -b ветка-сессии-N origin/master

Она создаёт ещё одно рабочее дерево того же репозитория - в подпапке _wt/задача-сессии-N, с полным набором файлов проекта и, главное, со своим собственным HEAD. При этом никакого второго клона не появляется - история, объекты и все ветки остаются общими, физически одними. Новое дерево знает, где лежат эти общие данные, а они держат запись о каждом живом дереве. Занимает это примерно столько, сколько весят файлы проекта, то есть история не дублируется.

При этом переключение ветки в одном дереве больше не может повлиять на другое, потому что HEAD теперь у каждого свой. Каждая сессия сидит в своём каталоге и делает там что хочет. Главный чекаут остаётся стоять на мастере как справочная копия, и его никто не трогает.

Несколько практических нюансов, всплывших в процессе работы. Одну и ту же ветку нельзя чекаутить в двух деревьях сразу - Git прямо откажет и назовёт, какое дерево её держит: fatal: 'feature' is already used by worktree at '…'. Запрет можно снять флагом --force, и я специально попробовал, что тогда будет. Получается так. Коммит из первого дерева двигает ветку, второе дерево оказывается на новом коммите, но с файлами старого состояния, и его git status показывает чужой файл как удалённый. Следующий обычный коммит оттуда этот файл действительно удаляет. То есть без запрета вернулась бы та же гонка, только хуже. В исходной истории работа терялась и её можно было достать из коммита, а здесь она отменяется коммитом, который выглядит совершенно законным. То есть изоляция worktree - это про то, что у каждой ветки ровно один писатель.

Другой нюанс. Дерево после мержа надо убирать командой git worktree remove, а не просто удалять папку, так как иначе Git продолжит держать служебную запись о нём и считать ветку занятой, пока запись не почистить через git worktree prune - сам он до неё может добраться, но нескоро, там свой TTL (время жизни, срок устаревания). И ещё. В новом дереве нет ничего, чего нет в Git - ни .env, ни установленных зависимостей, ни виртуального окружения. Для питона мы обошлись относительным симлинком на окружение основного чекаута, для веба - общей папкой зависимостей. При этом, отсутствие .env неожиданно оказалось полезным - свежее дерево - это то, что видит CI, и однажды именно так мы воспроизвели падение, которое локально не воспроизводилось никак.

После перехода на рабочие деревья класс проблем "коммит ушёл не туда" исчез полностью. Даже не уменьшился, а именно, что исчез. В истории видно, как переключения веток в главном чекауте обрываются в один день, и дальше их просто нет. Мержится по-прежнему несколько десятков PR в день, и нужно понимать, что подход не ускоряет отдельно взятые операции, он делает параллельную работу надёжной, а значит устраняет временные потери на то, чтобы разобраться, почему всё идёт не так, как хочется. Скорость даёт параллельность, а worktree убирает аварии, которыми изначально мы с агентами за эту скорость расплачивалась.

А теперь главный урок всей истории. Worktree изолирует рабочее дерево, но не изолирует окружение. Файлы разъехались, а всё остальное, что сессии делят между собой, осталось общим. И следующие три инцидента пришли именно оттуда.
  • 👍 5
  • 💋 1
Post #1193 1.25K
Ломается так. Сессия А создаёт ветку и какое-то время правит файлы. Сессия Б в этот момент домержила свой PR и делает то, что положено делать после мержа - переключается на мастер и подтягивает свежую версию. Она не сделала ничего неправильного, это рутинная уборка. Git её пустил, потому что незакоммиченные правки сессии А не мешали переключению - те файлы одинаково выглядели на обеих ветках, и Git спокойно перенёс изменения за собой (если бы они конфликтовали, checkout бы отказал, и вся история пошла бы иначе). Но дерево-то одно на двоих. HEAD, стоявший на ветке сессии А, теперь стоит на мастере. Сессия А об этом не узнает никак: она не выполняла команд, ей никто ничего не сообщил, у неё в чате по-прежнему написано "создал ветку feature/…".

Затем сессия А делает git commit. Коммит всегда ложится туда, куда указывает HEAD, а HEAD в этот момент на мастере. Локальный мастер уезжает вперёд на один коммит с чужой работой. Дальше git push -u origin ветка-сессии-А создаёт эту ветку на сервере - и она указывает на тот коммит, с которого была отрезана, потому что локально её никто никуда не двигал. В origin уезжает ветка ровно в том виде, в каком её создали - пустая. Отсюда и "нечего сравнивать".

Такие записи сохранились в reflog (журнале, куда гит пишет каждое перемещение HEAD). Журнал не знает, кто именно выполнял команду, но, в целом, картина восстанавливается однозначно:

12:44:14 checkout: moving from master to feat/the-controls… ← сессия А
13:11:49 checkout: moving from feat/the-controls… to master ← сессия Б
13:11:51 pull -q --ff-only: Fast-forward ← сессия Б
13:23:32 commit: feat(editor): the controls are part of… ← коммит сессии А, на мастере
13:37:26 reset: moving to origin/master ← сессия А

Двадцать семь минут нормальной работы между "создал ветку" и "дерево ушло". Ни одна команда за это время не упала.

В принципе, чинится это без переключения дерева - чтобы не мешать соседу, который в нём работает. Ветку переставляют на нужный коммит принудительно (git branch -f), отправляют на сервер с перезаписью и откатывают локальный мастер к серверному состоянию. Перезаписывать опубликованную ветку лучше не голым --force, а --force-with-lease. Он отправит только в том случае, если на сервере с момента вашей последней сверки ничего не изменилось. В обычной работе разница не существенна, а при нескольких параллельных сессиях именно она отделяет починку от затирания чужой работы. И перед всем этим стоит посмотреть git show --stat на спорный коммит - если внутри оказалось что-то чужое, это уже другая история.

Теперь та же проблема в обратную сторону. Незакоммиченные файлы не принадлежат какой-либо ветке. Совсем. Они просто лежат в рабочем дереве, а ветка про них ничего не знает, потому что ветка - это указатель на уже сделанные коммиты. Особенно это касается новых файлов, которые Git ещё не отслеживает. При переключении веток он их не трогает вообще, они остаются на диске как есть.

И вот сессия Б создала в дереве пару новых файлов и ушла на мастер. Сессия А, ничего не проверив, создаёт ветку - и чужие файлы оказываются в её ветке как её собственные. Команда git add -A, которой агенты пользуются постоянно, означает "взять все изменения из рабочего дерева, которые Git не игнорирует", и она честно берёт всё, включая чужое. Дальше сессия Б делает свой коммит, но HEAD ведь уже стоит на ветке сессии А. В origin уезжает коммит с сообщением сессии Б, её работой, чужой поправкой и под именем ветки сессии А. Распутать этот клубок постфактум можно, но агент сталкивается с тем, что не всегда однозначно понимает чью историю имеет право переписывать.

У всех этих случаев одна общая черта - ни одна команда не завершилась ошибкой. Git каждый раз сделал ровно то, что у него просили. Просто это "что просили" складывалось из команд двух независимых процессов, каждый из которых считал дерево своим. Такие вещи не ловятся тестами, не видны в CI и обнаруживаются, только когда я открываю ссылку на PR и вижу, что сравнивать нечего.
  • 👍 5
  • 💋 1
Post #1192 1.38K
Изначально, выстраивая свою работу с кодинг-ИИ-ассистентами, я работал очень последовательно - небольшими срезами и не больше одного PR на репозиторий за раз. И при этом моя любовь к микросервисам позволяла распараллелить работу, потому что для каждого отдельного микросервиса я завожу отдельный репозиторий. Также я обычно держу отдельный реп с контрактами и документацией, с которым сверяются остальные. Работа была выстроена так, что сначала всегда идут сверки с контрактами и если контракты затрагиваются - в первую очередь приводятся в нужное состояние они, а только потом идут изменения в сервисах, ложащиеся на изменившиеся контракты.

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

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

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

Несколько сессий оправданны ещё потому, что воркфлоу состоит из большого количества тестов (юнит, интеграционных, e2e, мутационных и т.п.) и пока одна сессия ждёт прогона тестов, который может занимать от нескольких минут до их десятков, вторая делает следующую по списку задачу. Выигрыш очевиден. Но первые недели он регулярно съедался происшествиями, которые выглядели как "больше работы по поддержке параллельности, вместо самой работы". Из самого частого: агент отчитался, что работа сделана, ветка запушена, вот ссылка - а GitHub на этой ссылке пишет "There isn't anything to compare". Ветка есть, коммита в ней нет. Работа была, я её видел, тесты по ней проходили.

Чтобы объяснить, куда делся коммит, напомню одну вещь про Git для лучшего погружения в контекст. В повседневной работе о ней не думаешь, потому что в одиночку она не мешает никогда. Клон репозитория состоит из двух разных вещей. Первая - данные самого Git - вся история, все коммиты, все ветки. У обычного клона они лежат в скрытой папке .git. Вторая - рабочее дерево - те самые файлы проекта, которые мы видим в редакторе. И вот ключевое - данных гита хватает на сколько угодно веток одновременно, а рабочее дерево у клона одно. Ветка при этом - это не папка и не копия проекта, а просто указатель на коммит - строчка в файле, если совсем грубо. Ещё есть указатель по имени HEAD, который говорит "мы сейчас вот здесь". Когда мы делаем git checkout другая-ветка, git не создаёт никакую копию, - он переписывает файлы в вашем единственном дереве под состояние той ветки и переставляет HEAD. Пока вы в клоне один, это ровно то, что нужно. Как только в нём двое - начинается то, ради чего этот пост.
  • 👍 4
  • 🔥 4
  • 💋 1
Post #1190 2.11K
Поэтому следующая ступень ритуала звучит так: после мутации убедись, что прогон вообще состоялся и упало именно то, что должно. Смотреть нужно не только на "0 failed", а на количество собранных тестов и на имя красного. Ноль упавших при неверном пути к тестам и ноль упавших, потому что всё работает - в консоли выглядят одинаково.

Выводы из опыта:

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

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

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

Четвёртое. Тестировать через тот путь, которым ходит прод. Не через хелпер, который дублирует шаг, и не через мок, который добрее реальности. Мок обычно добрее, если только осознанно и целенаправленно не делать его злым - он часто не таймаутится, не возвращает 401 и не забывает поле.

Пятое. Красный тест тоже нельзя принимать на веру, как известно "зелёный" не всегда значит "работает", а "красный" не всегда значит "баг". Сначала нужно проверить окружение. Устаревший чекаут, не тот порт базы, забытая env-переменная дают ровно такую же красноту, как настоящая проблема, и ведут расследование не туда.

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

Ну, и, как говорится, тестов много не бывает. И, как я уже когда-то писал, агенты пишут их всё лучше, но понимать, что именно и как проверяется, думаю, смысл до сих пор имеет.
  • 🔥 9
  • ❤ 2
  • 🤔 2
  • 💋 1
Post #1189 1.76K
Есть особый сорт дефекта, который появляется часто тогда, когда код и тесты к нему пишет один и тот же агент. Это известная проблема, которую я уже несколько раз упоминал и даже писал, что Claude с ней разобрался самостоятельно. Проблема называется "тест, который не может упасть". Он зелёный, он в CI, он выглядит как проверка, но если сломать то, что он охраняет, он останется зелёным. И вы об этом не узнаете, потому что узнать об этом теперь можно только одним способом - известным в народе тестированием продом.

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

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

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


expected = calculate_price(order)
assert response.price == expected


Дальше. Недавно я добавлял в проект роль "админ readonly" и обнаружил, что десяток тестов админских страниц всё это время проходили от лица анонимного гостя. Просто потому, что роль до этого дня ни на что не влияла, и разницы не было видно. Тесты были зелёные ровно потому, что проверять было нечего. Как только роль начала что-то значить, они посыпались все разом. Зелёный тест отвечал "работает" на вопрос, не имеющий смысла.

Так у нас появился ритуал, который зафиксирован отдельным правилом. Написал проверку - сломай то, что она охраняет, и посмотри, что она покраснела. Это позволяет отделить реальную проверку от её имитации. По сути, это ручное, но теперь руками агента, мутационное тестирование.

Но вы, надеюсь, не подумали, что на этом проблемы с тестами прекратились? Мутация, оказывается, тоже может не сработать. Буквально на этой неделе Claude сломал правило в файле, прогнал тесты и увидел в выводе "1 error". Обрадовался, что поймал - и чуть не пошёл исправлять. А это была не упавшая проверка, это питон не смог собрать файл. Клод неаккуратно вырезал строку и порвал синтаксис. То есть тесты вообще не запускались, и проверка проверки ничего не проверила.
  • 🔥 7
  • 🤔 3
  • 💋 1
Post #1188 2.09K
Программирую, не приходя в сознание. Так вот как китайская комната, оказывается, работает.
  • 🔥 14
  • 🤓 6
  • ❤ 3
  • 🌚 3
  • 💯 3
  • 💋 2
Post #1185 2.94K
Кажется, я понимаю, что могли чувствовать люди-компьютеры, занимавшиеся математическими вычислениями на бумаге, когда их постепенно начали заменять компьютерами железными. Да, стало быстрее, да стало точнее и надёжнее, но тот самый кайф от работы карандашом, быстрыми прикидками в голове порядка получающихся значений, ощущение того, что ты чувствуешь цифры кончиками пальцев - всё это стало уходить. Кому-то, конечно, эти изменения были исключительно в радость, так как счёт на бумаге был скучной рабочей рутиной, лишённой творчества. Но, уверен, были и те, кому такие размеренные действия доставляли удовольствие, а железные машины лишали ощущения власти над числами.

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

Видимо, надо снова идти решать литкод, теперь только там и получится что-то реально поделать руками, в работе этого больше не требуется.
  • ❤ 22
  • 😱 8
  • 🤝 3
  • 👍 2
  • 💯 2
  • ⚡ 1
  • 🔥 1
  • 💋 1
Older posts →

About this channel

How can I read @tobeanmlspecialist without a Telegram account?
TGViewer shows the public web preview Telegram publishes for Стать специалистом по машинному обучению: recent posts, photos, videos and the subscriber count, with no app, login or account.
How many subscribers does Стать специалистом по машинному обучению have?
Стать специалистом по машинному обучению (@tobeanmlspecialist) has 10.9K subscribers on Telegram, refreshed roughly every 30 minutes.
Does Стать специалистом по машинному обучению know I viewed it here?
No. Public channel previews carry no viewer identity, and TGViewer has no accounts or tracking of what you look up.
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →