Канал
У Льоні сьогодні
Нотатки архітектора систем та майстра на всі руки. Що я зробив, що з того вийшло, і що це все значить.
-
Як я робив плейлист на гулянку разом з Claude
💃🪩🕺 Стала задача зібрати девʼятигодинний плейлист для суботньої гулянки невеликою компанією бачатерос. В мене такий досвід вперше, тож ніякого заготованого матеріалу не було. Збирати 160 пісень вручну — та навіщо, якщо є Claude?
По структурі. Ну можна, звісно, накидати просто 160 пісень, зробити shuffle і закінчити. Втім. День не є однорідним. Музика має відповідати потребам. Ми перевірили погоду та розбили на такі стадії: збір-розігрів; передобідній блок танців; сієста; післяобідній блок танців; та блок танців під заграву. Погода підманула та замість спеки півдня був мінливий дощ — тож частково ця підготовка була хибною.
Але загалом допомогло те, що в мене були різні частини. Наприклад, на розігрів йшли не дуже відомі треки, а в танцювальний блок — хіти. Тут зробив відкриття. Танцювати лише хіти виснажує, бо хочеться навалити кожну пісню. Пʼять хітів поспіль — і вже втомився. Треба перемежати чимсь простішим-скромнішим.
Вечірній блок починався не з бачати, а з Tití Me Preguntó. Клод підказав, що це розкачає настрій люду, і так воно і було! Також в мене були сумніви про меренгу Suavemente, проте вона стала одним з яскравих моментів запальних танців.
🌇 Окремою задачею в мене стояло підготувати плейлист на захід сонця. Щоб там були гарні романтичні треки та було красиво. Клод нафантазував, що можна привʼязати плейлист по часу… але ж точний час заходу в певному місці неможливо передбачити, ми ж не в чистому полі. Тож зійшлися на тому, що просто був момент, куди перейти, як сонце почне заходити. (На жаль, погода не сприяла зовсім, тож дослухали романтичні треки в машині.)
По наповненню. Звісно, перелічив улюблені треки — свої та інших учасників. Та улюблених виконавців. Решту змісту Клод успішно заповнив різною музикою, багато такої що я й не знав. О, я йому нагадав, що є ремікси українських пісень — та й не тільки українських — які можна знайти тільки на YouTube. Також Клод чудово фільтрує по чорному списку виконавців.
Тут найбільше буде допомоги в наступний раз, бо вже не доведеться будувати все з нуля. Взагалі стає очевидним, що необхідно мати базу музики. (Мабуть, будь-який ді-джей це підтвердить.)
Наприкінці помітив, що Клод не має уявлення про пісню поза її назвою і загальних відомостей. Він не знає тривалість пісень, наприклад. (І при тому Клод думав, що точно підгадає пісні під час заходу. Ага!) (Заради справедливості зазначу, що загалом тривалість плейлиста статистично зійшлася.)
Тому планую зробити навичку чи навіть MCP для впровадження пісні як сутності — з URL, тривалістю, темпом тощо. Може, як раз в контексті бази пісень.
Бо ще гірше — плейлист лише з назв важко надійно перенести в будь-який музичний майданчик. (А я обрав YouTube, щоб точно можна було передати список іншій людині — і це знадобилося!) Є сервіси, втім, з непопулярною музикою вони дають багато хибних результатів — помиляється з виконавцем тощо.
Розвʼязав задачу інженерно. Спочатку попросив той же ж Клод згенерувати скрипт, який за допомогою
yt-dlpдав по пʼять результатів пошуку по назві з YouTube та — головне — зробив сторінку HTML, на якій було легко передивитися по кожній пісні варіанти та обрати підхожий. Так я за розумний час переклав свої назви у справжні ID треків з YouTube.Але… список ID треків це ще не плейлист! І засобів закачати такий список на YouTube я не знайшов. Отже — згенерував іще один скрипт, який бере список та через YouTube Data API пхає його мені в акаунт. Довелося кілька хвилин провозитися з налаштуваннями Google Dev Console, бо куди без неї — і успіх.
Нарешті — грати музику зі свого телефону це дуже незручно. Якщо вже готуватися серйозно, то взяти ще один телефон, налаштувати суто під відтворення музики, та використовувати його. Або… зробити хмарну платформу і бота… але до цих фантазій ми ще доберемось!
-
Ключовий фактор в роботі з LLM: уява
🎂📢🛫 Та в мене черговий збіг! Рівно чотири роки тому я почав цей канал, і сьогодні відновлюю щоденні пости. Досвід проситься назовні!
Спостерігаючи, як люди навколо використовують LLM, мені здається, що загалом це використання достатньо прозаїчне. Згенерувати текст — чи код. Або ж зробити конспект тексту — чи коду.
Звісно, все, що LLM вміє — це генерувати текст. Але текстом може бути що завгодно. Не код, а план реалізації.Чи план всього проєкту? Рецензія на код? Історія змін? Журнали? Метрики? Чати з менеджером? Чати з самою LLM? Команди для адміністративних операцій?
Наприклад. Сидимо ми на відеодзвінку з Данилом, аж як помічаю, що дзвінок гальмує, бо CPU зашкалює. Починаю дивитися, що до чого - Activity Monitor, htop тощо. І тут Данило пропонує — а доручи це агенту. Доручив! Агент виконав кілька команд та відразу запримітив, що справа навіть не в CPU, а в RAM. Недостача памʼяті призвела до жорсткого використання свопу. До того ж він відразу запропонував, які застосунки варто зупинити — та навіть міг сам це й зробити, не виходячи з чату.
Тобто достатньо було уявити, що LLM допоможе — і це відкрило нові можливості. Ну або помітити, можна й так сказати.
Інший приклад. Шукаємо причину активної помилки в продакшні. Ситуація горить. Можна шукати логи та скидати потроху LLMці. Або питати її, де в коді є релевантні місця. Але можна прямо сказати: в мене така-то ситуація, нумо розв’язувати. Та вона дуже компетентно почне це робити! Особливо якщо ти навісиш достатньо корисних MCP - можливо, згенерованих під власні потреби. Це теж потребує уяви.
Отже. На сьогодні закликаю дивитися на власні задачі ширше та сміливіше віддавати LLMці. Звісно, з надійними запобіжниками, про що я ще напишу.
-
Заміна пісні на відео на чистий трек
Дружина поділилася відео, де вона чудово танцює. Але: гучніше музики було чутно як хтось розмовляє. От би залишити тільки музику… Виглядає як задача для інженера!
-
Визначив пісню. Для того в Mac вбудований Shazam, відкриваєш
Control Center -> Recognize Music, запускаєш відео, готово. Так, це працює не тільки з тим що десь грає ззовні, а й з компʼютерним аудіо — дуже корисно. -
Завантажив MP3. Тут в нас є, хто не знає, yt-dlp. Просто золотий інструмент.
-
Знайшов приблизно позицію. Легше за все це було зробити за текстом. Підслухав, що там співають у відео. А далі в Apple Music відкрив відтворення з текстом, клацнув на потрібний рядок та отримав таймкод.
-
І, нарешті, залучив агента, щоб він мені зібрав все до купи. Ну не хочу я памʼятати ключі
ffmpeg- та, на щастя, вже не знадобиться. За кілька спроб підібрали точний зсув — та й готово! Справа вийшла хвилин на 10.
…Звісно, тепер залишилося питання: чи не може агент зробити все це зовсім автоматично? Виявилося, може!
Зрозуміло, що з Shazam та yt-dlp в агента проблем ніяких. Хоча в першу спробу агент завантажував лише демку треку — довелося уточнити, що нам потрібна саме вся пісня.
Залишався рівно один складний (як на мене) етап: визначити зсув. Але тут виявилося, що в агента більше знань, ніж в мене. Він і вокал ізолював, і аналіз хроми зробив. І здогадався звіряти тільки ті частини відео, де нема гучної розмови (а саме, ті, де Shazam давав збіги.) Щоправда, агент трішечки розгубився, бо відео починалося десь з середини пісні — не повірив. Поперевіряв ще та сам переконався. З першої спроби той же ж результат, якого я досягнув вручну.
Власне, от вам навичка, якщо цікаво принаймні ознайомитися.
PS: потужна практика роботи з агентами: після того, як той виконає певну складну задачу, попроси створити навичку. Це закарбує набутий підхід та зробить наступну спробу значно прямолінійніше.
PPS: чи я почав публікувати навички? Так!
-
-
GTD: розібратися з поточними справами, а не набрати нових
Ось хиба, яку я в себе знайшов. Я не раз писав про наслідки, але нарешті вдалося вхопити першопричину.
Отже: в систему GTD (та й в будь-який інший самоменеджмент) можна запхати дві категорії задач. Дійсні та бажані.
Дійсні — це ті, які в тебе є без усякого самоменеджменту. Та ти їх зробиш так чи інакше, бо вони ну дійсно потрібні. Я зараз не про рутину — то інше. А про неординарні життєві справи, які все одно робиш. Будь-яка людина замінить бойлер, підготує звіт, забронює готель для майбутньої подорожі без схем обробки вхідних та інших ритуалів.
Та бажані задачі — їх ми хочемо, але не робимо… на що класично кажемо “бо часу не вистачає”. Та я не про шалені мрії (переїхати жити у гори та відкрити там ресторан), а про близькі справи. Може це те, чим ти збираєшся зайнятися, як тільки “буде час” (у відпустці, разом з десятьма іншими планами). Чи певні обовʼязки, які трохи почекають (поки півник не підкрадеться та не клюне — тоді задача переходить в категорію гарячих та “дійсних”.)
Моя хиба в тому, що я дивився на GTD як на засіб досягнути саме бажаних задач. А не тільки дійсних. Власне, дійсні “і так робляться”, тому я не завжди звертав на них достатньо уваги.
Головна проблема з цим така, що бажані задачі — категорія не обмежена. Тож список проєктів розростається. Зʼявляється потреба розрізняти більш та менш важливі. Бо часу на все не вистачить, тож як впоратися? Та чому ж в GTD не те що не заклали пріоритети, а відверто від них відмовилися?
Для мене тут важливо було зрозуміти, що мій список проєктів мусить бути розміром з мене. Та в нього йдуть тільки дійсні задачі. Тоді їх не фізично буде більше, ніж я можу зробити. Якщо ставитися чесно, звісно. Тоді й чистити список простіше: не торкнувся задачі за тиждень — то й не така вона й потрібна, значить. То й пріоритетів з таким списком не потрібно — належність до нього вже найвищий пріоритет.
Тобто на GTD краще дивитися як на засіб робити дійсні задачі з меншим стресом, без відкладань та зайвого обмірковування. А потім, в міру того як зросте продуктивність, додавати щось бажане, яке зʼявиться, бо я вже маю на це змогу, а не лише бажання.
-
Аудіо у фоні це ще складніше
Отже, на минулому пості мої пригоди з базовими потребами застосунку не закінчилися.
Помітив, що у фоні застосунок грає музику недовго, та зупиняється. А складно не помітити — бо я відразу почав використовувати його для власних тренувань. В застосунку є можливість зациклити певну частину пісні, що мені дуже спрощує життя. Тож вмикаю пісню, вмикаю камеру та практикую… от тільки недовго.
Отже, промучився трохи, та почав спостерігати. Заміряв, що застосунок виживає у фоні рівно одну хвилину. Це завело мене на стежку хибних гіпотез, які починалися із “є певний період, який iOS дозволяє тримати застосунок у фоні”.
Вирішив, що то мій застосунок недостатньо зрозуміло пояснює iOS, що він є програвачем музики та потребує тривалого існування у фоні. Нацькував на цю тему Cursor, він вже і так, і сяк пробує — а результат однаковий — 60 секунд і кінець.
(До речі: що LLM вміє робити дійсно потужно — так це читати та навіть змінювати код залежностей. Я сам майже ніколи так не робив, бо було складно та ризиковано. А LLM ставить patch-package та успішно патчить навіть код на Swift.)
Прориву ми досягли, коли нарешті здогадалися додати вичерпне журналювання. Причому відразу! З першої же ж спроби.
(Тут теж LLM здатна прорватися туди, де я не знаюся: пояснила, де читати журнали застосунку з iPhone та власне потім сама ці журнали й проаналізувала.)
І що знайшли? Та досить очевидну в ретроспективі річ. Застосунок споживав надто багато CPU. Виявляється, в iOS є правило: якщо застосунок у фоні споживає понад 80% CPU, його зупиняють.
(А 60 секунд - це вікно оцінювання.)
Мій застосунок безтурботно ганяв анімацію React Native Animation - яка й керувала відтворенням аудіо. Зокрема, в режимі зациклювання я додав fade in/out - от він і був головною причиною.
Поки що вистачило зупиняти анімацію та трохи збільшити інтервал зміни гучності. Але також дізнався, що є готові API для автоматичного fade in/out - це було б ідеально.
Загалом бачу що в майбутньому треба весь рушій аудіо переписати на нативний, щоб React Native туди не лазив аж зовсім. Втім, для MVP піде й так.
Яка тут мораль: та класична. Спочатку зрозумій, у чому проблема, та доведи це, а потім вже починай виправляти. Це залишається правдою з LLM чи без.
-
Аудіо — дуже складно, насправді
Сьогодні пост не про LLM! Я теж радий.
Я тут роблю застосунок для розбору пісень для танців. Кілька місяців тому зробив онлайн варіант, але зараз переробляю у вигляді мобільного застосунку. Традиційно, 80% зусиль пішло на аспект, який здавався мені тривіальною базою, яка сама по собі нікому й не потрібна.
Отже, в серці застосунку лежить те, що інтерфейс рухається в такт пісні. Насамперед рахує удари — як метроном. Для того ми визначаємо BPM - удари на хвилину, привʼязуємо перший удар, і потім в момент часу
Tнескладна математика каже нам, що грає ударNвісімкиM, ну з цим проблем ніяких.Але виявилося несподівано складно відтворити це в інтерфейсі. Здавалося б, будь-який програвач таке вміє. Не зовсім. Біт на екрані розповзався від звуків пісні. Я довгий час думав, що то в мене нема хисту визначити BPM та оскільки помітити десинхронізацію треба на око (та вухо), це було дуже важко підтвердити. Але без нормальної привʼязки до аудіо решту функцій можна було не будувати.
Першим чином я думав, що гальмує React Native? (Ну бо це те, з чим знайомий.) Трішечки такого ефекту було, але насправді біт в пісні це близько 500 мс, та ніякий RN настільки не гальмує. Звісно, оптимізувати було що, бо екран пісні, як бачите, жирний та весь його перемальовувати не гарно. Тут нічого особливого.
Друге — затримка аудіовиходу. Така затримка є, особливо якщо програвати через Bluetooth вона сягає 200 мс та більше… Але то теж навіть не один удар. Втім, дізнався, що принаймні в iOS є вбудована можливість дізнатися тривалість затримки поточного виходу. (Та звісно — в кожного виходу власна затримка.) Що й потрібно для того, щоб відповідно затримувати оновлення інтерфейсу. На щастя, це одноразове та автоматичне покращення, та хоч я зробив інтерфейс для редагування цієї затримки, базових налаштувань достатньо.
Справжні цікавинки почалися, як закопався у формат файлів. Власне, помітив, що на відміну від моєї замученої піддослідної пісні, інша грає та перемотується без жодних проблем. Між піснями знайшлася важлива технічна різниця. Отже: для точної перемотки потрібне кодування з CBR, тобто сталим бітрейтом, (а не VBR - змінним). Оскільки не бачу сенсу вимагати CBR від користувачів, впровадив перекодування в CBR на вході. Зберігав я у формат AAC.
(Змінний бітрейт суттєво ефективніше, я памʼятаю ще як перекодовував власну колекцію MP3, щоб більше влізло в плеєр. Суть його в тому, що простіші місця пісні будуть стиснуті більше.)
Перехід до CBR відразу зробив перемотку стабільною. Це був очевидний успіх. Але зʼявилася нова дивна річ: певні місця в пісні завжди перемотувалися неправильно. Ну, наприклад, в той час, як перемотуєш на другий удар, грає ще перший. Причому, тільки після певної перемотки! Якщо грати з початку пісні, такого не було.
Тут я дізнався ще один нюанс: файл AAC не “проіндексований”, це плаский потік даних. Тому, хоч перемотка і працювала стабільно, але не завжди вона була точною. (А мені, на відміну від типового програвача, потрібна ідеально точна перемотка.) Виявилося, що залишається загортати потік AAC в контейнер M4A. M4A як раз додає індекс, метадані тощо. От ніколи не думав про медіакодеки та медіаконтейнери, а довелося.
Та, о щастя, пісні в CBR M4A відтворюються так, як треба! База, про яку ніхто й не подумає, закріплена надійно.
-
Не сперечайся з LLM - вчи її
Коли людина робить щось не так, як тобі хотілося б, ти просиш її робити інакше, та людина запамʼятає це на наступний раз. (Ну, як правило.) От з LLM надважливо не запозичити цей підхід та не виправляти LLM, бо ти марнуєш час (та гроші) на покращення тільки поточного результату. В LLM антероградна амнезія, та інший раз вона зробить по-старому.
(Звісно, це ламає звички, бо ми ж наче спілкуємося з LLM як з колегою в чаті, привіт - будь ласка - дякую, імена їм вигадуємо. Тому і звертаю увагу. Називай молоток як хочеш, тільки не забивай ним шурупи.)
Отже. Коли LLM робить щось не так, як тобі хотілося б, найкраще це повернутися до початку та виправити інструкції. Взагалі тут два варіанти є.
Або ми погано поставили задачу. Треба прийняти, що це нормальна ситуація, ставити задачі - складно. Так, інколи можна вийти на правильне рішення продовженням діалогу: згенерували сайт - давай тепер задеплоїмо. А в інших випадках ти вже виправляєш помилки: я хотів окрему табличку, а ти додала стовпчик в ту, що існує.
Легше зрозуміти на чомусь побутовому. Якщо ти попросиш в LLM джинси та вона пошиє модні продрані, то вже пізно просити її зашити дірки. З кодом все так само - виправлення коду, що існує, залишає артефакти та зайві ускладнення. Це і без LLM завжди було так - звідки знайома мрія “переписати все з нуля”. Проте з LLM переписати з нуля легко - принаймні в межах однієї задачі точно. Та, між іншим, це ще й може бути дешевше за виправлення, бо продовження діалогу здорожчує роботу з LLM - ще одна “нелюдська” особливість.
Тож, покращуємо початковий запит - чи план - та починаємо наново.
Або LLM не знає про наші загальні вподобання. Я хотів логіку в сервісному шарі, а ти написала все в моделях. Чи я завжди роблю стилі з shadcn/ui. Чи процедурні питання: запускай тести після кожної зміни. Чи по стилю: я полюбляю функції до 20 рядків та винесення літералів у константи.
Коли до згенерованого коду зʼявляються такі претензії, ще більше варто не виправляти на ходу, а відкрити SKILL.md та написати, як любиш. Мене особисто така зміна рамки відновлює інтерес до роботи. Замість нескінченних виправлень одних та тих самих дрібних негараздів я розробляю комплект навичок. А це вже моя улюблена аналітична задача: помітити, що не так, узагальнити, знайти рішення. Ще й свої вподобання починаєш краще розуміти.
-
Код має значення — навіть коли його пише LLM
Та звісно ж, питання було риторичним. Тільки чому? Не тому же ж, що нам за нього платять, та страшно, що буде, коли LLM здатна видавати тисячі рядків коду на будь-яку примху. (Втім, сподіваюся, тобі насправді платять не за код, а за розуміння. Але про те не сьогодні.)
Отже, код. Поки ми працюємо з чистого аркуша та задача “щоб працювало”, дійсно, будь-який зміст коду нас влаштовує. Та це потужне місце для використання LLM! Знаєш, багато задач вкладаються саме в одноразову генерацію коду. Від команд та скриптів до цілих утиліт.
Я от тільки вчора згенерував з телеграм-каналу інформаційний сайт. За пів години. Код взагалі не бачив. Причому тут два шари згенерованого коду: спочатку скрипти для збору та впорядкування змісту, а потім вже власне сам сайт. Оце для мене чудовий — чарівний навіть — результат LLM.
Проте як тільки ми закриємо сеанс LLM, код стає джерелом істини. Ваш найдетальніший план не містить всіх подробиць. Код є остаточною специфікацією всіх подробиць реалізації. Всіх функцій, розгалужень, змінних та констант. Так саме як і люди, LLM читає код, щоб зрозуміти, що ж він робить. Та так саме LLM потрібно, щоб код був зрозумілим.
Тільки LLM зовсім не зорієнтовані на генерацію зрозумілого коду. Це за досвідом. Ну тобто я не кажу, що вони завжди пишуть поганий код — навпаки, здебільшого цілком прийнятний. Але задачі писати якісний код в LLM немає. Що можна помітити, коли щось йде не за її планом. Ось тоді починаються зміни характеру “тут підправимо, там посунемо”, а зовсім не про помірковані зміни.
Бо LLM більше “грає в хорошого розробника”, це буквально, що воно робить, бо це ж семантична папуга, розумієте? Вона не знає, як правильно, в неї немає сенсу смаку.
Тому у сталих, не одноразових проєктах я завжди читаю код, згенерований LLM. Та зазвичай вношу виправлення. Нудні виправлення, як-от “чому ця умова така довга” та “як ми можемо робити менше повторень” та “не насипай 100 рядків в цей модуль, зроби окремий”. Без цього я просто не можу підписувати продукт LLM власним іменем.
Я не вірю, в широкому сенсі, що можна обмежити власну увагу планами та специфікаціями та залишити шар коду суто для ШІ. Не виключаю, що певним чином можна переконати LLM писати гарний код, але для цього потрібно принаймні її для того інструктувати. Та якщо у тебе виходить, напиши, будь ласка, i am trapped in an LLM prompt please send help.
-
Чи має значення код, коли його пише LLM?
Знаєте трикутник “дешево, швидко, якісно: обери два”?
Я спостерігаю, що із використанням LLM вершина “швидко” роздулася безмежно та затьмила дві інші. Та ні, гірше, бо “дешево” ж теж стало так, як ніколи - які б ті токени не були дорогі, але праця розробника завжди ще дорожча.
От і виходить, що ми колективно обрали “дуже дешево” та “дуже швидко”… та пояснили собі, що на “якісно” можна не дивитися. Або, наприклад, що “воно працює” - це показник якості. Пʼять років тому теж траплялося, що “ось зробив за ніч величезну фічу, все працює… в код не дивись”. Я знаю людей, які здатні за вихідні переписати проєкт на іншу мову. Та й сам таке робив.
Але. Ніхто такі зміни не брав в продукт на віру! Яким би визнаним та досвідченим інженером ти не був, нормальна практика була почитати той код, та якщо він виявився погано структурованим та незрозумілим - попросити доробити, впорядкувати, і таке інше.
До речі. Раніше взагалі тільки круті інженери могли видавати код, що працює, з такою швидкістю. Тому, гадаю, раз LLM пише код ну настільки швидко, ми опиняємося під впливом гало-ефекту та високо ставимося до її розуміння теж. Втім, за досвідом, результат більше нагадує зграю мало оплачуваних, зате завзятих кодерів з прямим підключенням до Stack Overflow.
(От вам щеплення від гало-ефекту - ставтеся до агента не як до rockstar 100x developer, а як до найманої команди фрілансерів з біржі. Бо воно, бляха, так і є, якщо подумати.)
-
LLM для дослідження
В моїй роботі найбільше користі від LLM навіть не в написанні коду, а в дослідженні та операціях.
Ну тобто код воно генерує — проте тут зміни кількісні, не якісні. Ну швидше генерує, ніж я пишу. Ну можна через це згенерувати більше коду, як всі ми знаємо. Але в цілому, цінність коду відʼємна, та впровадження LLM ніяк це не змінило.
Зате із дослідженням… Якщо дати агенту достатньо інструментів - CLI, API, MCP - байдуже — то він настільки вправно збирає дані та аналізує, як я по-людськи ніколи не зможу.
Наприклад, я не зможу витягнути з CloudWatch метрики різної роздільної здатності, за різні проміжки, зробити кореляцію та видати табличку. Точно не за розумний час!
Я не зможу формулювати запити до OpenSearch, включаючи до адміністративних джерел, так швидко, щоб зводити результати з різних серверів у відповідь до одного запитання.
Я не зможу прочитати купу логів та знайти в них визначні місця — особливо коли я ще й не знаю, що саме шукаю, та в яких логах до яких сервісів. (Теж, може й зможу, якщо мені дати день тільки на це, та я не вигорю ментально від такого навантаження.)
А потім, все це зібрати до купи, скорелювати, підсвітити важливі місця, ще й зробити звіт відразу. В межах робочого дня.
Звісно, агент не розуміє всіх подробиць та нюансів, але на то є я та є діалог. Я направляю дослідження, а агент робить. І так я отримую доступ до стількох джерел інформації, які до LLM й уявити не міг.