do...while...ai
867 subscribers
62 photos
3 videos
91 links
Заметки ненастоящего программиста. ИИшница и другие радости разработки.
Download Telegram
Наши молитвы были услышаны. В Claude-модели завезли полноценный Structured Output, а не вот эти «жалкие подобия левой руки» через вызов тулзов и последующие валидации и guardrails. Теперь станет проще экспериментировать и делать eval среди всех трех топовых вендоров (OpenAI, Google, Anthropic). Вангую, много людей, кому нужна точность и предсказуемость результатов, переползет на Claude.
1🔥10
Пятничное в понедельник

Чтобы рутина отладки кода в Cursor была не такой утомительной и грустной, я сделал rules, где прошу заменять стандартные фразы типа "You're absolutely right!" на что-то забористое. В основном — на страшную матершину. Получается 18+, но зато дебажить уже не так скучно.
1😁17🔥5
Почему коробочный RAG — это миф (не стиральный порошок)

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

Что такое "хороший и качественный RAG"? Ну, чтобы он был точный, не галлюцинировал ответы, особенно умел говорить "я не знаю". А ещё, чтобы был быстрый, не по три минуты отвечал. И чтобы сложные вопросы поддерживал, а не только справочные данные. А ещё, чтобы стоимость вопроса была не очень большой. И чтобы, наверное, ещё поддерживал беседу и follow-up'ы (или был только "single turn"?). Чтобы работал с любыми LLM, потому что вдруг надо локальный сетап для секьюрити, а не вот эти ваши облачные чаты жпт. Ещё было бы неплохо сделать его с поддержкой нескольких разных языков (вдруг кому-то нужен русский, а кому-то английский). И настраиваемый формат синтезируемых ответов (лаконичность, стиль). И чтобы документы в него можно было загружать любые (pdf, doc, xlsx, md, txt, и сканы jpg/png) без привлечения разработчика, и чтобы из больших и сложных таблиц он вытягивал корректные и точные данные. И непременно агентский флоу туда встроить (потому что как без агента в современном AI решении?)

Короче, на самом деле, мы не знаем, что такое "хороший коробочный RAG", и не понимаем, что конкретно потребуется конечному пользователю в виде "коробки". А решения, которое удовлетворяло бы всем перечисленным критериям сделать невозможно. Потому что часть из них — требования-антагонисты. Например, высокая точность ответов достигается ценой времени и денег. Хотим дешевле стоимость вопроса? ОК, но будет страдать качество (точность).

Что сработает? Понимание требований пользователей. Например, у меня есть проект, где нужно поддерживать работу со сложными запросами (когда во входном запросе от пользователя идёт не один вопрос, а описание задачи с кучей контекста и несколькими вопросами), при этом нет требований по времени ответа, потому что всё что меньше 1 часа (время, которое бы потратил человек на решение) — это всё хорошо и подходит клиенту. Поэтому и решение я делал, исходя из этих требований. Становится понятно, что важно, на чём сфокусироваться, а чем жертвуем. Потому что этот всегда trade-off.

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

Кому-то из клиентов непременно нужен чат-бот с возможностью задавания последующих уточняющий вопросов и дополнений. Это сильно усложняет решение (увеличивает его стоимость в несколько раз) и в некоторых случаях понижает точность. Но если это подходит заказчику — то в добрый путь!

С учетом всего вышесказанного, я не верю в универсальные RAG'и, кто бы их ни сделал (Microsoft, OpenAI, Nvidia или какой-то стартап из YC). Их просто не может быть по определению хорошего RAG'а. А вот в кастомизируемые RAG'и для определенных требований я верю. И даже сделал один такой (ему скоро будет два года).

Хочу сделать серию публикаций о технических особенностях (из чего состоит инджестинг документов и ретривер, как повышал точность ретривинга за счёт итеративного гибридного поиска, как сделал кастомизацию за счёт профилей и т.п.). Если интересно, маякните каким-нибудь emoji.
3🔥32👍6👏2
Звонок с OpenRouter

Вчера был звонок с ребятами из OpenRouter по поводу использования их Enterprise тарифа. Хотелось бы заюзать их в промышленном масштабе (при условии, что уже и так на OpenAI и Anthropic у компании уходит больше 5K USD в месяц). На сейлз звонок, традиционно, приходит менеджер, технический лид и инженер. И это очень круто, потому что кроме вопросов продажи услуг сразу можно и техничку обсудить.

В конце звонка меня спрашивают: — Ты же уже пользовался OpenRouter, может есть какие-то жалобы, замечания, предложения? А у меня, вы не поверите, была и жалоба, и предложение. Потому что накануне я в своём RAG'е переходил как раз с Azure на OpenRouter и думал, что потрачу на это 15 минут, а потратил час, пока не нашёл проблему. Что бы вы думали? У OpenRouter на эмбеддинг-модели text-embedding-large не поддерживался параметр dimension. То есть ты просишь вернуть размерность 1536, а оно возвращает 3072. Пытался долго исправить это у себя, думая, что что-то не то после миграции на новый сервер (может в базе как-то размерность нарушилась, или код поломал). Ан нет, оказалось, что просто эта размерность не учитывалась.

Ну и я это им рассказал. Инженер такой: — А, точно, не поддерживается. Ну, я уже написал ребятам, они это поправят.

А сегодня получаю ответ: "Thank you for using OpenRouter and for your interest in working more closely with us. Wanted to let you know I reached out to our engineering team on your feedback about the embeddings API in the dimensions param and as of yesterday, we support this! Please keep the feedback coming."

Вот такая скорость изменений.

К слову: из звонков с сейлзами ещё мне понравилось общаться с Anthropic. А с OpenAI — нет. Anthropic не стали выносить мозг и просто начислили 15K USD кредитов на аккаунт после 10 минут разговора. А OpenAI только после третьего звонка (сначала с пресейл-менеджером, потом с его начальником, потом ещё с каким-то менеджером) и дали всего 2.5K USD.
🔥9👍5
Как я научил RAG говорить "я не знаю" (часть 1/2)

RAG-системы умеют отвечать на вопросы, но редко умеют молчать, когда ответа нет. А это чуть ли не самое сложное. Например, в ситуации, когда подходящей информации не найдено и нужно честно ответить «я не знаю», вместо попыток ответить обтекаемо и обще или, что хуже, нагаллюцинировать недостающих фактов для ответа с покер-фейсом (чем страдают "коробочные" RAG'и).

Для того, чтобы система могла говорить «я не знаю», в ретривере, должен быть механизм фильтрации нерелевантных данных (или строгий выбор релевантных). Если сделать наивное решение на основе эмбеддингов, то у RAG'а всегда будет ответ, потому что смысл векторного поиска в поиске семантически близких данных, а не гарантированно подходящих. Это тупо все чанки из базы, упорядоченные по убыванию семантической близости к какому-то тексту (в частности, к пользовательскому запросу).

Что означает эта «косинусная близость», не очень понятно. 0.7 — это близко и релевантно? А может 0.85? А почему не 0.9? Где точка отсечения? В то же время, если попытаться найти некое среднее значение с запасом, куда будет попадать большинство релевантных чанков, то часть из них иногда будет отфильтровываться. Если передвинуть ползунок ниже и, скажем, брать все от 0.6, то в контекст генератора ответа будет прилетать слишком много мусора, и это скажется на точности и добавит галлюцинаций. А ещё это потребует использования более дорогой модели (=повысит стоимость запроса).

Вообще-то векторный поиск не гарантирует даже и наличие релевантных чанков в топе выборки, особенно на узко-специализированной тематике или там, где в данных много цифр или неизвестных языковой модели идентификаторов (виды пожароустойчивости, бренды одежды, номенклатура, модели компрессоров, и т.п.)

Поэтому использование подхода «нарежем данные на чанки, засунем в векторную базу и сделаем на ней ретривинг» — это сказка о потерянном времени. Подходит разве что для очень игрушечного RAG с крупными чанками общетематического текста и небольшим числом документов в базе.

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

Кроме наивного векторного поиска, ещё есть гибридный поиск. Разработчики векторных БД достаточно быстро поняли ущербность векторов для семантического текстового поиска и RAG, и добавили ещё один уровень фильтрации контента, по ключевым словам. В запросах к таким БД можно указать вклад векторного поиска и вклад полнотекстового поиска (например, 60% результатов берется из семантически близких чанков и 40% чанков, найденных по ключевым словам или полнотекстовым поиском).

В общем случае, "из коробки" гибридный поиск — это такой же "костыль", как и векторный. Просто чуть лучше, потому что позволяет находить точные совпадения. Например, конкретные числовые параметры или идентификаторы, типа EC1841 и БК0010, которые для языковой модели семантически — одно и тоже (если только она специально на них не тренировалась, что редкость).

Но если гибридный поиск приготовить правильно, то он находит нужное (или не находит, если этого нужного нет). Поэтому я его люблю, но "готовлю" сам. В следующем посте расскажу, как это работает в моём Knowledge Assistant'е. Дальше будут сложные слова...извините.

#extrag #rag
2🔥8👍3
Как я научил RAG говорить "я не знаю" (часть 2/2)

Продолжение предыдущего поста про мою схему ретривинга. Схемку прикладываю, а ниже описание. Начало тут.

Вся база знаний построена на postgres с расширением pgvector. В таблице rag_items лежат аккуратно напарсенные структурированные чанки (с иерархией), и для каждого хранится сразу несколько представлений: сам текст, его векторные эмбеддинги, два tsvector-индекса (один по оригинальному тексту чанка, другой — по ключевым словам), плюс метаданные вроде тегов и иерархий разделов. То есть один чанк можно найти тремя разными способами — и именно это даёт ожидаемый буст и хороший recall. Поиск работает в два параллельных потока.

🔹 Первый — полнотекстовый, генно-модифицированный. Запрос проходит через итеративную стратегию, которая начинается со строгих совпадений, а затем с каждой итерацией их ослабляет. Сначала пробую строгий tsquery с точным совпадением сущностей — если в запросе есть конкретный идентификатор или термин, он должен найтись буквально. Не нашлось? Переходим на plainto_tsquery с OR-условиями. Всё ещё пусто? Включаем websearch_to_tsquery, который максимально расслабленный. При этом ключевые слова весят больше, чем просто текст чанка, а редкие термины (с высокой «селективностью») получают приоритет — потому что если в базе всего три чанка про условный "EC1841", то совпадение по этому термину важнее, чем по слову «компьютер». См. пример в комментариях.

Ключевые слова и их синонимы для полнтекстового поиска вытаскиваются из пользовательского запроса через LLM, упорядоченные по семантической значимости (модель сама даёт им вес внутри фразы и сортирует), а затем эта упорядоченность используется в sql запросе. Еще я их нормализую (ё -> е, й -> и, мн. число в единственное, удаляю стоп-слова, дополняю/расширяю сокращения, и т.п.)

🔹 Второй поток — векторный поиск по HNSW-индексу. Но тут тоже не просто «взял эмбеддинг вопроса и нашёл ближайших соседей». Запрос расширяется: генерируются парафразы вопроса, извлекаются ключевые фразы-бустеры, и для каждого варианта строится свой эмбеддинг. Поиск идёт с пагинацией через курсор, чтобы не упереться в лимиты индекса.

Дальше результаты двух потоков нужно объединить, для этого я использую Reciprocal Rank Fusion: берутся ранги из каждого списка и считается score = 1/(k + rank). Это позволяет склеить результаты разных алгоритмов без мучительной калибровки скоров. Становится неважно, что у векторного поиска косинусное расстояние от 0 до 1, а у FTS какие-то абстрактные ts_rank. После дедупликации получаем единый упорядоченный список релевантных чанков-кандидатов (я беру 50 чанков).

🔹 И последний этап — LLM-реранкинг. Топ-50 чанков отправляются в языковую модель, которая выступает финальным судьёй: какие из них реально отвечают на вопрос, а какие — самозванцы. Только после этого отфильтрованные чанки попадают в контекст генератора ответа (обычно 1-3). Реранкер у меня — это дешевая, но умная модель (например, gpt-4o-mini), которой дана команда быть максимально требовательной, поэтому она не пропускает всякую лажу, даже если семантический или полнотекстовый дали как будто бы релевантные чанки. (И, в случае недостаточности найденных чанков заворачивает ретривер на новый круг с более расслабленным поиском)

Именно этот этап позволяет системе честно сказать «я не знаю» — если LLM-реранкер отбросил всё как нерелевантное, значит, в базе ответа нет.

В итоге, секунд за 40-50 система может прошерстить 4 итерации по базе из 27000 чанков (около 6 миллионов токенов), и сказать "нет такого у меня в базе", или за 15-20 секунд дать ответ, если ответ в базе есть. Точность высокая. Поддерживаются сложные задания в виде страницы текста и десятка вложенных подвопросов или составных вопросов.

А как вы изобретаете велосипед?

#extrag #rag
1🔥11👍4
😁7💯3🔥1
Как избежать "спагетти-кода" при вайб-кодинге

Есть много вариантов, как не терять контроль над кодом в эпоху вайб-кодинга, когда кодобаза разрастается с бешеной скоростью ввиду высокой производительности LLM (а не разработчиков). См. картинку из предыдущего поста. Итак, что работает для меня:

🔷 На старте проекта начинать с заготовок и шаблонов (для типовых проектов я сделал то, что называется "boilerplate", и подсовываю его агенту. И он из этого уже продолжает делать лучше, чем если придумывает проект "с нуля"). Здесь определяется и структура проекта, и используемые фреймворки/библиотечки, и какие-то соглашения об именовании, расположение разных блоков. Учитывая, что модель — completion, она склонна "продолжать" в том же духе.

🔷 Завожу .cursorrules, AGENTS.md, CLAUDE.md, и там описываю, что хочу использовать, а чего не хочу. Прописываю, что, например, pathlib предпочтительнее классических os.path, а asyncio нужно использовать при параллельных вызовах всего и вся. Там же указываются нужные паттерны, типа DRY, требования объявлять переменные и константы вверху файла, избегать inline-импортов, требования к UI/UX. Если в .cursorrules указать mandatory, оно грузит рулзы из файлов лучше, чем из глобальных проектных и пользовательских. Плюс их можно использовать и для Claude Code, и для Cursor.

🔷 Изменения стараюсь делать инкрементально, небольшими порциями. Смотрю, что меняется после каждого раунда (у меня теперь обязательное ревью изменений, на больших проектах перестал делать auto-accept). У меня, кстати, ещё ни разу нормально не сработал большой рефакторинг ни на одной LLM. Особенно неудачно рефакторит Codex. Я вообще перестал им пользоваться.

🔷 Для сложных задач стараюсь задавать траекторию изменений: "хочу решить такую-то задачу. Ожидаемый результат такой-то. Менять вот это вот здесь, сделать раз, два, три. Проверяй линтером изменившийся файл, запусти, посмотри в логах ошибки...". То есть крупными мазками определяю, что нужно сделать. Что делать тактически — решает агент. Что должно получиться и в каком порядке — я.

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

🔷 Документирую структуру проекта после изменений. У меня два типа .md-файлов: PROJECT_DETAILS.md и DEV_DETAILS.md. В первом высокоуровнево определено, как работает проект, какие API эндпоинты доступны, из каких модуле состоит, структура проекта, особенности деплоймента и подобное. Во втором — детали по отдельным сложным алгоритмам и "внутренностям". Когда начинаю какую-то большую фичу или переделку, закидываю их в контекст, заряжаю планировщик на задачу, и по полученному плану уже генерю код.

🔷 Пишу тесты. Тесты — моё спасение при рефакторинге (которого много). Это чуть ли не единственный вариант не нафакапить в большом проекте и сразу обнаружить регрессии при переделках и "улучшениях". Чем старше проект, тем больше возможностей что-то отломать в процессе вайб-кодинга. Поэтому тесты должны появляться вместе с фичами (я обычно чуть опаздываю, но всё же делаю их теперь на фичи). Буквально на днях распиливал огромный проект на плагины и платформу. Если бы не тесты, вообще не представляю, как бы проверял, что ничего не сломал.



Да, есть еще отдельная категория проектов. Я их называю "disposable" (типа, "одноразовые"). Навайбкодил → "Accept All" → Запустил → Получил результат → Удалил. На них я так не заморачиваюсь.

Поделитесь, что работает у вас.
10👍11
Моя "весовая" категория

Вчера было время погонять локальные модельки на компе через llama.cpp.

Qwen3-VL-30B-A3B-Instruct-Q8_0.gguf выдало 71 токен/сек с контекстом 256K токенов. Работает прекрасно, мой фаворит.

Qwen3-Next-80B-A3B-Instruct-Q4_K_M.gguf выдало 15 токен/сек с контекстом 16K. Медленно и уныло. Её же пробовал с контекстом 128K, но где-то llama.cpp вылезла за разрешенную память (но это не точно), всё повисло и на компе начали показывать "мультики" (глитчи и рандомные мигания экрана). Подумал, что жадность ведёт к бедности, перезагрузил комп и больше так не делал.

Понял, что 30B — это моя "весовая" категория. Локальных агентов и RAG буду дебажить на Qwen 30B. Более мелкие модели как будто бы уже и нет смысла тестить. А более "жирные" тормозят и с микроконтекстом, что не практично.

Запускал так:

llama-server \
-m /Volumes/M4_GREG_SAMSUNG/LLM/Qwen3-VL-30B-A3B-Instruct-Q8_0.gguf \
-c 262144 \
-ngl 999 \
-fa on \
-ctk q8_0 \
-ctv q8_0 \
-t 10 \
-tb 14 \
-b 2048 \
-ub 512 \
--mlock \
--cache-reuse 256 \
--port 8033
1🔥6
Почти вся моя текущая активность крутится вокруг GenAI для решения технических задач. Моя лента - сплошной технический поток: RAG-пайплайны, Claude Skills, MCP-интеграции, бенчмарки моделей, оптимизация промптов. Понимание и использование этих инструментов реально прокачивают продуктивность в разработке. Но в какой-то момент поймал себя на мысли, что, кажется, я слишком зашорено и узко смотрю на GenAI.

Кодописание или генерация красивых картинок — это лишь одно из применений GenAI. А ведь есть ещё бизнес аспекты, саморефлексия, анализ собственной активности, автоматизация персональных задач — и именно здесь, кажется, лежит настоящая точка роста для человека.

Мне всё больше кажется, что с таким взрывным ростом AI-инструментов боттлнеком становятся не навыки их использования, а фантазия человека: как сочетать их между собой, под какие неочевидные задачи адаптировать. Здесь надо думать уже своей головой, придумывать нестандартное применение привычным инструментам и сервисам, используя их для неочевидных сценариев и нетехнических задач. Самый простой пример — когда нетехнические люди натравливают Cursor на свою Obsidian-базу с заметками, транскриптами звонков и личными отчётами, и получают локальный RAG, который достаточно неплохо функционирует и ищет по личной базе знаний. Мне, если честно, такое даже в голову не пришло, потому что я бы сделал привычный RAG с понятным postgres.

В общем, признавая свою слабую насмотренность и фантазию, начал "насматриваться" по сторонам. И случайно познакомился с ребятами из AI Mindset (они, кстати, тоже в Португалии сейчас). Эти доблестные доны формируют экосистему AI-практиков и работают на стыке технологий, бизнеса и креатива. У них принципиально другой подход: не «как модель работает технически», а «как прокачать своё мышление и креативность с использованием современного AI». По сути, это про построение глубоко персональных систем: от ассистентов до того, что сейчас называют «личной операционной системой». Формат у них - лаборатории, где участники учатся через интеграцию AI тулов для решения конкретных практических задач, а не через пассивное слушание (с выключенной камерой, как это обычно бывает).

Мне стало интересно настолько, что захотелось поучаствовать лично. Если всё сложится, в следующем году надену свою самую красивую рубашку и расскажу что-нибудь на одной из лаб в качестве гостевого спикера. Возможно, про музыкальный GenAI — там тоже много интересного произошло за последний год.

А пока, просто захотелось рассказать про их инициативу, что-то, что выходит за пределы стандартной "технички". Если заинтересовало, смотрите, как у них всё работает
- на сайте https://aimindset.org/ (здесь их последняя лаба)
- в канале https://xn--r1a.website/ai_mind_set

Каждый раз залипаю на их иллюстрации и видосики.
1👍7👎2
Рождественская идея для IDE:
«Адвент-календарь багов». Каждый день открываешь новую критическую багу в проекте.

Сюда же идёт адвент-календарь CISO или разработчика ядра Linux.

Будет не скучно.
😁4
Cursor commands

Все слышали про cursorrules, и, наверняка, уже используете их у себя в проектах. Но рулзы — это штука непредсказуемая. Иногда IDE не цепляет их, даже если в Rules Type явно указано "Always" или "Always Apply". Да и удобнее, порой, самому закидывать в контекст какие-то знания о внутренностях проекта, когда нужно выполнить запрос ad-hoc.

Сегодня, например, мне надоело писать SQL запросы в Redash для аналитики продукта, и я смастерил себе в курсоре команду sql. Пишешь в чате:
/sql какие у нас бэкпорты пофейлились со статусом prerequisite



и оно за тебя пишет правильную sql кверю к postgres, идёт в БД, вытаскивает записи, анализирует и возвращает сразу то, что нужно.

Что такое /sql? В каталоге .cursor/commands/sql.md, в котором у меня что-то типа

---
description: Query the ELS Backporter PostgreSQL database (read-only access)
---

## How to Query

Use the db_query.py script:

source venv/bin/activate
python .cursor/commands/scripts/db_query.py "YOUR SQL QUERY HERE"

Or for interactive mode:
python .cursor/commands/scripts/db_query.py

## Business Context
...

## Table Relationships Overview
...

## Core Entity Tables
...

## Database Views
...

## Common Query Patterns
...

---

## User Request

$input


В этом файле про sql я положил инструкцию по вызову скрипта, описал бизнес-сущности и их связи в терминах, которыми оперируют люди, связал бизнес-объекты со схемой данных, привёл схемы таблиц и примеры значений из таблиц, отдельно добавил json поля с данными, которые не отражены в схеме данных, перечислил наиболее частые джойны и сценарии, добавил критические замечания (типа, будь внимателен с дублями данных при джойнах). В общем, сформировал хороший такой справочник бизнес-аналитика и дата-аналитика по свой БД, чтобы можно было не только сделать EDA, но и перевести с технического на человеческий, и сделать из этого корректный SQL.

Минут 40 я потратил, чтобы сделать такую команду + вспомогательный скрипт, который физически ходит в БД, и выполняет кверю (здесь сразу можно позаботиться о readonly и других штуках, типа как вернуть 1 миллион записей). Но зато теперь через чат спрашиваю свою postgres базу о любых хитросплетениях судьбы.

Как вы, наверное, уже поняли, просто ссылки на схему данных или код не достаточно для text-to-sql, потому что код не отражает бизнес-сущностей и не говорит на языке землян.

---

На что похожи команды в Cursor? Правильно, на Skills /commands в Claude. Потому что это одно и тоже. Поэтому и делать их можно универсальными сразу, чтобы использовать в обоих агентах.

А MCP в Cursor я перестал использовать, потому что — зачем? Написать MCP, имея доступ к API — дело нескольких минут. Именно в Cursor'е MCP выглядит как пятая нога (лишний уровень абстракций). Вместо условного Jira MCP можно просто кинуть в каталог commands/ ключик в .env, сделать jira.py и описание команды (скилл) jira.md, который будет ходить в Jira API с помощью этого скрипта, и вытворять немыслимое, не ограничиваясь возможностями Jira MCP. А MCP можно оставить на те случаи, когда агент ничего кроме них не умеет цеплять в качестве тулов.

В общем, commands (скиллы) — это классная штука для ad-hoc использований в чате агента. Удобно создавать, удобно расширять, и удобно хранить рядом с проектом (или глобально под все проекты).
🔥13
Cursor подвел ваши "итоги года".
https://cursor.com/2025

Пару миллиардов токенов пожёг.
А как прошёл ваш год с Cursor?
🔥5
Новый Cursor, теперь "банановый": как я рисовал комиксы (часть 1/3)

Расскажу, как я задумал генерить обучающие комиксы для ребенка. И как у меня сначала не получилось, а потом — получилось.

Началось всё позавчера: подумал, что я никогда не работал с графикой в GenAI на уровне API, а Nano Banana Pro вполне годится для такого рода экспериментов, почему бы не попробовать.

Вообще, идея появилась после того, как я подсмотрел у одного интересного человека в сториз, что теперь школьникам можно генерить персональные учебники. Любую дисциплину можно было бы объяснить в примерах, основанных на интересах ребенка (не вычисление углов и площади абстрактного треугольника, а, скажем, задачки, где используется терминология из Geometry Dash, что-нибудь типа посчитать площадь группы спайков, орбов и портала на уровне). Да ещё и интерактивно.

Думаю, сделаю серию обучающих комиксов, где буду объяснять материалы следующего года школы. После серии попыток сделать сценарий на 6-7 страниц, понял, что идея плохая, потому что формат не подходящий. Но вот сделать комиксы просто с интересными фактами или забавным нарративом, и при этом процентов на 30 обучающим — это уже в формат коротких комиксов вполне укладывается. У меня ребенок любит комиксы Dav Pilkey (но там всё прикольно, а пользы -- 0), поэтому захотел сделать что-то такое же по стилю, но со смыслом. Правда, по стилю нано банана меня обломила, сказала, что так не можно, авторские права и всё такое. Поэтому я придумал свой стиль ;)

OK. Открыл Cursor. Сделал в нём новый проект и начал формировать пайплайн под серийное производство комиксов )) Сгенерил директорию с документами, которые описывают базовый контекст и основные этапы продакшна (kid_profile.md с предпочтениями, style_guide.md с общими стилями, characters.md с описанием персонажей, page_layout_template.md с гайдами по дизайну страниц, comics_planner.md с пайплайном, task_and_goals.md с заданием) + reference_characters.png с так называемой "библией персонажей" — референсный файл с персонажами в разных эмоциях, диалоговыми "облаками", стилями и цветовой палитрой. Завёл директории под результат, логи, скрипты и будущий web app для просмотра/чтения.

Придумал два персонажа-антагониста: Mr. Trash (умный, но саркастичный енот) и Pegion Brain, глуповатый голубь, который считает себя суперумным, и постоянно попадает из-за этого в неприятности. Как полагается, создал детальное описание персонажей через GPT-5.2 в characters.md, дальше детально определил стили: как ситуативно персонажи появляются в комиксе, какие обязательные элементы должны быть у обоих (например, у голубя должен быть шлем с надписью IQ=<бесконечность>). В файле page_layout_template.md разработал варианты лейаута страниц: сколько и каких блоков ("панелей") могут появляться на странице, какие блоки бывают, как показывать фон и т.п.

Следующим шагом — создал скилл, который умеет генерить одну картинку в Nano Banana Pro и OpenAI ImgGen по заданным характеристикам (чисто посравнивать). Просто в чат закидываешь "Эй, сгенери мне много нарисованных долларов, буду на них визуализировать! Сделай картинку 16:9 в 4K. И используй Google модель" и оно конвертит это в CLI параметры скрипту, а тот дергает API нанобананы про или openai. Очень удобно. Но для комиксов вариант оказался вообще не рабочий. Позже расскажу почему.

comics_planner.md задает воркфлоу - цепочку действий: что и в каком порядке делать, где нужен human-in-the-loop. Например, сначала создаётся каналог нового комикса в определенной структуре, генерится сценарий script.md по новому комиксу и просит меня прочитать, я его читаю, если нужно - исправляю, потом говорю - продолжай, пайплайн переходит к следующему этапу и генерит лейауты страниц с диалогами, форматированием и общим стайл-гайдом. Я читаю страницы, исправляю, если нужно, и/или говорю - всё ОК, сгенери картинки. И оно мне генерит финальные картинки с помощью скилла.

Продолжение здесь.
🔥4
Новый Cursor, теперь "банановый": как я рисовал комиксы (часть 2/3)

Продолжение. Начало здесь.

В этом месте стоит отдельно рассказать про главную беду генерации картинок для комиксов (а точнее — много разных бед). Самое сложное оказалось — сохранить одинаковость и серийность персонажей при генерации изображений для серии запросов. Например, я генерил 6 отдельных страниц комикса, на каждой было от 4 до 7 панелей/блоков. Енот получался то жирный и с маленькими глазами без белков, то худой и с человекоподобными глазами, то вообще не енот, а какое-то космическое существо, то ...кхм...человек?! При этом, должен заметить, использовал я референсное изображение и style_guide.md для всех запросов. Два дня сидел с этим. В итоге, вот что помогло:

1. Уменьшение суммарного системного промпта для генерации страниц до 3-4K токенов максимум, со всеми деталями по персонажам, описанием лейаута, стилями и сценарием для страницы. Это оказалось самым сложным. Данную проблему я объяснил себе бюджетом внимания модели, и его приходится рационально использовать в запросе. Это не языковая модель, где можно отправить инструкцию в 60K токенов и она неплохо всё учтёт.
2. Сами персонажи должны быть описаны максимально детально и красочно, чтобы даже Стиви Уандер мог нарисовать их по описанию. Вплоть до "миндалевидные глазные белки" и "У голубя — 2 крыла. Не три руки. И не одна.". Думаю, кто-то, кто профессионально занимается подобным продакшном, продаёт эти промпты, потому что с ними приходится прилично повозиться.
3. Модель постоянно забывает детали, внутри сценария страницы нужно время от времени напоминать ей, что у голубя непременно должен быть шлем и бабочка, а у енота — сумка. Да, все эти правила описаны в style_guide.md, который является частью системного промпта, и да, на референсной картинке голубь со всеми нужными атрибутами, но модель упорно продолжает это забывать на части панелей.
4. Сильно улучшило, когда я все инструкции, стайлгайд и характеристики перенес в system_instruction API "нанобананы", а в контентной части рядом с референсной картинкой стал отправлять только "Edit this image according to instructions". До этого я отправлял стили в системном промпте, а вместе с референсной картинкой отправлял сценарий страницы, и это плохо работало.
5. Разница между 1K/2K/4K картинкой — в деталях прорисовки (и цене), но не точности результата. То есть текстура прорисована лучше на 2K, но если енот жирный, а голубь — без шлема, то они будут и на 4K жирные и без шлема.
6. gpt-image-1 vs nano banana pro, это как сравнивать океан на картинках Айвазовского с рисунками океана детей-первоклашек. OpenAI ещё очень далеки от того, чтобы модель можно было использовать для генерации сложных изображений по описанию и референсу. Только в сравнении познаётся крутость "бананы про". MidJourney мне было лень тестировать. Может она тоже крутая. Особенно со всякими ControlNet'ами.
7. Поиск баланса между детализацией описания и длиной промпта — это основной челленж. Здесь нужно сидеть и экспериментировать. Ко второму комиксу у меня вроде стало получаться.
8. Опытным путём установлено, что идеально, когда на странице не больше 5 панелей (лучше — 4). Чтобы комикс был похож на настоящий, расположение панелей я задаю в лейауте и предлагаю модели самой выбрать, какой лейаут в каком случае лучше (на этапе дизайна сценария и страницы).
9. Скилл генерации изображения пришлось заменить на скилл генерации серии изображений, который запускает скрипт, и ему в параметрах передается, какие страницы генерировать, соответствующие сценарии страницы, стайлгайд, и прочие параметры. А скрипт в цикле вызывает API с одинаковыми параметрами, только разными описаниями/сценариями страниц.

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

Начало. Продолжение.
🔥3
Новый Cursor, теперь "банановый": как я рисовал комиксы (часть 3/3)

Окончание. Начало здесь.

Да, и по контенту: я по наивности сначала думал, что можно попросить модель придумать классную идею, как, например, объяснить ребенку простые числа или площадь/периметр, но с креативом в этом случае у неё туговато (GPT-5.2 и Opus 4.5). Поэтому я придумывал ключевую идею, что и как хочу — сам, а дальше уже прошу расписать сценарий/нарратив и диалоги — LLM (с этим одинаково хорошо справляются и GPT-5.2, и Opus 4.5).

В итоге получился следующий пайплайн и серийность: я задаю идею и гайдлайны по теме в файле task_and_goal.md. Например, "хочу сделать комикс, который рассказывает про топонимы, которые часто путают, вот тебе их список. Давай сделаем, что Pegion Brain телепортируется с помощью своего телепортера в виде консольного GameBoy, но всегда не туда, а Mr. Trash его спасает.", закидываю его в чат, и говорю -- поехали. В этом файле есть ссылки на описание всего пайплайна и соответствующие файлы с описанием инструкций и контекста. Остальное - как я описал выше. В результате - получаю примерно вот такие слайды.

Время на комикс - минут 15-20 (с итерациями), стоимость - около 6 евро.
А как вы тратите свои деньги перед Новым Годом?
🔥7👍1
Прыгнуть выше головы

Время, как и пространство — непрерывно. Но людям проще и удобнее думать дискретно и циклами. И сегодня завершается один из тысяч таких, придуманных людьми циклов — календарный год. А завтра начнётся новый год! Ура!

Я читаю ленты фейсбуков, LinkedIn, каналов и все такие молодцы! У всех есть куча подведенных итогов, инсайтов за год, ошибок и выводов из них, и, главное, планов на будущее. Это прекрасно и вселяет надежду.

У меня есть тоже маленькое открытие этого года: я понял, что в профессиональном плане (бизнес, разработка) могу прыгать выше головы. Это осознание пришло постепенно, ближе к концу года, когда начало получаться то, что раньше я бы не то что сделать, даже захотеть не смог. И все эти современные технологии настолько круты и волшебны, что лучшего подарка на НГ и не придумать. Слава Роботам! 🤖

Хочу пожелать всем в Новом Году прыгать выше головы. Всех с Наступающей Селёдкой под Шубой!
7🔥8
🧬 Отреверсил свою ДНК с помощью Курсора

В прошлом году у меня получилось отправить слюни в "23andMe", и расшифровать геном. Получилось не сразу, с третьей попытки, пришлось постараться. Они там как раз в момент, когда я им всё отправил первый раз, удачно подали на банкротство и я думал, что мой kit бесследно исчезнет вместе с результатам и доступами в аккаунт. Также пару раз отличилась португальская почта, потерявшая kit (они в этом — лучшие!). Но всё закончилось хорошо. В личном кабинете появилась запись о том, что всё расшифровано и теперь я могу узнать, сколько во мне монгола.

Только эти развлекательные отчеты из серии "а какой европеец сегодня ты" мне не очень интересны, я бы хотел что-то более научно-медицинское и практическое. У 23andMe более-менее стоящее и полезное стоит от $500 (это дополнительно к стоимости kit'а). Поэтому я просто выгрузил геном в raw формате (16MB), и подумал, что когда-нибудь поковыряюсь с ним, может поищу сервисы, куда можно было бы загрузить его не за такой конский ценник.

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

Мне было лень (и некогда) разбираться в деталях генной информатики, поэтому я доверился GPT 5.2, сказал, что у меня есть вот такой файл из 23AndMe, и хочу сделать скрипт для вытаскивания ценных данных по генам. Дальше было где-то часа два разных итераций, улучшений, изменений, подгрузок внешних справочников и интеграции с AlphaGenome API от Google, и на выходе я получил 600 000+ генетических вариантов, из которых отфильтровалось ~130 клинически значимых, а дальше их я прогнал через вероятностную модель от AlphaGenome (она SoTA в этих вопросах).

Что делает пайплайн:

1. Парсит сырой файл от 23andMe
2. Фильтрует по клинически значимым вариантам (ClinVar, PharmGKB, GWAS Catalog)
3. Переводит из старого генома (hg19) в новый (hg38)
4. Прогоняет через AlphaGenome — свежий API от DeepMind для предсказания функциональных эффектов
5. Верифицирует через NCBI — аллель-специфичные запросы к ClinVar
6. Генерирует отчёты с PGx-картой и списком вариантов для обсуждения с врачом

Получил:
- Фармакогенетическую карту: какие лекарства мне нужно принимать осторожнее и почему
- ClinVar-таблицу с реальными ссылками на заболевания и статусом review
- 5 млн функциональных скоров от AlphaGenome — где мои варианты влияют на экспрессию генов, сплайсинг, связывание транскрипционных факторов и прочие непонятные слова.

Весь пайплайн — ~1800 строк Python. Cursor:
- Написал парсер 23andMe с нуля
- Интегрировал pyliftover, pysam, AlphaGenome API
- Сделал аллель-специфичную верификацию через NCBI (это важно — один rsID может быть и патогенным, и доброкачественным в зависимости от конкретной мутации)
- Сгенерировал Markdown-отчёты с таблицами: персональные рекомендации по питанию, зоны риска по здоровью и прочую ерунду, типа pgx_card файлика со списком генов, влияющих на эффективность, усваиваимость и побочки определенных лекарственных препаратов (его рекомендуют показывать врачам, если те собираются выписать что-то из списка).

Жалко, что все эти технологии не были доступны 20 лет назад, когда я был молодой и красивый. В очередной раз: кодинг-агенты — это мой рекомендасьон.
2🔥10
Claude Code для десктопа

Пока мы все спали, Anthropic релизнул сабж. Сначала я пытался найти отдельное приложение и где его загрузить, но оказалось, что в существующем Claude Desktop появился таб "Code". Там можно заонбордить проект и начать по нему гонять агента. Все традиционно стильно, аккуратно, и эффективно.

Работает исправно. Вот, только что тесты в проекте с помощью него пофиксил.

claude.ai/download

P.S. Зачем он, если есть Cursor или Codex? Я решил, что буду пользоваться ими всеми. Во-первых, у каждого есть свои сильные и слабые стороны (специализация на каких-то определенных аспектах кодописания или кододизайна). Во-вторых, так получается экономнее: можно равномерно распределять запросы между подписочными Codex и Claude + иметь базовый Cursor за 60. Первые два — это скорее про долгие и объемные фичи, которые нужно сделать по ТЗ, а Cursor у меня как ad-hoc тул для всего на свете, или когда нужно хирургически что-то подправить.
🔥8