❌

Normal view

История одной IT-разработки: как мы создали софт по рассылке деловых писем

Всем привет! Я Дмитрий Шихаев, маркетолог для IT-компаний. Сейчас работаю с крупными IT-заказчиками, помогаю выводить технологические продукты на рынок и развиваю направление ABM-маркетинга в России.

Но путь к этому был далеко не сладким. Когда-то мы с товарищем Гошей, у которого за плечами были базовые знания BASIC и опыт работы с конструктором BAS, решили практически без денег и опыта создать собственный софт для массовой отправки качественных персонализированных деловых писем.

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

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

Читать далее

Фотосессия стоматологов без фотосессии за $3: FLUX.3 klein + ComfyUI + RunPod = 290 кадров

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

Читать далее

Старый софт на свежем Linux: свой GCC, свой OpenSSL и честный RPATH

А что если… нужен старый софт, но при этом хост Linux свежий?

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

Казалось бы, проблема решена. Но есть нюанс (с). Добро пожаловать под кат.

Читать далее

Пайпы OpenWebUI: зачем писать код там, где «всё уже есть в настройках»

Настроить кастомную модель с инструментами и RAG в OpenWebUI — легко. Заставить её работать предсказуемо в бою — сложнее. Наш бот техподдержки на Gemma 4 сначала отвечал правдоподобно, но без данных из систем. Мы разобрались, почему это происходит, и превратили «одну модель в интерфейсе» в детерминированный конвейер с помощью пайпов.

Читать далее

В логе было «не удалось получить данные:» и дальше пустота. Это и была вся ошибка

В логе моего сборщика данных годами появлялась строка «не удалось получить данные:» и дальше ничего. Я считал это багом форматирования, пока не посмотрел: код написан правильно, просто у asyncio.TimeoutError пустой str(). Дальше выяснилось интереснее. Рядом лежит хелпер с тремя попытками и паузой в пять секунд, написанный ровно под сетевые сбои, а ловит он except aiohttp.ClientError. Начиная с Python 3.11 asyncio.TimeoutError это встроенный TimeoutError, наследник OSError, и к иерархии aiohttp он отношения не имеет. То есть единственная ошибка, ради которой ретраи существовали, пролетала мимо них. Причём у aiohttp есть и свои таймаутные исключения, которые как раз ClientError, и именно это сбивает с толку. Заодно разбираю, как я на основе этих пустых ошибок записал в заметки несуществующее «сетевое ограничение VPS» и построил вокруг него архитектурное решение.

Читать далее

Бэкап SQLite прошёл проверку. Второй заказ в него не попал

Копия SQLite открывается, контрольная сумма совпадает, integrity_check отвечает ok. А подтверждённого заказа в ней нет. Разбираю результат десяти локальных прогонов: где осталась запись, почему проверки её не заметили и как принять резервную копию по содержимому. Полный проверенный скрипт — внутри.

Читать далее

Сколько миллисекунд без читателей нужно SQLite, чтобы файл -wal перестал расти

Документация SQLite говорит, что -wal перестаёт расти, только если у читателей, которые видят кадры из -wal, бывают общие паузы, но не говорит, какой длины. Я замерил в контейнере на SQLite 3.46.1: на моём стенде хватало единиц миллисекунд — 2 мс при 50 коммитах/с, 5 мс при 1000/с, и быстрой записи понадобилась пауза шире. Без паузы три читателя по 50 мс раздувают -wal так же, как один на 20 секунд, а случайные запросы по 5 мс почти не мешали, пока база была свободна хотя бы 13% времени.

Читать далее

Глобус в терминале: Земля из символов Брайля и реальная смена дня и ночи

Представьте, что в уголке вашего терминала висит глобус Земли, и ночь на нём наступает там же, где за окном. Я написал такую программу за два дня. Она загружает карту мира, обтягивает ею сферу и отображает её символами: точками шрифта Брайля, значками Юникода либо буквами. Шар вращается мышью и раскрашивается одной из десяти палитр. Помимо Земли утилита показывает Солнце, Луну, все планеты и Цереру. Ставится одной командой, без Python и зависимостей.

Читать далее

Как построить self‑hosted RAG‑систему с LangGraph‑агентом на локальных LLM

Обычно стек для RAG‑систем и AI‑агентов строится вокруг облачных API (OpenAI, Anthropic) и управляемых векторных баз. Но для ряда задач критически важна полная автономность: устойчивость к санкциям и независимость от внешних провайдеров.

В этой статье я расскажу, как спроектировал и собрал аналитическую систему, которая за две недели работы агрегировала более 12 000 новостей: от сбора RSS‑лент до умного агента, который сам вызывает инструменты. Мы пройдем путь от архитектуры до граблей, на которые я наступил.

Стек: Python 3.12, FastAPI, PostgreSQL + pgvector, Celery, LangGraph, Ollama (Qwen 2.5 14B, BGE‑M3), Streamlit, Langfuse, Docker.

Читать далее

Остатки нельзя обороты

IT драма с элементами комедии

Фриланс манит свободой — к черту офис! Теперь ты сам себе начальник! Однако свобода эта обманчива. Вот вроде сидишь ты, работаешь дома, тебе тепло и сухо, и начальства над тобой нет, и никто не мешает, ты один... совсем один. И отвечаешь за все ты. А время уже далеко за полночь, А код выкидывает исключения, а Django ORM отказывается вывозить нагрузку, а все варианты архитектуры сулят скорее минусы, а таймер биржи неумолимо тикает. Тик‑тик‑тик... И нужно уже что‑то решать, но помочь некому — психов нет.

Читать далее

Когда Python становится слишком медленным: пишем C‑расширение с нуля и сравниваем с pybind11 (бонус JIT‑компилятор)

Недавно я прочел статью про pyrusmorph — python обёртку над 30-летним с++ ядром libmorph/rus. Бенчмарки удивили: в 30 раз быстрее PyMorphy3 при обработке текста. После я задумался. Как это вообще работает? Почему мы пишем import pyrusmorph и получаем доступ к нативному с++ коду и функциям прямо внутри python.

В статье я предлагаю пройти этот путь с нуля. Разобраться, как устроен Python «под капотом», написать своё расширение на чистом C‑API, а потом посмотреть, как эту боль лечит pybind11. В конце — бенчмарки и практические советы, когда вообще стоит лезть в c++ для оптимизации. Бонус — немного о компиляторе python кода (JIT‑компилятор)

Читать далее

Бесплатный аналог Auto Label от Roboflow: как я разметил 12 576 кадров за ночь

Под диплом мне нужен был датасет по средствам индивидуальной защиты. Шесть классов, и хуже всех в открытых наборах представлена сварочная маска: после удаления дублей около двух тысяч кадров. Я нарезал видео, получил в пять-шесть раз больше кадров, чем попало в итог, и упёрся в разметку.

Auto Label у Roboflow задачу решал, но на бесплатном тарифе это полгода ожидания, а платный прогон по всему массиву обошёлся бы примерно в двести долларов. Поэтому я написал автоаннотатор на SAM3: пятнадцать файлов на PyQt6, текстовые промпты вместо обучения, пороги по классам.

Главный трюк — конфликты классов. SAM3 находит «руку» и «перчатку», а нарушение приходится вычислять вычитанием: если бокс руки перекрыт перчаткой больше чем на четверть, он уходит в нарушение. На этом держится вся разметка шести классов.

Итог: 12 576 кадров, 104 726 боксов, одна ночь на RTX 4060 Ti, ноль рублей. Модель на датасете даёт mAP50 = 0.825. В статье есть код и разбор того, где SAM3 врёт.

Читать далее

Агент повесил Bash одной кавычкой. Как детерминированный FSM спасает CLI-рантаймы

Представьте: ваш автономный агент генерирует команду для терминала, но ошибается на один символ. Шелл уходит в бесконечное ожидание, а процесс агента намертво зависает. Сегодня разберём, почему регулярки не спасают от этой боли, и покажем, как детерминированный конечный автомат (FSM) на хуке PreToolUse снимает проблему на уровне инфраструктуры.

Читать далее

Как я рисовал NFT-подарки Telegram в 60 fps, не имея API: бот для эмодзи, стикеров и подарков

В Telegram есть премиум-эмодзи, паки стикеров и NFT-подарки. Всё это красиво выглядит, но достать оттуда что-то для своей работы неудобно. Скачать пак целиком нельзя. Чтобы узнать custom_emoji_id, нужен отдельный бот или отладчик. А анимацию подарка вне Telegram можно разве что записать с экрана.

Я написал бота Emoji Studio, который всё это делает. В статье — не столько про сами функции, сколько про несколько интересных задач, которые пришлось решить по дороге:

Читать далее

[Перевод] Я встроил процессор RISC-V в веса Qwen 3.5, а памятью ему стали слои DeltaNet

Qwenputer — это обычная Qwen 3.5 4B, в веса которой скомпилирован 32-битный процессор RISC-V (RV32IM). Оперативной памятью ему служат собственные слои Gated DeltaNet модели: при гейте 1, силе записи 1 и one-hot-ключах каждая запись перезаписывает ровно одну строку состояния, а one-hot-запрос возвращает точное значение. Процессор занимает 11 слоёв перед 32 слоями Qwen, проходит официальные riscv-tests и выполняет C и MicroPython внутри прямых проходов модели, без внешних инструментов. На длинной арифметике NUPA точность выросла с 48,6% до 100%, примерно на 230 000 позиций вывода не нашлось ни одного расхождения с эталонной машиной. Автор объясняет, как это устроено, где ограничения и как к модели прикрутили файловую систему.

Читать далее

Дедупликация фото без нейросетей. Часть 1: измеряем перцептивные хеши

Недавно у нашей команды появилась необходимость отвечать на вопрос: А такое фото у нас уже есть?». Не «такой же файл» — его можно было бы легко проверить по SHA-256, — а именно такое же фото: пережатое, уменьшенное, с чужим логотипом поперёк кадра, подрезанное по краям.

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

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

Читать далее

Джойстик отпустили. Робот не в курсе

После ожидания SDK обработчик геймпада отправлял старую ось, хотя нейтраль уже лежала в очереди. Дочитывание ввода, отдельный исполнитель кнопок и deadline для RLock. Программные проверки и отдельный опыт с 8Bitdo SN30 Pro по Bluetooth.

Читать далее

Байты приходят, события нет: проверяем кадры SSE и gzip

Сервер отправляет по SSE три сообщения с паузой 250 мс, а клиент видит события только через 750 мс. Собрал стенд на стандартной библиотеке Python и поставил три отметки времени: байты тела ответа, текст после декодера gzip, готовое событие. Незавершённый кадр и сжатие без промежуточного сброса дают одинаковый симптом, но задержка появляется в разных точках.

Читать далее

Данные для языка, которого почти нет в интернете: текстовые слои, турецкие подмены и псевдоразметка двумя моделями

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

Эта статья про данные. Оговорка та же, что и раньше, только строже: я не называю источники, архивы, издания, каналы, людей и организации, которые нам что-то передали, и не рассказываю о договорённостях с ними. Нет здесь и названий моделей, порогов фильтров и внутренних скриптов. Объёмы даю порядками величин. Зато будут метрики, методика проверки и грабли, на которые я наступил, и все цифры взяты из отчётов экспериментов, а не по памяти.

Читать далее
❌