Канал
У Льоні сьогодні
Нотатки архітектора систем та майстра на всі руки. Що я зробив, що з того вийшло, і що це все значить.
- ActiveRecord
- AmazonRedshift
- API
- AppleScript
- AWS
- AWSLambda
- Cadencia
- CGO
- Chezmoi
- CI
- Clojure
- Cloudflare
- CloudflarePages
- CloudWatch
- CssParser
- CтохастичнийТаймтрекер
- DNS
- Docker
- Dotfiles
- Fly.io
- GCP
- Git
- GitHub
- GitHubActions
- Go
- Golang
- GTD
- HomeAssistant
- Hugo
- I18next
- JavaScript
- Jira
- JSON
- Kafka
- MacMiniВДорозі
- MacOS
- Markdown
- Mastodon
- Obsidian
- ObsidianCanvas
- OmniWope
- OpenSearch
- Oura
- PagerDuty
- Ping
- Plausible
- PostgreSQL
- ReactNative
- Redis
- RSS
- Ruby
- RubyOnRails
- Sentry
- Sintra
- Slack
- SMTP
- SQL
- SQLite
- Svelte
- Swift
- SwiftData
- SwiftUI
- Telegram
- Terraform
- TLS
- TypeScript
- Vercel
- VPN
- WeightPlot
- WordPress
- XCode
- Адвент2024
- Бази Даних
- БДС
- Безпека
- Блокнот
- Вебтехнології
- ВолюІнформації
- Гаджети
- ДизайнМовПрограмування
- Ігри
- Інструменти
- ІнтеграційніТести
- Кава
- КеруваннняЗадачами
- Кодогенерація
- Криптографія
- Локалізація
- Маркетинг
- МетаПост
- МоїПроєкти
- Навігація
- Оптимізація
- ОсновиІнтернетБезпеки
- Помічник ШІ
- ПомічникШІ
- ПостПроПохід
- Програмування
- Продуктивність
- Проєкти
- Проза
- РДУГ
- Рівночасність
- РобочийКомп
- Розробка
- РозумнийБудинок
- СинПопросивПриготувати
- Сон
- СтохастичнийТаймтрекер
- Танці
- ХмарніТехнології
-
Визначення BPM силами користувача
В моїй програмі для музичності](https://cadencia.dance) багато завʼязано на темп пісні, тобто на її BPM. Практика показує, що автоматичні засоби визначення BPM помиляються. Окрім того, мені також треба знати, де “раз”, тобто де починається вісімка (бо, взагалі-то, як мінімум бачата та сальса для танцівників складаються з вісімок.)
Отже, розповідь про дві ітерації інтерфейсу для цього… з їхніми недоліками.
На початку я зробив варіант “тепай на 1”. Тобто ти задаєш послідовність “разів”, та з неї програма обчислює частоту тактів. Помилився — починай наново.
(Що робити в разі помилки — це одне з головних питань. Причому як до дизайнера, так і до користувача.)
Проблемою цього варіанту було те, що точок даних було замало. А значить, дрібні помилки в тепах сильно впливали на результат. Тож тепати доводилося довго… що дедалі збільшувало шанс помилки, бо не завжди легко той “раз” почути — дійдеш до якогось брейкдауну і сиди вгадуй.
Тоді я спробував зробити більше точок: тепати на кожний рахунок, а не тільки на “раз”. Будь-який танцівник вже вміє крокувати на кожний рахунок, тож, в теорії, тепати теж вийде?
Як би не було, а “тепати в рахунок” на досвіді виявилося незручніше. Впевненість субʼєктивно просіла помітно. Шанс на помилки теж зріс: бо тепер тепати треба не раз на 4 секунди (приблизно), а 2 рази на секунду.
Здається, тепер я зрозумів, у чому річ: чіткий рахунок не так вже й легко почути. Коли танцюєш, мати варіативність кроку не тільки дозволено, але й може бути музично та гарно. Підтримувати стабільний ритм — задача більше для барабанника, ніж танцівника.
Отже… що робити? Як отримати BPM від користувача — в ідеалі так, щоб навіть не називати слово BPM?
Успішною стала третя ітерація, але про неї завтра.
-
Захист від копіювання — це завжди лише ускладнення
Оскільки відтворення цифрового змісту будь-якого роду є, неодмінно, його копіюванням — технічно неможливо заборонити копіювання категорично.
До прикладу. У бабці був канал в популярному соціальному месенджері. Закритий. З обмеженням копіювання! На жаль бабця померла та не лишила після себе ключів. В нас стоїть задача зберегти бабцини світлини та відео поза межами платформи.
Можна шукати грубі шляхи — перезняти з екрана камерою? Відкрити у вебверсії, яка не може захистити від знімків екрана?
А можна замість того згадати, що в цього месенджера є не тільки Bot API, а й відкритий Client API. (Що, credit where credit is due, гідний та важливий крок.) З використанням цього Client API можна побудувати повноцінний клієнт месенджера, який вмітиме авторизуватися вже у твій обліковий запис, не бота — та матиме доступ до всього, що й звичайний клієнт. Буквально, до всіх функцій.
Ну а коли далі той клієнт буде не відтворювати відео, а зберігати на диск — то це вже його справа. Бо зміст в нього вже є.
Серйозні пани з кіностудій вже попіклувалися про це. Тому ми маємо HDCP та інші інновації, які обмежують, хто саме має доступ до змісту. (Ну й хіба воно працює?)
Ба більше, якщо раніше написати власний клієнт було справою зухвалих — а решті доводилося шукати екземпляри на сумнівних форумах — то зараз LLM тобі його згенерує за пʼять хвилин. Ще й під конкретну потребу: може каталог каналу згенерувати, чи перейменувати як треба тощо.
Величезний дисклеймер: тільки не розповсюджуйте закритий контент. Серйозно.
-
Пости про походи вертаються — завдяки LLM
В далекому 2023 році я витратив чимало часу, щоб зробити красивий пост про похід на Клаферкесель з анімованою картою та журнальним форматом. Витратив… і зрозумів: на другий такий в мене нескоро буде час. Зокрема підгодування карти та треків було рутинною роботою, та ще й вимагала серйозної математики. У 2025 я спробував щось відтворити, напоровся на баги з проєкціями та знову відклав.
Та ось… Цього разу взявся за справу із Курсором. І воно пішло! LLM легко порається з задачею “зроби мені карту по аналогії, але іншого місця” - тут я був впевнений.
А що здивувало, так це те що Курсор достатньо візуально обладнаний, щоб вручну налагоджувати проблеми з вирівнюванням текстур та розташуванням обʼєктів на карті. А проблеми були суттєві. От він брав, рендерив кадри з різними версіями, та перевіряв. Наприклад, наклав координати міст України на текстури Європи — та помітив, що міста не на місці. Серйозно вражає. Я гадав, що ми будемо робити це в парі — моїми очима — а потреби не було.
Та друга ручна задача: підчистити GPS-треки прогулянок для мапи. Та хоча б підрізати. Тож я скористався трюком: згенеруй мені редактор для того, щоб обрізати ці треки по довжині. І він згенерував! (Дивись ілюстрацію.) Та я за пʼять хвилин встиг закінчити цю роботу, жодного разу не дивившись в файл з координатами.
Або ще: локальна 3D-мапа в мене була квадратна, бо на це вистачило зусиль. Але кругла ж красивіше! Тож Курсор за один запит переробив квадратну на круглу (відредагував 3D-сітку, на яку накладаються текстури.)
Або ще краще: мені хотілося, щоб під час анімації на карті “їхав потяг”. Хтозна скільки б я це робив вручну, а тут встиг за годинку паралельно з іншими задачами. (А в старому пості тепер їде машинка з Відня, бо чому б й ні.)
А далі — вже робота над матеріалом: фотографії впорядкувати, тексти написати. Звісно, в цьому мені допомога не потрібна. Ну хіба що LLM швидше та точніше заміняє таку ручну роботу як “візьми ці пʼять фотографій та винеси в окрему галерею”.
Тож багато надії, що справа буде рухатися!
Результат ось: Що ми робили в Яремче. Краще дивитися на великому екрані, хоча для телефонів, я гадаю, ми з Курсором теж щось придумаємо, якщо піде. (Поки що я розробляв дизайн для моніторів, а на телефон лише підганяв, щоб мало хоч якийсь приємний вигляд.)
-
_id в OpenSearch - не так ефективно, як можна подумати
В продовження теми того, що розуміння однієї бази даних не завжди переноситься на іншу.
В OpenSearch (чи ElasticSearch, бо одно й те саме) в кожного документа є ідентифікатор -
_id. Наче зрозуміле явище, за ним ми швидко знаходимо та змінюємо документи. Так саме як в PostgreSQL, наприклад.Але. Якщо вам раптом захочеться відсортувати за
_id, то виявиться, що прямої можливості це зробити в OpenSearch немає. Словник того не підтримує. Та тому для виконання запиту з"sort": "_id"база завантажить всі ID в памʼять. Цей маневр називається field data.З мільйонами документів в індексі ця
fielddataзайматиме гігабайт RAM, а то й більше — саме так я про неї й дізнався. Та дуже дивно було, що саме_id, з усіх полів, спричиняє такі витрати.Що робити натомість? Можна дублювати
_idвсередині документу. Можна сортувати за якимсь іншим полем. Зокрема, якщо задачею сортування є стабільність видачі (бо навіщо ще сортувати за ID?) - то в пошуку є спеціальне поле_shard_docяк раз для такої потреби.Власне, в останніх версіях OpenSearch навіть є налаштування
indices.id_field_data.enabled: false- воно примусово заборонить створенняfielddataз_id. Тож то й таке. -
Вірний вибір каналу для сповіщень
Уявимо собі що ми робимо сповіщення про вибіг терміну TLS сертифікату. (А знаєте, краще не уявляти, а таке сповіщення мати точно — якщо так сталося що сертифікат не оновлюється автоматично.)
Ми розробники, звикли працювати з кодом та кидати помилки в Sentry. Тож робимо цикл-монітор, який перевіряє стан сертифіката та, якщо він наближається терміну, надсилаємо відповідну помилку.
Перші рази все працює. Минають місяці… як от прокидаємося вночі від тривоги на брак сертифіката. Йой, не подивилися вчасно в Sentry. А річ у тім, що Sentry це взагалі не місце для тривожних сповіщень! Так, певні помилки ми там побачимо. Але зазвичай в Sentry дивляться вже тоді, коли іншим чином виявили проблему — хтось поскаржився, наприклад. Тому як ти не фразуй сповіщення, а ставлення до нього буде недостатньо відповідальним.
Добре, тоді може направити повідомлення в канал Slack? Це краще для помітності, навіть надто гарно. Бо монітор, щоб бути помітним, почне бомбити канал постійно. Він же ж не знає, що ми побачили. А нам взагалі-то треба побачити одне сповіщення, відреагувати та зробити сертифікат — на що піде не один день. Ну таке.
(Робити власну складнішу систему, звісно, теж не хочеться.)
Можна зайти з іншого боку — дата вибігу сертифіката відома. Тож можна створити тікет в Jira з терміном. Це просто, але покладається на те, що хтось буде відповідальним та побачить той тікет. Що — для задачі, яка відбувається раз на три місяці та потребує неухильного виконання — мені не подобається. Потрібний механізм, що буде смикати за руку, і робити це надійно.
На цих думках я згадую про PagerDuty. Оце воно! А щоб сповіщення зʼявилося там, сервіс надсилає в CloudWatch метрику “залишок терміну дії сертифіката”.
І як тільки я бачу задачу у вигляді метрики, картинка складається. Бо тоді є чітка історія стану. Є вбудована у Cloudwatch обробка випадку “монітор зламався та нічого не надсилає”. Є готовий механізм сповіщень, та впевненість, що хтось побачить та підтвердить попередження. А головне, що це лягає в систему інших критичних сповіщень, тобто стає зрозумілим для команди.
(Нарешті, маленька порада про PagerDuty: в нас в описі кожної тривоги є посилання на посібник по її виправленню. Чудово допомагає, коли тривога будить тебе о третій ранку.)
-
Як я робив плейлист на гулянку разом з 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 чи без.