<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Python</title>
    <description>Python — мультипарадигменный язык программирования. Изучайте Питон с помощью представленных в разделе материалов.</description>
    <link>https://tproger.ru/tag/python</link>
    <atom:link href="https://tproger.ru/tag/python/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Tue, 29 Sep 2026 12:32:23 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Python</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>Как распознать паспорт на Python за 5 минут</title>
      <link>https://tproger.ru/articles/kak-raspoznat-pasport-na-python-za-5-minut</link>
      <comments>https://tproger.ru/articles/kak-raspoznat-pasport-na-python-za-5-minut?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-raspoznat-pasport-na-python-za-5-minut</guid>
      <description><![CDATA[<p>Как распознать паспорт на Python за 5 минут: пошаговая интеграция Smart ID Engine, настройка распознавания паспорта РФ, обработка изображения и получение текстовых и графических данных.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-raspoznat-pasport-na-python-za-5-minut">Как распознать паспорт на Python за 5 минут</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Распознавание]]></category>
      <category><![CDATA[Финтех]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 10 Sep 2026 11:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ввод данных паспорта – отправная точка для доступа к продуктам и услугам в банках и других финансовых организациях, страховых и телеком-компаниях. Технологии автоматического <a href="https://smartengines.ru/smart-passportreader/">распознавания паспорта</a> позволяют ускорить доступ клиента к услугам, избежать ошибок и рисков утечки персональных данных.</p><p>Python для этого подходит идеально: на нем пишут и быстрые прототипы с MVP, и промышленные серверные и высоконагруженные системы. Значит, распознавание можно встроить прямо в то приложение, где данные уже обрабатываются, без отдельного сервиса между ними.</p><p>Smart ID Engine распознает удостоверяющие личность документы и отдает структурированные данные и изображения отдельных полей: фото владельца, подпись, штамп. Полный код доступен на<a href="https://github.com/SmartEngines/Smart-ID-Engine-SDK/tree/main"> GitHub</a>.</p><h2>Пошаговое встраивание в Python-приложение</h2><h3>1. Подключение библиотеки</h3><p>Для подключения библиотеки в проект необходимо скопировать в ваш проект файлы обертки (папка bindings), саму библиотеку (папка bin) и конфиг распознавания (папка data-zip с файлом bundle_…se). Обертку и библиотеку нужно подключить в проект:</p><p>и импортировать библиотеку:</p><h3>2. Создание движка распознавания</h3><p>Движок распознавания создается из конфига распознавания (data-zip/bundle_…se). В параметрах необходимо указать путь к нему и параметр ленивой инициализации (True/False). При значении False все внутренние объекты будут инициализированы сразу не дожидаясь, когда они понадобятся для какой-то конкретной сессии.</p><h3>3. Настройка распознавания</h3><p>Настройка распознавания (settings) включает в себя выбор режима распознавания (mode), маски документа (document type masks), а также при желании указание дополнительных опций. Для распознавания документа по одному изображению используется режим singleshot (если документ распознается по нескольким картинкам — default). Для внутреннего паспорта РФ требуется специфицировать маску документа, указав rus.passport.national в доступных к распознаванию документах.</p><h3>4. Распознавание документа</h3><p>Сессия распознавания паспорта РФ подразумевает распознавание одного документа (по одному или нескольким кадрам). Для распознавания другого документа создайте новую сессию, настройки можно оставить те же. Создание сессии осуществляется путем указания ранее созданных настроек (settings) и персонализированной подписи (personalized_signature).</p><p>Загрузка изображения паспорта РФ по пути (path_to_image):</p><p>Распознавание паспорта:</p><p>Получение результата:</p><h3>5. Извлечение информации из результата распознавания</h3><p>Тип документа</p><p>Вывод:</p><p>Описание документа</p><p>Вывод:</p><h4>Текстовые поля</h4><p>Включают все текстовые строки, которые были извлечены с документа</p><p>Вывод:</p><p><i>Полный вывод содержит более 30 полей, включая информацию об органе выдачи и MRZ-строку.</i></p><h4>Поля изображений</h4><p>Включают в себя вырезанные из исходного документа зоны интереса при их наличии. Для паспорта РФ — это подпись владельца, подпись органа выдачи, фото владельца, штамп.</p><p>Вывод:</p><h2>Заключение</h2><p>Как можно убедиться, <a href="https://smartengines.ru/smart-passportreader/">распознавание паспорта</a> можно легко встроить на Python. На выходе система Smart Engines возвращает структурированный результат, включая текстовые данные, подписи, печати и другие реквизиты и атрибуты документа.</p><p>При этом распознавание паспорта на Python – не единственный вариант интеграции Smart ID Engine. Та же библиотека может работать и как часть мобильного или десктоп-приложения, а также прямо в браузерах или мессенджерах. Выбор конкретного варианта зависит от того, какие документы нужно распознавать и откуда поступает изображение – подробнее об этом можно прочитать на сайте <a href="https://smartengines.ru">Smart Engines</a>.</p><p><i>Реклама. Рекламодатель: ООО «Смарт Энджинс Сервис» ИНН 7728328449, erid: 2W5zFJof1MB</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Polars 2.0 RC переводит LazyFrame на потоковый движок и ломает тихие касты</title>
      <link>https://tproger.ru/news/polars-2-0-rc-perevodit-lazyframe-na-potokovyj-dvizhok-i-lomaet-t</link>
      <comments>https://tproger.ru/news/polars-2-0-rc-perevodit-lazyframe-na-potokovyj-dvizhok-i-lomaet-t?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/polars-2-0-rc-perevodit-lazyframe-na-potokovyj-dvizhok-i-lomaet-t</guid>
      <description><![CDATA[<p>Первый release candidate Polars 2.0: LazyFrame.collect() идёт через streaming-движок, порядок строк в join и group_by не гарантирован, is_in и concat больше не молчат об ошибках типов. Как проверить свой код.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/polars-2-0-rc-perevodit-lazyframe-na-potokovyj-dvizhok-i-lomaet-t">Polars 2.0 RC переводит LazyFrame на потоковый движок и ломает тихие касты</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Анализ данных]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 03 Sep 2026 06:53:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Команда Polars 2 сентября <a href="https://pola.rs/posts/announcing-polars-2/">выпустила</a> первый release candidate версии 2.0 библиотеки для работы с таблицами на Python и Rust. Финальный релиз обещан «в ближайшие недели». Главное изменение одно: любой вызов collect() у LazyFrame теперь по умолчанию выполняется потоковым движком, который обрабатывает промежуточные данные порциями и, по словам авторов, заметно снижает расход памяти. Автор библиотеки Ричи Винк пишет, что новых функций в 2.0 почти нет, а мажорная версия нужна, чтобы избавиться от старых архитектурных решений и поменять дефолты.</p><p>Для тех, кто гоняет Polars в пайплайнах, это значит два дела на сегодня. Во-первых, после обновления часть запросов может вернуть строки в другом порядке: потоковый движок не гарантирует порядок для join, group_by и unpivot, если явно не попросить. Во-вторых, код, который годами «работал» на неявных приведениях типов и молчаливом заполнении пропусков, начнёт падать с ошибкой. Разработчики называют это осознанной политикой: ошибка сразу лучше неверного результата через двадцать минут работы пайплайна.</p><ul><li>RC ставится командой pip install polars==2.0rc1; финальный 2.0 выйдет в ближайшие недели, точной даты нет.</li><li>LazyFrame.collect() по умолчанию идёт через streaming-движок; по ожиданиям авторов, в совокупности он «легко в 5 раз быстрее» старого in-memory и заметно экономит память; независимых замеров RC нет.</li><li>Порядок строк после join, group_by и unpivot больше не гарантирован; для join и group_by порядок возвращает параметр maintain_order, для unpivot нужен явный sort; старый движок включается через pl.Config.set_engine_affinity("in-memory") или collect(engine="in-memory").</li><li>is_in с разными типами, горизонтальный concat с разной высотой, касты строк в даты и целых в Enum теперь бросают исключение вместо тихого приведения.</li><li>Большинство удалённых методов и параметров отвечают типизированными ошибками AttributeRemovedError и ArgumentRemovedError с подсказкой, чем заменить.</li></ul><h2>Почему потоковый движок потребовал мажорной версии</h2><p>Polars давно развивает два движка. Классический in-memory собирает результат каждой операции целиком в памяти. Потоковый разбивает данные на куски и прогоняет их через план запроса конвейером, поэтому промежуточные результаты не раздуваются. До 2.0 потоковый движок нужно было включать явно; теперь режим engine="auto" выбирает именно его.</p><p>Цена такого дефолта в порядке строк: потоковый движок для ряда операций его не гарантирует. Старый движок порядок сохранял, и на это молча полагалось много кода: например, брали первую строку после группировки и считали её «самой ранней». В 2.0 такие места нужно найти и явно попросить порядок (у join и group_by есть параметр maintain_order, после unpivot остаётся явный sort):</p><p>Заявление о скорости стоит читать как оценку вендора: «в сумме мы ожидаем, что потоковый движок будет легко в 5 раз быстрее», со ссылкой на <a href="https://pola.rs/posts/benchmarks/">собственные бенчмарки</a> Polars. Независимых замеров на RC пока нет, и выигрыш зависит от запроса: на маленьких таблицах, которые целиком помещаются в кеш, разница будет меньше, чем на группировках по десяткам гигабайт.</p><h2>Где код перестанет молчать об ошибках</h2><p>Вторая тема релиза сформулирована в посте так: ошибки должны подниматься заранее, а не через 20 минут работы пайплайна, и неявное поведение при несовпадении данных должно включаться явно, а не быть дефолтом. Авторы отдельно отмечают, что строгость стала ценнее с приходом ИИ-агентов: агент может вызвать collect_schema(), проверить типы без чтения данных и быстро получить обратную связь. Примеры из поста и <a href="https://docs.pola.rs/releases/upgrade/2/">руководства по миграции</a>:</p><ul><li>is_in с разными типами. Раньше Int64 и Float64 приводились к общему супертипу, даже если это теряло точность. В примере из поста идентификатор 9007199254740993 при касте в float64 округлялся до 9007199254740992 (граница, до которой float64 представляет все целые точно), и проверка по списку «помеченных» аккаунтов давала ложное совпадение. В 2.0 это InvalidOperationError: кастовать нужно самому и осознанно.</li><li>Горизонтальный concat. Таблицы высотой 5 и 4 раньше склеивались, а недостающая ячейка молча становилась null; типичный сценарий из поста: тихо упавшая задача за один из дней. Теперь ShapeError, а старое поведение включается через how="horizontal_extend".</li><li>Касты заменены специализированными методами. Целые в Enum или Categorical и обратно: вместо cast() нужны .cat.to() и .cat.physical(). Строка в дату: вместо cast(pl.Date) методы .str.to_date() и .str.to_datetime(), которым можно явно задать формат. Кастовать плоскую колонку в List через cast(pl.List(...)) тоже нельзя, для этого есть pl.list().</li><li>Булевы операторы между Boolean и целыми числами теперь ошибка; std() и var() для Duration удалены, сначала переводите в микросекунды через .dt.total_microseconds().</li><li>Каст между Struct с разным числом полей при strict=True (дефолт) падает, а не обрезает лишние поля; руководство помечает это отдельным предупреждением, потому что раньше данные терялись молча.</li></ul><p>Отдельный набор изменений касается чтения CSV. При сканировании набора файлов схема теперь выводится по первым 10 файлам, а не по всем (параметр infer_schema_files). Автоматические имена колонок для файлов без заголовка начинаются с column_0, а не column_1; это же касается read_excel и read_ods. Пользовательская схема в scan_csv сопоставляется с файлом по именам колонок, а не по позиции: раньше первая запись схемы молча получала данные первой колонки файла, даже если имена не совпадали. Для лишних и недостающих колонок появились параметры extra_columns и missing_columns, по умолчанию оба бросают ошибку.</p><h2>Что будет со старым кодом</h2><p>Для большинства удалений библиотека получила два типизированных исключения (документация предупреждает, что часть удалённого по-прежнему даёт обычные AttributeError и TypeError): polars.exceptions.AttributeRemovedError для удалённых методов и атрибутов и polars.exceptions.ArgumentRemovedError для удалённых параметров. Оба сообщения указывают на замену:</p><p>По словам Винка, большая часть удалённого давно помечена как deprecated, и у тех, кто обновлялся регулярно, пайплайны пострадать не должны. Команда просит сообщать, если из библиотеки убрали что-то, на что реально полагались. Смена движка не затрагивает eager-API DataFrame: выигрыша в скорости там не будет, потому что дефолт меняется только у LazyFrame. Остальные несовместимости на eager распространяются полностью: строгий concat, новые правила read_csv, убранные касты и удалённые методы.</p><h2>Как проверить свой проект до финального релиза</h2><ol><li>Поставьте RC в отдельное окружение: pip install polars==2.0rc1. В прод его тащить рано, это кандидат в релиз.</li><li>Прогоните тесты и посмотрите на исключения AttributeRemovedError, ArgumentRemovedError, InvalidOperationError и ShapeError: каждое сообщение содержит подсказку с заменой.</li><li>Найдите места, где код полагается на порядок строк после join, group_by или unpivot: сортировки «по умолчанию», head(1) после группировки, сравнение с эталоном по позициям. Добавьте maintain_order (join, group_by) или явный sort (unpivot).</li><li>Если результат обязан совпадать со старым до байта, зафиксируйте старый движок на время миграции: pl.Config.set_engine_affinity("in-memory").</li><li>Проверьте чтение CSV без заголовка (сдвиг нумерации колонок на единицу) и scan_csv с явной схемой (сопоставление по именам).</li></ol><p>В планах ветки 2.x, о которых команда, по её словам, «недостаточно говорила публично»: полноценная out-of-core обработка для потокового движка, новая архитектура IO-плагинов, собственный читатель S3, расширенное покрытие SQL, планировщик на основе оценки стоимости с переупорядочиванием join и отказ от mmap, после которого конвейер станет асинхронным от начала до конца. Сроков по этим пунктам нет. Замечания по RC команда принимает в <a href="https://github.com/pola-rs/polars/issues">issues на GitHub</a>.</p><p>Источники: <a href="https://pola.rs/posts/announcing-polars-2/">Pre-release of Polars 2.0 (блог Polars, Ritchie Vink)</a>, <a href="https://docs.pola.rs/releases/upgrade/2/">Руководство по переходу на Polars 2.0</a>, <a href="https://pola.rs/posts/benchmarks/">Бенчмарки Polars</a></p><p>Изображение на обложке: Логотип: Polars</p>]]></content:encoded>
    </item>
    <item>
      <title>PyTorch 2.14 добавил NVGEMM, backend nccl2 и сборки для Python 3.15</title>
      <link>https://tproger.ru/news/pytorch-2-14-dobavil-nvgemm-backend-nccl2-i-sborki-dlya-python-3</link>
      <comments>https://tproger.ru/news/pytorch-2-14-dobavil-nvgemm-backend-nccl2-i-sborki-dlya-python-3?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/pytorch-2-14-dobavil-nvgemm-backend-nccl2-i-sborki-dlya-python-3</guid>
      <description><![CDATA[<p>Ядра CUTLASS через NVGEMM, backend nccl2, torch.switch, wheels для Python 3.15 и 3.15t без torch.compile, удалённые torch.cholesky и acc_policy=balanced.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/pytorch-2-14-dobavil-nvgemm-backend-nccl2-i-sborki-dlya-python-3">PyTorch 2.14 добавил NVGEMM, backend nccl2 и сборки для Python 3.15</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 03 Sep 2026 04:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>PyTorch Foundation 2 сентября <a href="https://pytorch.org/blog/pytorch-2-14-release-blog/">выпустила</a> PyTorch 2.14. Релиз собран из 2 995 коммитов от 487 участников и меняет сразу несколько слоёв: компиляцию GPU-ядер (NVGEMM с ядрами CUTLASS в Inductor), распределённое обучение (backend nccl2, перестройка process group без перезапуска), управляющие конструкции для torch.compile (torch.switch) и матрицу сборок, в которой появились wheels для Python 3.15 и free-threaded 3.15t.</p><p>Практический вопрос для тех, кто обновляется: что сломается. Удалены torch.cholesky, torch.qr и параметр профилировщика use_cuda; acc_policy="balanced" в LinearCrossEntropyOptions теперь вызывает ValueError, нужно "compact"; у скалярного clamp изменился градиент на границе. А torch.compile на Python 3.15 в этом релизе не работает и завершается явным RuntimeError.</p><ul><li>NVGEMM добавляет в Inductor ядра CUTLASS, сгенерированные CuTeDSL, с fusion эпилогов, scaled и NVFP4 GEMM.</li><li>Distributed: новый backend nccl2 с неблокирующими коммуникаторами и eager splitting; c10d умеет перенастраивать process group на месте; Flight Recorder работает с любым backend.</li><li>torch.switch обобщает torch.cond на несколько веток, torch.while_loop захватывается CUDA Graphs, @dynamic_spec единообразно описывает динамические формы для torch.compile, torch.export и make_fx.</li><li>Apple Silicon получил нативные SVD, eigh, QR и Cholesky; появились ROCm 7.14 wheels, захват графов на Intel XPU и цель sm_107 для NVIDIA Rubin.</li><li>Wheels для Python 3.15 и 3.15t собраны для Linux x86-64 и aarch64, Windows x86-64 и macOS Apple Silicon, но лежат на download.pytorch.org, а не на PyPI; torch.compile на 3.15 не поддерживается.</li></ul><h2>Что даёт NVGEMM и кому он нужен</h2><p>NVGEMM, по описанию релиза, подключает к компилятору Inductor ядра матричного умножения CUTLASS, которые генерируются через CuTeDSL. Поддерживаются слияние эпилогов (операций после умножения), scaled GEMM и формат NVFP4, а также grouped-reduction epilogues. Это продолжение линии PyTorch 2.13, где CuTeDSL-путь только закладывался. Выигрыш получат те, кто гоняет обучение и инференс на современных NVIDIA GPU через torch.compile; на CPU, ROCm и Apple Silicon эта часть релиза ничего не меняет. Цифр ускорения относительно 2.13 в блоге релиза нет, поэтому эффект стоит мерить на своей модели.</p><h2>Отказоустойчивость распределённого обучения</h2><p>Backend nccl2 появился рядом с прежним NCCL и приносит неблокирующие коммуникаторы и eager splitting. Важнее для практики изменения в c10d: process group можно перестроить на месте, без полного перезапуска задания, когда один из узлов выпал. Добавлены односторонние окна RMA, а Flight Recorder, инструмент посмертной диагностики зависших коллективных операций, теперь работает не только с NCCL. Для кластеров на десятки GPU это означает меньше потерянных часов при сбое одного узла; для одной машины изменения не заметны.</p><h2>Python 3.15: wheels есть, torch.compile нет</h2><p>Python 3.15 ещё не вышел: 1 сентября <a href="https://www.python.org/downloads/release/python-3150rc2/">появился</a> второй релиз-кандидат, финал назначен на 1 октября. PyTorch 2.14 уже публикует сборки под 3.15 и его free-threaded вариант 3.15t для Linux, Windows и macOS на Apple Silicon, включая применимые CPU-, CUDA-, ROCm- и XPU-варианты. Две оговорки из заметок к релизу: эти wheels не лежат на PyPI, их нужно ставить с индекса download.pytorch.org, и torch.compile под 3.15 не поддерживается; попытка скомпилировать модель завершится RuntimeError, работает только eager-режим.</p><p>Что это значит на практике: проверить совместимость своего кода с 3.15 до октября можно уже сейчас, но замеры производительности с компиляцией придётся отложить до следующего релиза PyTorch. Про сам релиз-кандидат и заморозку ABI мы <a href="https://tproger.ru/news/vywel-python-3-15-0rc2-abi-zamorozhen-finalnyj-reliz-1-oktyabrya">писали</a> отдельно.</p><h2>Несовместимые изменения</h2><ul><li>LinearCrossEntropyOptions(acc_policy="balanced") удалён: теперь ValueError, заменять на "compact".</li><li>Удалены устаревшие torch.cholesky и torch.qr (используйте torch.linalg.cholesky и torch.linalg.qr) и параметр профилировщика use_cuda.</li><li>Градиент скалярного clamp на границе изменён с 1 на 0; для тензорных границ градиент делится как 0,5 и 0,5. Модели, чувствительные к поведению на границе клиппинга, могут обучаться иначе.</li><li>Поддержка комплексных тензоров в torch.compile помечена как экспериментальная.</li></ul><h2>Как обновляться</h2><ol><li>Свериться с матрицей сборок на <a href="https://github.com/pytorch/pytorch/releases/tag/v2.14.0">странице релиза</a>: версии CUDA, ROCm 7.14, XPU и Python для вашей платформы.</li><li>Прогнать по коду поиск acc_policy="balanced", torch.cholesky, torch.qr и use_cuda.</li><li>Если используете кастомные process group с параметром backend, проверить их на nccl2 отдельно.</li><li>Для Python 3.15 ставить только с индекса download.pytorch.org и не рассчитывать на torch.compile.</li></ol><p>Библиотека torchvision 0.29 в релизе объявлена ABI-совместимой с будущими torch 2.15 и 2.16, то есть её не придётся пересобирать под следующие два выпуска. Ограничений на загрузку wheels по регионам в источниках нет. Что ещё вышло в ML-инструментах за последние недели, смотрите в обзоре <a href="https://tproger.ru/news/otkrytye-nejroseti-avgusta-frontir-na-odnoj-videokarte-i-chto-za">открытых моделей августа</a>.</p><p>Источники: <a href="https://pytorch.org/blog/pytorch-2-14-release-blog/">PyTorch 2.14 Release Blog</a>, <a href="https://github.com/pytorch/pytorch/releases/tag/v2.14.0">Release notes v2.14.0 на GitHub</a>, <a href="https://www.python.org/downloads/release/python-3150rc2/">Python 3.15.0rc2</a></p><p>Изображение на обложке: PyTorch Foundation, логотип PyTorch</p>]]></content:encoded>
    </item>
    <item>
      <title>Anthropic открыла Claude Commerce Agents: чертёж торгового агента на Claude</title>
      <link>https://tproger.ru/news/anthropic-otkryla-claude-commerce-agents-chertyozh-torgovogo-agent</link>
      <comments>https://tproger.ru/news/anthropic-otkryla-claude-commerce-agents-chertyozh-torgovogo-agent?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/anthropic-otkryla-claude-commerce-agents-chertyozh-torgovogo-agent</guid>
      <description><![CDATA[<p>Anthropic открыла Claude Commerce Agents под Apache 2.0: два агента для магазина, три способа запуска, проверки внутри вызова инструмента и свой бэкенд.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/anthropic-otkryla-claude-commerce-agents-chertyozh-torgovogo-agent">Anthropic открыла Claude Commerce Agents: чертёж торгового агента на Claude</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 02 Sep 2026 19:53:51 GMT</pubDate>
      <content:encoded><![CDATA[<p>Anthropic 2 сентября 2026 года <a href="https://x.com/ClaudeDevs/status/2095233745167282602">открыла исходники</a> Claude Commerce Agents: двух агентов на Claude для интернет-торговли под лицензией Apache 2.0. Первый, покупательский, бизнес встраивает в своё приложение для клиентов. Второй, мерчантский, нужен сотрудникам, чтобы вести листинги, цены, остатки и кампании. В <a href="https://github.com/anthropics/commerce-agents">репозитории anthropics/commerce-agents</a> лежат оба агента, четыре демонстрационные вертикали (розница, путешествия, телеком, билеты), семь pip-пакетов и плагин для Claude Code, который собирает такого агента под чужой стек.</p><p>Для команды, которая пишет ассистента магазина или маркетплейса, интерес здесь в архитектуре, а не в демо. Anthropic показывает, как описать агента один раз и запускать тремя способами, где стоят проверки, чтобы модель не могла подложить в корзину чужой товар или изменить цену без человека, и как подключить свой каталог через два интерфейса на Python.</p><p>Оговорка стоит в самом README: это референсная реализация, она не поддерживается и не принимает внешний вклад. В примерах нет аутентификации, MCP-серверы по умолчанию слушают loopback.</p><ul><li>Два агента, три способа запуска (Messages API, Claude Agent SDK, Managed Agents (beta)), четыре вертикали и восемь веб-приложений в одном репозитории под Apache 2.0.</li><li>Не оформляет заказ, не списывает деньги и не меняет живые листинги: checkout только рисует корзину, каждая запись мерчанта ждёт одобрения человека.</li><li>Проверки provenance, лимиты, ограждение стороннего текста и валидация памяти работают внутри вызова инструмента и держатся на всех трёх путях запуска.</li><li>Своя интеграция сводится к реализации StorefrontBackend или MerchantBackend; отсутствующие системы выключаются переключателями enable_*.</li><li>Модели по умолчанию: claude-sonnet-5 у покупательского агента, claude-opus-5 у мерчантского, claude-haiku-4-5-20251001 для памяти; рантаймы работают через Vertex AI, Bedrock, Microsoft Foundry и шлюзы.</li><li>Требования: Python 3.11 и новее, Node 22. Демо поднимается шестью командами из README.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-03/8278495a-80f4-42a1-baf3-e052ab3d3504.webp" alt="Кадр из видео Anthropic с четырьмя вертикалями: Retail, Travel, Telecom, Ticketing" /><figcaption>Четыре вертикали из анонсирующего видео. Скриншот: Anthropic, commerce-agents</figcaption></figure><h2>Один агент описан один раз и работает на трёх рантаймах</h2><p>Главная архитектурная идея репозитория, как её формулирует <a href="https://github.com/anthropics/commerce-agents/blob/main/README.md">README</a>: каждый агент определён один раз (промпт, скиллы, контракты инструментов, гейты) и запускается на Messages API, на Claude Agent SDK и на Managed Agents. Четыре вертикали работают поверх одних библиотек и различаются бэкендом и доменными расширениями интерфейса.</p><p>Покупательский агент ищет и сравнивает товары, собирает планы покупок, наполняет корзину, отвечает на вопросы о заказах и правилах магазина и запоминает, что покупатель о себе рассказал. Его пять сценариев лежат как skills в каталоге <a href="https://github.com/anthropics/commerce-agents/tree/main/shopping-agent/skills">shopping-agent/skills</a>. Мерчантский агент объясняет показатели, правит листинги, реагирует на алерты по остаткам и заказам, назначает цены и промо, готовит кампании. Его пять сценариев лежат в <a href="https://github.com/anthropics/commerce-agents/tree/main/merchant-agent/skills">merchant-agent/skills</a>. Любая запись мерчанта становится staged change, которую применяет уже интерфейс одобрения на стороне хоста.</p><blockquote>Nothing places an order, charges a card, or changes a live listing: checkout renders the cart for the host to complete, and every merchant write is staged until a person approves it.</blockquote><p>Код разложен на семь pip-пакетов. Общее для обеих ролей вынесено в commerce-common: конфиг, ограждение стороннего текста, память, скиллы, grounding, презентационные компоненты, исполнитель инструментов и события. У каждой роли три пакета: core с типами, интерфейсом бэкенда, промптом, контрактами инструментов и гейтами; runtime с циклом ходов на Messages API; sdk с тем же агентом на Agent SDK и консолью. CI проверяет, что имена семи пакетов остаются незарегистрированными в публичном индексе, а pin-файлы ставят их из каталогов репозитория.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-03/6f818534-34eb-4db9-9321-d353b648b4e1.webp" alt="Горизонтальная диаграмма: состав репозитория commerce-agents в штуках: 2 агента, 3 рантайма, 4 вертикали, 7 пакетов, 8 веб-приложений, 10 flows, 7 расширений интерфейса, 4 команды плагина" /><figcaption>Состав поставки по README репозитория. График: Tproger по данным README anthropics/commerce-agents</figcaption></figure><p>Эталонный путь запуска, вокруг которого построены примеры, это Messages API. Хост создаёт объект агента с бэкендом, каталогом скиллов и конфигом и стримит события хода; извлечение памяти вызывается отдельно после хода, и только на этом пути. Код из README:</p><p>На Agent SDK тот же промпт, скиллы и инструменты, но цикл ведёт SDK: хост заранее подгружает данные для grounding, после хода ничего не выполняется. На Managed Agents агент размещён у Anthropic и ходит за данными в ваш MCP-сервер; деплой делает скрипт scripts/deploy_managed_agent.sh, без флага --live это сухой прогон. Различия трёх путей по docs/safety.md:</p><ul><li><b>Messages API.</b> Все правила grounding включаются принудительно через tool_choice; работают извлечение памяти после хода, сжатие истории и бюджеты аналитического делегата мерчанта.</li><li><b>Agent SDK.</b> Grounding только для правил с формой предварительной загрузки; цикл ограничен max_turns; аналитика мерчанта идёт субагентом без SQL-инструмента и бюджетов; память хост извлекает сам.</li><li><b>Managed Agents.</b> Grounding отсутствует, циклом владеет платформа, память пишется только через save_memory, одобрением staged change служит промпт always_ask на apply_change.</li></ul><h2>Проверки стоят внутри вызова инструмента, поэтому переживают смену рантайма</h2><p>Документ <a href="https://github.com/anthropics/commerce-agents/blob/main/docs/safety.md">docs/safety.md</a> делит правила на три группы: что код проверяет сам, что по-прежнему просят у модели промптом и что обязан добавить деплой. Ключевой приём: правило внутри вызова инструмента держится на всех трёх путях, потому что все три прогоняют вызовы через один исполнитель в модуле commerce_common/execution.py. Правило на уровне хода живёт в конкретном рантайме, и таблица указывает, где оно не работает.</p><blockquote>A rule enforced inside a tool call holds on all three paths, because the Messages API runtime, the SDK toolset, and the MCP server execute tools through the same executor.</blockquote><p>Что именно проверяется в коде, по таблице документа:</p><ul><li><b>Ограждение (fencing).</b> Сторонний текст очищается, оборачивается в ограждение с фиксированной меткой и обрезается до max_fenced_chars. Очистка убирает невидимые и управляющие символы, поддельные маркеры ходов, теги транскрипта и вызовов инструментов и копии самого маркера ограждения.</li><li><b>Лимиты цикла.</b> Запрошенное моделью число результатов поиска обрезается до max_search_results; после max_tool_iterations раундов рантайм Messages API принудительно делает ход без инструментов.</li><li><b>Provenance корзины.</b> В корзину попадают только идентификаторы товаров, которые в этой сессии вернул инструмент каталога или заказов. Попытка добавить товар с опциями (размер, цвет) задерживается, и модели показывают варианты. Действует лимит на позицию и на число строк.</li><li><b>Нет оплаты.</b> У StorefrontBackend нет метода, который размещает заказ или списывает деньги. URL размещённого checkout приходит из checkout_handoff после вызова модели и не проходит через неё.</li><li><b>Provenance staged-записей мерчанта.</b> Изменение принимает только идентификаторы листингов и кампаний, возвращённые инструментом в этой сессии; правка контента требует чтения get_listing. apply_change принимает только идентификаторы изменений, которые вернули staging или get_pending_changes.</li><li><b>Guardrails мерчанта.</b> Проверяются при постановке изменения и повторно при применении: число позиций, размах изменения цены, глубина промо, размер пополнения, бюджет кампании, защищённые поля.</li><li><b>Одобрение хостом.</b> При require_host_approval (включено по умолчанию) apply_change проходит только для идентификаторов, которые хост пометил одобренными. Карточка предпросмотра ничего не одобряет, «да, применяй» в чате тоже.</li><li><b>Память.</b> Ключ факта до 64 символов, значение до 200, одна из трёх категорий; значения, похожие на идентификаторы, отбрасываются. Извлечение читает только текст последнего обмена, никогда результаты инструментов.</li><li><b>Поверхность инструментов и идентичность.</b> Список инструментов вычисляется из конфига деплоя, исполнитель отказывает любому другому имени. Идентичность держит сервер: ни один аргумент инструмента не называет пользователя или мерчанта.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-03/858edd4a-4a45-4ee5-adca-71116592a154.webp" alt="Горизонтальная диаграмма лимитов: ключ факта памяти 64 символа, значение 200 символов, ограждение стороннего текста 12 000 символов по умолчанию" /><figcaption>Лимиты, которые проверяет код: память покупателя и размер одного ограждения. График: Tproger по данным docs/safety.md и docs/backends.md anthropics/commerce-agents</figcaption></figure><p>Вторая группа правил остаётся в промпте: трактовать ограждённый текст как материал для отчёта, называть условия и цифры только из результата инструмента, подтверждать запись только после успешного вызова, называть товары по идентификатору. Документ оговаривает: промптовые правила держатся ровно настолько, насколько модель следует инструкциям, а таблица держится на любой модели. Деплой, который меняет модель или выключает require_host_approval, должен сначала перегнать свои evals по этому разделу.</p><blockquote>When the model breaks one of these, the error is confined to its text. Every write, figure, and disclosure behind that text still passed the checks in the table above, so the failure is a misstatement to correct and no action needs reversing.</blockquote><p>Третья группа целиком на стороне деплоя: аутентификация и авторизация на каждом маршруте и на MCP-серверах (примеры принимают любого вызывающего), учётные данные для вызова ваших сервисов, лимиты запросов, бизнес-правила (фрод, право на покупку, цены, остатки), оплата после checkout, обращение с памятью как с персональными данными, гигиена логов и сама поверхность одобрения. Значения guardrails в двух файлах config.py названы демонстрационными.</p><h2>Своя интеграция сводится к двум интерфейсам и переключателям enable_*</h2><p>Точка входа для собственных систем описана в <a href="https://github.com/anthropics/commerce-agents/blob/main/docs/backends.md">docs/backends.md</a>. Деплой реализует StorefrontBackend поверх каталога, корзины, заказов и политик или MerchantBackend поверх аналитики, каталога, остатков, цен и кампаний. Контрактом служат докстринги методов в backend.py и types.py каждой роли. Каждый метод вызывает ваш сервис на стороне сервера с учётными данными, которые хост хранит для сессии; модель видит только результат.</p><blockquote>Each one calls your service server-side with the credential your host holds for the session; the model reads only the result.</blockquote><p>Документ раскладывает интеграцию на шесть шагов. Первый: решить, кто вызывающий. Хост аутентифицирует человека и стартует сессию с принципалом; токен покупателя живёт в контексте сессии, сервисная учётка передаётся в конструктор бэкенда. Гость тоже принципал: чтение, которому нужен аккаунт, бросает исключение, которое подкласс исполнителя превращает в просьбу войти. Второй: порядок многошаговых сценариев (удержать места, затем подтвердить) хранится и проверяется в бэкенде, а нарушение маппится через domain_error в понятный модели результат. Третий: как завершается checkout. Вариантов три: ссылка на маршрут в вашем приложении, размещённый checkout платформы, для которого checkout_handoff возвращает URL, или маркетплейс с записью на каждого продавца.</p><p>Четвёртый шаг: товары с опциями. Запись бывает простой, семейством (с полем options) или вариантом (с option_values и variant_of); идентификатор варианта идёт всюду, где ждут идентификатор товара. Детали товара возвращают все варианты семейства в одном ограждении, обрезанном до max_fenced_chars (12 000 символов по умолчанию). Компактная строка варианта занимает 70–120 символов, так что в семейство помещается около шестидесяти вариантов; кроссовки в восьми цветах и четырнадцати размерах документация советует подавать как восемь семейств по четырнадцать, потому что за пределами лимита результат режется без ошибки. Пятый: записи мерчанта по семействам, где изменение цены и пополнение именуют вариант. Шестой: для цифр, которых у платформы нет, возвращать None с пометкой, а не подставной ноль.</p><p>Для пилота README предлагает начинать с малого. Покупательский пилот реализует поиск и карточку товара, а остальное заглушает: метод-заглушка возвращает результат «недоступно» и не меняет ни байта промпта. Мерчантский пилот реализует восемь методов чтения, записи отказывают. Система, которой у бизнеса нет совсем, выключается переключателем enable_*: это убирает её инструменты, строки промпта и правило grounding на всех трёх путях, а сценарии, которым она нужна, паркуются в каталоге skills/_staged/. Свой сценарий добавляется каталогом с файлом SKILL.md, доменный интерфейс расширением PresentationExtension (вертикали поставляют семь), а brand_name, assistant_name и brand_voice в конфиге задают личность ассистента.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-03/632d0218-5e02-4a2c-8f59-e75bb9f7efce.webp" alt="Кадр из видео Anthropic: путь от намерения покупателя до staged-заказа" /><figcaption>Кадр «From intent to a staged order» из анонсирующего видео. Скриншот: Anthropic, commerce-agents</figcaption></figure><h2>Модели заданы строкой в конфиге, а переключение платформы сосредоточено в одном месте и зависит от рантайма</h2><p>По <a href="https://github.com/anthropics/commerce-agents/blob/main/docs/deployment.md">docs/deployment.md</a> код по умолчанию ходит в API Anthropic, но у каждого пути есть одно место, где деплой переключает платформу. Рантаймы Messages API принимают любой асинхронный клиент из пакета anthropic аргументом client=: AsyncAnthropicVertex для GCP Vertex AI, AsyncAnthropicBedrockMantle или AsyncAnthropicBedrock для AWS, AsyncAnthropicFoundry для Microsoft Foundry, AsyncAnthropic с base_url и auth_token для собственного шлюза. Пакеты требуют anthropic 0.91 и новее. Рантаймы Agent SDK HTTP-клиента не создают: платформу выбирает CLI Claude Code по переменным окружения, которые добавляются в options.env. Managed Agents работает на инфраструктуре Anthropic, поэтому у него нет варианта для Vertex, Bedrock и Foundry.</p><p>Модель задаётся строкой в конфиге: поля model и memory_model у каждой роли, у мерчанта ещё analysis_model. Значения по умолчанию, по таблице документа: claude-sonnet-5 для покупательского агента, claude-opus-5 для мерчантского, claude-haiku-4-5-20251001 для извлечения памяти. Грамматика идентификаторов у платформ разная: Vertex пишет датированные снимки через @, Bedrock через Mantle берёт идентификаторы с префиксом anthropic., а через Invoke API идентификаторы inference-профилей. Все три поля идут через один клиент, поэтому все три модели должны существовать на целевой платформе. Живого разговора с облаком в CI нет; документ просит прогнать его на своей платформе до того, как на неё полагаться.</p><p>Доступность API Anthropic и облачных платформ для конкретной страны или способа оплаты в документации репозитория не обсуждается. Для собственного шлюза документ называет требование: отдавать /v1/messages со стримингом SSE для Messages API, а для Managed Agents ещё проксировать /v1/skills, /v1/agents, /v1/environments, /v1/sessions и поток событий сессии с заголовками anthropic-beta.</p><h2>Демо поднимается шестью командами, а плагин Claude Code собирает агента под ваш стек</h2><p>Порядок запуска из README (нужны Python 3.11 и новее и Node 22, ключ ANTHROPIC_API_KEY в файле .env):</p><p>Флаг --merchant поднимает портал мерчанта вместо витрины, --all оба. В README каждой вертикали есть раздел Try с репликами, которые прогоняет scripts/smoke_chat.py. Розничная ACME показывает поиск, сравнение, корзину, checkout и память; ACME Travel добавляет инвентарь с датами; ACME Mobile матрицу тарифов и серверные раскрытия комиссий; ACME Tickets таймированные удержания мест, листы ожидания и карту зала.</p><p>Второй способ начать: <a href="https://github.com/anthropics/commerce-agents/tree/main/plugins/commerce-builder">плагин commerce-builder</a> для Claude Code. Он читает клонированный репозиторий как эталон и собирает агента на этих пакетах против ваших систем либо проверяет уже написанного. Установка и первая команда из README:</p><p>Команда /scaffold-commerce-agent спрашивает о стеке, проговаривает план и строит проект. Дальше /add-commerce-flow добавляет сценарий, /author-commerce-evals пишет evals, /review-commerce-agent начинает с уже существующего агента. Собственных MCP-коннекторов в поставке нет: оба агента ходят в системы через интерфейсы бэкенда. Где официальный коннектор является источником истины (Snowflake, BigQuery, Stripe, Square, Slack и другие в списке README), он и становится целью интеграции. MCP-сервер торговой платформы вызывается из метода бэкенда на сервере, и гейты provenance остаются перед каждой записью.</p><p>Проверка после правок: ruff check и ruff format --check, pytest, python scripts/check.py; python scripts/verify_all.py добавляет сухие прогоны деплоя и сборку веб-приложений; python scripts/smoke_chat.py --vertical travel проводит один живой разговор и требует ключ. Чтобы убедиться, что кэширование промпта работает, README советует читать cache_read_input_tokens из события turn_complete: ноль на втором ходу означает, что префикс промпта изменился.</p><h2>Что делать команде, которая пишет ассистента для магазина</h2><ol><li>Поднять розничное демо по шести командам выше (Python 3.11 и новее, Node 22, ключ API) и пройти реплики из раздела Try в examples/retail/ на витрине (порт 3000) и в портале мерчанта (флаг --merchant, порт 3100).</li><li>Сверить таблицу «Enforced in code» и список «What a deployment owns» из docs/safety.md с собственной платформой: аутентификация, учётные данные, лимиты запросов, бизнес-правила, оплата, персональные данные в памяти, логи.</li><li>Для пилота реализовать в StorefrontBackend только поиск и карточку товара, для MerchantBackend восемь методов чтения. Отсутствующие системы выключить через enable_*, зависимые сценарии убрать в skills/_staged/.</li><li>Разложить каталог по трём формам записи из docs/backends.md и проверить самое большое семейство против лимита max_fenced_chars в 12 000 символов.</li><li>Выбрать завершение checkout: свой маршрут, размещённый URL платформы через checkout_handoff или ссылка на продавца для маркетплейса; оплата остаётся в хосте.</li><li>Для Vertex AI, Bedrock, Foundry или шлюза передать клиент аргументом client= (anthropic 0.91 и новее) или переменные CLI в options.env и заменить все три идентификатора моделей.</li><li>Перед выкладкой прогнать ruff, pytest, scripts/check.py и scripts/verify_all.py, затем один живой разговор scripts/smoke_chat.py на своей платформе; при смене модели перегнать evals по разделу «Still asked of the model».</li></ol><p>Репозиторий создан 1 сентября, анонс в аккаунте Claude Developers вышел 2 сентября в 19:33 UTC. На 3 сентября, 01:53 мск, по <a href="https://api.github.com/repos/anthropics/commerce-agents">данным GitHub API</a> у проекта 277 звёзд и 46 форков. Поддержки и приёма внешних изменений Anthropic не обещает: «This is a reference implementation; it is not maintained and does not accept contributions», говорится в README. На практике это значит, что исправления и адаптацию под свои системы команде придётся вести в собственном форке, а промпты и гейты под новые модели проверять своими evals. Цен, лимитов и сроков дальнейшего развития Anthropic в репозитории не называет.</p><p>Источники: <a href="https://x.com/ClaudeDevs/status/2095233745167282602">Claude Developers в X: анонс открытия Claude Commerce Agents (2 сентября 2026)</a>, <a href="https://github.com/anthropics/commerce-agents">GitHub: anthropics/commerce-agents</a>, <a href="https://github.com/anthropics/commerce-agents/blob/main/README.md">README репозитория commerce-agents</a>, <a href="https://github.com/anthropics/commerce-agents/blob/main/docs/safety.md">docs/safety.md: правила, проверяемые кодом, и обязанности деплоя</a>, <a href="https://github.com/anthropics/commerce-agents/blob/main/docs/backends.md">docs/backends.md: подключение своих систем</a>, <a href="https://github.com/anthropics/commerce-agents/blob/main/docs/deployment.md">docs/deployment.md: Vertex AI, Bedrock, Microsoft Foundry и шлюзы</a>, <a href="https://github.com/anthropics/commerce-agents/tree/main/plugins/commerce-builder">Плагин commerce-builder для Claude Code</a>, <a href="https://github.com/anthropics/commerce-agents/tree/main/shopping-agent/skills">Скиллы покупательского агента</a>, <a href="https://github.com/anthropics/commerce-agents/tree/main/merchant-agent/skills">Скиллы мерчантского агента</a></p><p>Изображение на обложке: Anthropic, кадр из анонса Claude Commerce Agents</p>]]></content:encoded>
    </item>
    <item>
      <title>В кэше десктопного ChatGPT нашли 1,7 ГБ с Python, Node.js и LibreOffice</title>
      <link>https://tproger.ru/news/v-kewe-desktopnogo-chatgpt-nawli-1-7-gb-s-python-node-js-i-libr</link>
      <comments>https://tproger.ru/news/v-kewe-desktopnogo-chatgpt-nawli-1-7-gb-s-python-node-js-i-libr?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/v-kewe-desktopnogo-chatgpt-nawli-1-7-gb-s-python-node-js-i-libr</guid>
      <description><![CDATA[<p>Саймон Уиллисон нашёл в кэше десктопного приложения OpenAI runtime на 1,7 ГБ: Python, Node.js, Git, Poppler, LibreOffice и skills к ним. Зачем ИИ-клиенту офисный пакет.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/v-kewe-desktopnogo-chatgpt-nawli-1-7-gb-s-python-node-js-i-libr">В кэше десктопного ChatGPT нашли 1,7 ГБ с Python, Node.js и LibreOffice</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 02 Sep 2026 06:04:36 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчик Саймон Уиллисон, автор Datasette и одного из самых читаемых блогов про ИИ-инструменты, 1 сентября <a href="https://simonwillison.net/2026/Sep/1/codex-libreoffice/">показал</a>, что десктопное приложение OpenAI Codex, которое компания недавно переименовала в ChatGPT, держит в скрытом каталоге кэша на macOS отдельную среду выполнения на 1,7 ГБ. Внутри полная установка Python, полная установка Node.js и нативные сборки Git, Poppler и headless-версии офисного пакета LibreOffice.</p><p>Для пользователей это ответ на вопрос, куда с диска ушли почти два гигабайта после установки чат-клиента. Для разработчиков это редкий взгляд на то, как устроен агент внутри потребительского приложения: для работы с документами у него под рукой те же инструменты командной строки, которые вы поставили бы руками, и инструкции к ним в обычных текстовых файлах.</p><ul><li>Каталог ~/.cache/codex-runtimes/codex-primary-runtime занимает 1,7 ГБ на macOS.</li><li>Node.js 446,4 МБ, Python 440,6 МБ, LibreOffice headless 429,7 МБ, Poppler 187,9 МБ, Git 148,1 МБ; вместе нативные бинарники весят 771 МБ.</li><li>В plugins/documents лежат skills, которые объясняют агенту, где искать бинарники и как ими пользоваться.</li><li>Наблюдение сделано на одной машине с macOS; официальных данных о составе runtime на Windows и Linux нет.</li></ul><h2>Что именно лежит в каталоге</h2><p>Уиллисон наткнулся на каталог случайно, разбирая ~/.cache/ утилитой OmniDiskSweeper. Внутри codex-runtimes/codex-primary-runtime три части: dependencies на 1,7 ГБ, plugins на 6,3 МБ и файл runtime.json на 4,1 КБ. В зависимостях четыре подкаталога: native на 771,0 МБ, node на 446,4 МБ, python на 440,6 МБ и bin на 28,7 КБ. В native лежат libreoffice-headless (429,7 МБ), poppler (187,9 МБ), git (148,1 МБ), libheif (4,7 МБ) и jxrlib (679,9 КБ).</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-02/3a146c9b-2d63-4ff5-9326-5bd902288f8a.webp" alt="Диаграмма размеров компонентов runtime десктопного ChatGPT" /><figcaption>Размер компонентов codex-primary-runtime на машине Саймона Уиллисона, МБ. График: Tproger по данным simonwillison.net</figcaption></figure><p>Набор говорящий. Poppler читает и рендерит PDF, LibreOffice в headless-режиме конвертирует документы Word, Excel и PowerPoint в другие форматы без графического интерфейса, libheif открывает фотографии с iPhone в формате HEIC, jxrlib нужна для JPEG XR. Git и полноценные интерпретаторы Python и Node.js дают агенту возможность выполнять код и работать с репозиториями на машине пользователя.</p><h2>Зачем агенту офисный пакет</h2><p>Самое интересное в находке лежит в папке plugins/openai-primary-runtime/plugins/documents. По словам Уиллисона, в ней лежат skills, то есть текстовые инструкции, которые объясняют модели, как найти эти бинарники и как ими пользоваться. Это тот же подход, что в Claude Code и других агентных инструментах: вместо встроенного парсера каждого формата агент получает описание команды и вызывает готовую утилиту.</p><p>На практике это означает, что когда вы просите десктопный ChatGPT «сделать из этого документа PDF» или «вытащить таблицу из презентации», конвертация может выполняться локально, вызовом LibreOffice или Poppler из этого кэша. Это один из возможных путей: из наблюдения нельзя сказать, какие операции остаются локальными, а какие всё равно отправляют содержимое файла модели или обрабатываются иначе; документации OpenAI о составе runtime и о том, когда он скачивается, в открытом доступе нет.</p><h2>Что стоит учесть</h2><p>Наблюдение сделано на одной машине с macOS. Приложение для Windows может комплектоваться иначе, и переносить цифры на другие платформы не стоит. Не ясно и то, как обновляется runtime: отдельным скачиванием при первом обращении к документам или вместе с приложением.</p><p>Тем, кто следит за дисковым пространством, достаточно заглянуть в тот же путь. Удалять каталог вручную мы бы не советовали: что произойдёт с функциями работы с документами без него, автор не проверял, а официального способа отключить или очистить runtime OpenAI не описывает.</p><p>Разработчикам агентов здесь два практических наблюдения. Первое: в поставке для работы с форматами лежат полтора гигабайта проверенных открытых инструментов, включая LibreOffice, форк OpenOffice.org 2010 года. Второе: инструкции к этим инструментам лежат в виде читаемых файлов рядом с бинарниками, и их можно изучить как пример того, как крупный вендор пишет skills для своей модели.</p><p>Источники: <a href="https://simonwillison.net/2026/Sep/1/codex-libreoffice/">Simon Willison: Codex bundles LibreOffice</a>, <a href="https://help.openai.com/en/articles/20001276-moving-to-the-new-chatgpt-desktop-app">OpenAI Help: Moving to the new ChatGPT desktop app</a></p><p>Изображение на обложке: Tproger</p>]]></content:encoded>
    </item>
    <item>
      <title>JetBrains раскрыла детали взлома Cadence: непропатченный TeamCity</title>
      <link>https://tproger.ru/news/jetbrains-raskryla-detali-vzloma-cadence-nepropatchennyj-teamcit</link>
      <comments>https://tproger.ru/news/jetbrains-raskryla-detali-vzloma-cadence-nepropatchennyj-teamcit?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/jetbrains-raskryla-detali-vzloma-cadence-nepropatchennyj-teamcit</guid>
      <description><![CDATA[<p>JetBrains подтвердила: сервис Cadence взломали через непропатченный TeamCity (CVE-2026-63077). Атакующие получили бэкап 2024 года, AWS-учётки и файлы в S3. Хронология, четыре категории утёкших данных, какие секреты считать скомпрометированными и что сделать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/jetbrains-raskryla-detali-vzloma-cadence-nepropatchennyj-teamcit">JetBrains раскрыла детали взлома Cadence: непропатченный TeamCity</a>»</p>]]></description>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Облачные технологии]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 01 Sep 2026 17:27:47 GMT</pubDate>
      <content:encoded><![CDATA[<p>JetBrains 1 сентября обновила <a href="https://blog.jetbrains.com/pycharm/2026/08/cadence-security-incident-august-2026/">отчёт о взломе Cadence</a>, своего облачного сервиса для запуска вычислений из PyCharm. Атакующие с 8 по 24 августа имели доступ к серверу api.cadence.jetbrains.com через уязвимость <b>CVE-2026-63077</b> в TeamCity, которая позволяла выполнять команды без аутентификации. Сервер, по признанию компании, «должен был быть пропатчен, но не был». Отчёт подписан Дэниелом Галло из JetBrains; последнее обновление датировано 1 сентября, 12:05 по центральноевропейскому времени, предыдущее — 31 августа.</p><p>Для обычного пользователя PyCharm без плагина Cadence новость ничего не меняет. Для тех, кто подключал сервис, JetBrains даёт жёсткую рекомендацию: все учётные данные, к которым Cadence имел доступ, считать скомпрометированными и заменить, включая ключи облаков, токены GitHub и GitLab, SSH-ключи и ключи подписи. Региональной разбивки пострадавших компания не даёт; если сервис у вас был подключён, рекомендации те же.</p><ul><li>Период доступа атакующих: 8–24 августа 2026 года; JetBrains заметила эксплуатацию 23 августа и отключила сервер 24-го.</li><li>Вектор: CVE-2026-63077 в TeamCity, который оркестрировал облачные задачи Cadence; патч на сервер не поставили.</li><li>Утекли имена пользователей, реальные имена, email, время последнего входа и IP; полный бэкап сервера Cadence за 2024 год; AWS IAM-пользователи и их учётные данные; файлы в S3-бакетах JetBrains.</li><li>Мог быть доступен исходный код, синхронизированный из PyCharm; затронуты ли бакеты клиентов, компания не установила.</li><li>Токены плагина Cadence в PyCharm аннулированы; JetBrains просит заменить все секреты, доступные сервису, и проверить репозитории, облака, реестры пакетов и системы деплоя.</li></ul><h2>Что такое Cadence и при чём тут TeamCity</h2><p>Cadence — облачный сервис JetBrains, который через необязательный плагин PyCharm позволяет отправлять тяжёлые вычисления, например обучение моделей, на удалённые машины. Чтобы запускать эти задачи, сервис использовал TeamCity, собственный CI-сервер JetBrains, как оркестратор облачных нагрузок. Именно в этой инсталляции TeamCity была уязвимость CVE-2026-63077, позволяющая удалённо выполнять команды без входа. Компания признаёт, что для этого сервера действовало то же правило, что и для остальных: патчить сразу после выхода исправления. Правило не сработало.</p><blockquote>The server should have been patched, but it was not.</blockquote><h2>Хронология</h2><ul><li>8 августа: атакующие воспользовались уязвимостью TeamCity и получили доступ к серверу api.cadence.jetbrains.com.</li><li>8–24 августа: период активности; в это время были доступны данные пользователей, бэкап, учётные данные AWS и файлы в S3.</li><li>23 августа: JetBrains обнаружила эксплуатацию.</li><li>24 августа: сервер отключён, токены плагина Cadence в PyCharm аннулированы.</li><li>31 августа: первое обновление отчёта с перечнем затронутых данных.</li><li>1 сентября, 12:05 CEST: второе обновление; расследование ещё включает несколько проверок.</li></ul><h2>Что именно утекло</h2><p>Компания перечисляет четыре категории. Первая — персональные данные пользователей Cadence: логины, реальные имена, адреса почты, время последнего входа и IP-адрес, с которого он был. Вторая, самая неприятная, — полная резервная копия сервера Cadence за 2024 год: JetBrains подтвердила, что атакующие получили доступ к её содержимому, а в нём могло быть всё, что сервис хранил на тот момент. Третья — AWS IAM-пользователи с учётными данными, то есть доступ к части облачной инфраструктуры JetBrains. Четвёртая — файлы в S3-бакетах компании; проверить, были ли затронуты бакеты клиентов, JetBrains на момент публикации не смогла.</p><blockquote>We have confirmed that the threat actors accessed data contained in the Cadence server backup from 2024.</blockquote><p>Отдельно компания допускает, что атакующим мог быть доступен исходный код, который пользователи синхронизировали из PyCharm в Cadence для запуска задач. Доказательств извлечения секретов из текущего окружения сервиса, по словам JetBrains, не найдено. Между «не найдено доказательств» и «не было» большая разница, и компания сама рекомендует исходить из худшего: список секретов, которые нужно считать скомпрометированными, включает облачные учётные данные, токены GitHub, GitLab и Bitbucket, учётные данные реестров пакетов и контейнеров, SSH-ключи и ключи подписи.</p><h2>Что сделать, если вы пользовались Cadence</h2><ul><li>Проверьте в PyCharm, стоял ли плагин Cadence; его токены JetBrains уже аннулировала, но это закрывает только вход в сервис.</li><li>Замените всё, что было доступно из задач Cadence: ключи AWS, Google Cloud и Azure и любых других облаков, токены GitHub, GitLab и Bitbucket, учётные данные реестров пакетов и контейнеров, SSH-ключи, ключи подписи.</li><li>Пересмотрите репозитории, которые синхронизировались в сервис, на предмет секретов в коде и истории коммитов: они могли уйти вместе с исходниками.</li><li>Проверьте журналы облачных аккаунтов за 8–24 августа на действия от имени сервисных пользователей, которыми пользовался Cadence; JetBrains отдельно называет AWS и S3, IAM, реестры пакетов и системы деплоя.</li><li>Если в 2024 году вы пользовались Cadence, а потом ушли, ваши данные могли находиться в бэкапе за 2024 год; состав пользователей в нём компания не раскрыла.</li><li>Ждите финальный отчёт: расследование не закончено, и список затронутых данных может расшириться.</li></ul><h2>Урок для всех остальных</h2><p>История примечательна не масштабом, а причиной. Компания, которая сама делает TeamCity, не поставила патч на свой TeamCity, и этого хватило. Для любой команды это напоминание: инвентаризация внутренних сервисов с публичным адресом и автоматическое отслеживание CVE для них — единственная защита от сюжета «должны были пропатчить, но не пропатчили». Второй урок про бэкапы: резервная копия двухлетней давности оказалась столь же ценной для атакующих, как и живая система, потому что секреты в ней никто не ротировал.</p><p>Источник: <a href="https://blog.jetbrains.com/pycharm/2026/08/cadence-security-incident-august-2026/">Cadence security incident, August 2026 (JetBrains)</a></p><p>Изображение на обложке: JetBrains</p>]]></content:encoded>
    </item>
    <item>
      <title>Сканеры атакуют открытые Langflow и Rails: у первого нет патча, у второго есть</title>
      <link>https://tproger.ru/news/dve-kriticheskie-uyazvimosti-ekspluatiruyut-pryamo-sejchas-langflow</link>
      <comments>https://tproger.ru/news/dve-kriticheskie-uyazvimosti-ekspluatiruyut-pryamo-sejchas-langflow?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/dve-kriticheskie-uyazvimosti-ekspluatiruyut-pryamo-sejchas-langflow</guid>
      <description><![CDATA[<p>VulnCheck зафиксировала сотни попыток эксплуатации CVE-2026-0768 в Langflow (CVSS 9.8, выполнение Python от root без аутентификации) и CVE-2026-66066 в Rails Active Storage (CVSS 9.5, чтение файлов и секретов). Условия, версии с исправлением, что проверить.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/dve-kriticheskie-uyazvimosti-ekspluatiruyut-pryamo-sejchas-langflow">Сканеры атакуют открытые Langflow и Rails: у первого нет патча, у второго есть</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Ruby on Rails]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 01 Sep 2026 17:26:48 GMT</pubDate>
      <content:encoded><![CDATA[<p>Компания VulnCheck, по <a href="https://thehackernews.com/2026/09/attackers-exploit-critical-langflow-and.html">сообщению The Hacker News</a> от 1 сентября, зафиксировала сотни попыток эксплуатации двух критических уязвимостей на своих ловушках: <b>CVE-2026-0768</b> в Langflow, визуальном конструкторе ИИ-приложений на Python, и <b>CVE-2026-66066</b> в Ruby on Rails. Первая позволяет без пароля выполнить произвольный Python-код от имени root, вторая — прочитать любые файлы сервера, включая ключи и пароли к базе, отправив специально собранную картинку.</p><p>Обе дыры не новые: <a href="https://github.com/advisories/GHSA-x5pr-rvjj-j6qm">advisory по Langflow</a> опубликован в GitHub Advisory Database ещё 23 января, но исправленная версия в нём не указана, а ZDI в бюллетене <a href="https://www.zerodayinitiative.com/advisories/ZDI-26-034/">ZDI-26-034</a> называет единственной мерой ограничение доступа к сервису. Для Rails исправления есть. Если у вас в проде или на тестовом сервере стоит Langflow либо Rails-приложение принимает файлы от пользователей, проверять нужно сегодня.</p><ul><li>CVE-2026-0768, Langflow: CVSS 9.8, CWE-94, инъекция кода через параметр code в эндпоинте /api/v1/validate/code, аутентификация не нужна, код выполняется от root; исправленная версия в advisory не указана.</li><li>CVE-2026-66066, Rails Active Storage: CVSS 9.5, чтение произвольных файлов при обработке изображений через libvips; уязвимы activestorage до 7.2.3.2, 8.0.x до 8.0.5.1 и 8.1.x до 8.1.3.1.</li><li>VulnCheck насчитала 360 срабатываний на ловушках в нескольких странах; в запросах ищут ключи OpenAI и AWS, файл secret_key Langflow, папку .ssh и историю shell.</li><li>Rails закрывается обновлением activestorage вместе с libvips не ниже 8.13; для Langflow единственная подтверждённая мера — ограничить доступ к сервису доверенными пользователями и сетями.</li><li>При признаках эксплуатации считайте доступные секреты скомпрометированными и меняйте их.</li></ul><h2>Langflow: код в параметре code и никакого патча</h2><p>Langflow — популярный инструмент, где ИИ-пайплайны собираются мышкой из блоков, а под капотом это Python-приложение. У него есть служебный эндпоинт /api/v1/validate/code, который проверяет пользовательский код компонента. Проверка входа там оказалась недостаточной: код из параметра выполняется, а эндпоинт не требует аутентификации. Langflow часто разворачивают в Docker от root, поэтому атакующий получает полный контроль над контейнером и всем, что в нём лежит: ключами моделей, токенами, доступами к базам.</p><blockquote>Authentication is not required to exploit this vulnerability.</blockquote><p>Это уже второй раз, когда тот же эндпоинт подводит: в 2025 году в нём же нашли CVE-2025-3248. По данным VulnCheck, в запросах атакующие целятся в переменные окружения LANGFLOW_SUPERUSER, OPENAI_API*, AWS_ACCESS*, AWS_SECRET*, файл /root/.cache/langflow/secret_key, каталог .ssh и .bash_history. Цель — не сам Langflow, а всё, к чему у него есть ключи. По данным VulnCheck, основной источник трафика на ловушки Langflow находился в России, а попадания фиксировались на canary-системе в Великобритании; атаки на Rails приходили на ловушки в Сингапуре, Израиле и Великобритании.</p><h2>Rails: картинка, которая читает файлы</h2><p>В Rails уязвим Active Storage, стандартный механизм загрузки файлов. Когда приложение обрабатывает изображение, оно передаёт файл в библиотеку libvips. Специально собранный файл заставляет libvips прочитать произвольный путь на сервере и вернуть содержимое в результирующем изображении. Так утекают secret_key_base, master key Rails, пароли БД и облачные учётные данные, а с ними в ряде конфигураций возможно и выполнение кода. Исследователи назвали уязвимость KindaRails2Shell.</p><p>Условия эксплуатации по <a href="https://github.com/advisories/GHSA-xr9x-r78c-5hrm">advisory</a>: libvips как бэкенд обработки и возможность для недоверенного пользователя загрузить файл. Если у вас ImageMagick или файлы грузят только администраторы, риск ниже, но обновиться всё равно стоит. Исправленные версии gem activestorage: 7.2.3.2, 8.0.5.1 и 8.1.3.1; advisory требует также libvips не ниже 8.13 и рекомендует заменить секреты.</p><h2>Что сделать сегодня</h2><ul><li>Langflow: ограничьте доступ ко всему сервису доверенными пользователями и сетями (VPN, reverse proxy с аутентификацией); это единственная мера, которую называет ZDI, потому что исправленной версии в advisory нет. Следите за релизами проекта.</li><li>Проверьте логи на запросы к /api/v1/validate/code с чужих адресов; нашли — считайте все ключи в окружении скомпрометированными и ротируйте их.</li><li>Rails: bundle update activestorage rails до 7.2.3.2, 8.0.5.1 или 8.1.3.1 и выше, обновите libvips до 8.13 или новее; проверьте Gemfile.lock.</li><li>Пока не обновились: на libvips 8.13 и новее включите блокировку недоверенных операций (переменная VIPS_BLOCK_UNTRUSTED или Vips.block_untrusted(true)), для более старых версий обхода advisory не даёт, остаётся отключить обработку файлов от пользователей.</li><li>После инцидента меняйте secret_key_base и master key: с их утечкой атакующий подделывает сессии и расшифровывает credentials.</li></ul><h2>Контекст</h2><p>Обе уязвимости известны с зимы, но волна эксплуатации началась, когда появился рабочий эксплойт и его добавили в автоматические сканеры. VulnCheck отдельно отмечает, что запросы приходили на ловушки в разных странах, то есть кампания не целевая, а ковровая: сканеры ищут любой доступный снаружи экземпляр. Langflow особенно распространён во внутренних ИИ-экспериментах, которые поднимают «на минутку» с публичным IP и забывают, и именно такие установки первыми находят сканеры.</p><p>Источники: <a href="https://thehackernews.com/2026/09/attackers-exploit-critical-langflow-and.html">Attackers exploit critical Langflow and Rails flaws (The Hacker News)</a>, <a href="https://github.com/advisories/GHSA-x5pr-rvjj-j6qm">GHSA-x5pr-rvjj-j6qm: Langflow code injection</a>, <a href="https://github.com/advisories/GHSA-xr9x-r78c-5hrm">GHSA-xr9x-r78c-5hrm: Active Storage arbitrary file read</a>, <a href="https://www.zerodayinitiative.com/advisories/ZDI-26-034/">ZDI-26-034</a></p><p>Изображение на обложке: Langflow, Ruby on Rails</p>]]></content:encoded>
    </item>
    <item>
      <title>Вышел Python 3.15.0rc2: ABI заморожен, финальный релиз 1 октября</title>
      <link>https://tproger.ru/news/vywel-python-3-15-0rc2-abi-zamorozhen-finalnyj-reliz-1-oktyabrya</link>
      <comments>https://tproger.ru/news/vywel-python-3-15-0rc2-abi-zamorozhen-finalnyj-reliz-1-oktyabrya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/vywel-python-3-15-0rc2-abi-zamorozhen-finalnyj-reliz-1-oktyabrya</guid>
      <description><![CDATA[<p>Python 3.15.0rc2 — последний запланированный кандидат перед релизом 1 октября. ABI больше не меняется, значит можно собирать wheels. Разбираем JIT, tail-calling interpreter, free-threading, lazy imports, frozendict и что сделать мейнтейнеру пакета сейчас.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/vywel-python-3-15-0rc2-abi-zamorozhen-finalnyj-reliz-1-oktyabrya">Вышел Python 3.15.0rc2: ABI заморожен, финальный релиз 1 октября</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Языки программирования]]></category>
      <category><![CDATA[Компиляторы]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 01 Sep 2026 17:26:22 GMT</pubDate>
      <content:encoded><![CDATA[<p>1 сентября <a href="https://www.python.org/downloads/release/python-3150rc2/">вышел</a> <b>Python 3.15.0rc2</b>, второй и последний запланированный релиз-кандидат. Для большинства разработчиков это сигнал не «попробовать новинки», а «пора действовать»: с этого момента бинарный интерфейс (ABI) ветки 3.15 больше не меняется, и авторы пакетов с C-расширениями могут собирать wheels, которые будут работать с финальной версией 1 октября.</p><p>Между rc1 и rc2 вошло около 144 исправлений от 76 участников: ошибки, сборка, документация. До финала принимаются только проверенные исправления багов, новых функций не будет. Релиз-менеджер Хьюго ван Кеменаде называет rc2 финальным кандидатом и просит мейнтейнеров тестировать пакеты именно сейчас; ставить rc2 в прод команда не рекомендует.</p><ul><li>3.15.0rc2 — последний запланированный кандидат; ABI заморожен, финальный релиз назначен на 1 октября 2026 года.</li><li>JIT: в среднем на 8–9% быстрее обычного интерпретатора на x86-64 Linux и на 12–13% быстрее tail-calling interpreter на macOS с Apple Silicon; разброс по тестам от замедления на 15% до ускорения больше чем вдвое; цифры предварительные.</li><li>Официальные сборки для Windows x64 используют tail-calling interpreter, сборки для macOS по умолчанию поддерживают free-threading.</li><li>Новое в языке: ленивые импорты через ключевое слово lazy (PEP 810), встроенные типы frozendict (PEP 814) и sentinel (PEP 661).</li><li>Мейнтейнерам: собирайте wheels под 3.15 и гоняйте тесты на rc2; после 1 октября исправление попадёт только в 3.15.1.</li></ul><h2>Что означает «ABI заморожен»</h2><p>ABI — набор правил, по которым скомпилированный код (numpy, pydantic-core, любой пакет с C или Rust внутри) обращается к интерпретатору: размеры структур, порядок полей, сигнатуры функций. Пока ABI меняется, wheel, собранный под бету, может упасть на финальной версии. Теперь команда CPython обещает: «There will be no ABI changes from this point forward in the 3.15 series» (перевод редакции: «С этого момента в серии 3.15 изменений ABI не будет»). Значит, wheel, собранный сегодня под rc2, останется валидным для 3.15.0 и всех 3.15.x.</p><p>На практике это точка, после которой авторы популярных библиотек выкладывают на PyPI сборки для новой версии. Если ваш проект зависит от пакета, который до сих пор не собран под 3.15, самое время открыть issue или прислать PR: у мейнтейнеров есть месяц.</p><h2>Сколько на самом деле даёт JIT</h2><p><a href="https://docs.python.org/3.15/whatsnew/3.15.html">Документация «What's New»</a> приводит две цифры, и их важно не смешивать. На x86-64 Linux JIT даёт 8–9% ускорения в среднем геометрическом относительно обычного интерпретатора. На macOS с процессорами Apple JIT сравнивают с более быстрым tail-calling interpreter, и там выигрыш 12–13%. Разброс по отдельным тестам огромный: от замедления примерно на 15% до ускорения больше чем вдвое. Документация подчёркивает, что цифры предварительные и до финала могут измениться. Так что «Python стал на 10% быстрее» — неверное обобщение: на вашем коде может быть и минус.</p><p>Tail-calling interpreter — другой способ реализовать цикл исполнения байткода, где каждая инструкция вызывает следующую хвостовым вызовом вместо большого switch. В 3.15 именно он идёт в официальных 64-битных сборках для Windows. Официальные сборки для macOS по умолчанию получают поддержку free-threading, то есть режим без GIL остаётся экспериментальным, но доступным без пересборки.</p><h2>Три новинки языка, которые вы заметите</h2><p><b>Ленивые импорты</b> (PEP 810): ключевое слово lazy перед import откладывает загрузку модуля до первого обращения. Это ответ на старую проблему медленного старта CLI-утилит, которые тянут тяжёлые зависимости «на всякий случай».</p><p><b>frozendict</b> (PEP 814) — неизменяемый словарь, встроенный в язык: если все его ключи и значения хешируемы, его можно использовать как ключ другого словаря, и его безопасно отдавать наружу как константу. <b>sentinel</b> (PEP 661) закрывает старый обходной приём с _MISSING = object() для «значение не передано»: у таких маркеров появляется нормальный repr и корректное поведение при pickle.</p><h2>Что сделать до 1 октября</h2><ul><li>Поставьте rc2 рядом с рабочей версией: официальные установщики и исходники на python.org; в uv или pyenv сначала проверьте, что версия появилась в их списках (uv python list), и прогоните тесты проекта.</li><li>Если у вас пакет с расширениями: соберите wheels под 3.15 и выложите на PyPI; ABI больше не изменится.</li><li>Замерьте свой код с JIT и без: флаги сборки и переменные окружения описаны в документации; средним цифрам не верьте.</li><li>Проверьте зависимости на совместимость с ленивыми импортами: код, который полагается на побочные эффекты при импорте, с lazy сломается.</li><li>Нашли регрессию — сообщайте в трекер CPython сейчас: после релиза исправление уйдёт только в 3.15.1.</li><li>Если пользуетесь корпоративным зеркалом PyPI, убедитесь, что оно синхронизирует wheels с тегом cp315: первые сборки под 3.15 появятся в ближайшие недели.</li></ul><h2>Контекст</h2><p>Разработка 3.15 началась 7 мая 2025 года, первая бета вышла 7 мая 2026-го, rc1 — 4 августа. После финального релиза ветка получит около двух лет исправлений ошибок и затем ещё примерно три года обновлений безопасности, по обычному графику CPython.</p><p>Источники: <a href="https://www.python.org/downloads/release/python-3150rc2/">Python 3.15.0rc2 (python.org)</a>, <a href="https://docs.python.org/3.15/whatsnew/3.15.html">What's New in Python 3.15</a></p><p>Изображение на обложке: Python Software Foundation</p>]]></content:encoded>
    </item>
    <item>
      <title>RAG-бот для любого сайта на Python за 60 строк кода</title>
      <link>https://tproger.ru/articles/rag-bot-dlya-lyubogo-sajta-na-python-za-60-strok-koda</link>
      <comments>https://tproger.ru/articles/rag-bot-dlya-lyubogo-sajta-na-python-za-60-strok-koda?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/rag-bot-dlya-lyubogo-sajta-na-python-za-60-strok-koda</guid>
      <description><![CDATA[<p>Соберите простого RAG-бота на Python, который читает любую веб-страницу и отвечает на вопросы только по её тексту. Гайд с кодом и объяснениями.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/rag-bot-dlya-lyubogo-sajta-na-python-za-60-strok-koda">RAG-бот для любого сайта на Python за 60 строк кода</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 26 Aug 2026 12:06:57 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если попросить языковую модель ответить по свежей документации или конкретному сайту, она с блеском выдумает то, чего там нет. Стандартное решение — <strong>Retrieval-Augmented Generation (RAG)</strong>: сначала достать реальный текст, найти в нём нужные куски и только потом передать их модели в качестве контекста.</p><p>В этой статье соберём работающего RAG-бота, который отвечает на вопросы о любой веб-странице, примерно на 60 строках Python. Никаких парсеров HTML, headless-браузеров и сложных фреймворков — только requests, локальная Ollama и чистый Markdown.</p><h2>Что такое RAG</h2><p>RAG (retrieval-augmented generation) — это подход, при котором языковая модель не отвечает по своей памяти, а сначала ищет релевантные фрагменты во внешнем тексте, а потом генерирует ответ на их основе. Это резко снижает галлюцинации и позволяет работать с данными, которых не было в обучающей выборке.</p><ul><li>RAG-бот на Python укладывается в ~60 строк и работает без облачных LLM.</li><li>Самое неприятное в RAG-пайплайне — очистка HTML; готовый Markdown API убирает эту работу.</li><li>Текст разбивается на чанки (~1200 символов), каждый превращается в эмбеддинг и сравнивается с эмбеддингом вопроса.</li><li>Локальные модели nomic-embed-text и llama3 через Ollama не требуют иностранных карт и VPN.</li><li>Качество ответа зависит от качества исходного текста: сырой HTML зашумляет поиск, чистый Markdown улучшает ретривл.</li></ul><h2>Что мы соберём</h2><p>Архитектура бота простая и универсальная:</p><ol><li>Отправляем URL в сервис извлечения текста и получаем чистый Markdown.</li><li>Разбиваем Markdown на фрагменты (чанки) по границам абзацев.</li><li>Превращаем каждый чанк в вектор — эмбеддинг.</li><li>То же самое делаем с вопросом пользователя.</li><li>Находим чанки, ближайшие к вопросу, по косинусной близости.</li><li>Отдаём найденные фрагменты + вопрос языковой модели с инструкцией отвечать только по контексту.</li></ol><p>Такая схема легко масштабируется: можно заменить локальную Ollama на OpenAI, Anthropic или российские модели, а векторы перенести в Qdrant или Chroma.</p><h2>Что понадобится</h2><ul><li>Python 3.9+</li><li>Библиотека requests</li><li>Локально запущенная Ollama с моделями nomic-embed-text и llama3</li><li>Доступ к API извлечения текста (в примере — <a href="https://rapidapi.com/xiaobao882026/api/web-to-markdown-json-api" rel="noopener noreferrer">Web to Markdown/JSON API</a>; бесплатный тариф даёт 50 запросов в сутки)</li></ul><h2>Код бота</h2><p>Сохраните скрипт как rag_bot.py и подставьте свой ключ, если сервис извлечения текста требует авторизации:</p><p><b>На что обратить внимание:</b><br />Если вы используете RapidAPI-версию сервиса, запрос к API_URL обычно требует заголовка X-RapidAPI-Key. Без ключа бесплатный endpoint может вернуть 401.</p><h3>Разбор по частям</h3><p>fetch_markdown — единственный внешний вызов. Сервис сам забирает страницу, убирает навигацию, баннеры и футер и возвращает Markdown: заголовки, абзацы, списки. Это освобождает от зависимостей вроде BeautifulSoup или headless Chrome.</p><p>chunk_markdown режет текст на фрагменты примерно по 1200 символов, не разрывая абзацы. Мелкие чанки дают более точный ретривл, но увеличивают число эмбеддингов; для начала 1200 символов — хороший баланс.</p><p>embed и cosine превращают текст в векторы и считают их близость. Модель nomic-embed-text из Ollama бесплатна, быстрая и неплохо понимает русский и английский.</p><p>answer эмбеддит вопрос, выбирает три самых похожих чанка и строит промпт с жёстким ограничением: отвечать только по контексту. Это главная страховка от галлюцинаций.</p><h2>Запуск</h2><p>Установите зависимости и скачайте модели в Ollama:</p><p>Если всё в порядке, в консоли появится примерно такой результат:</p><h2>Почему важен чистый Markdown</h2><p>Если скормить эмбеддинг-модели сырой HTML, в векторах окажутся теги &lt;div&gt;, меню навигации и копирайты из футера. В результате поиск похожести выдаст «Copyright © 2026» вместо полезного ответа. Чистый Markdown — заголовки, абзацы, списки — улучшает качество ретривла почти бесплатно.</p><p>Если не хочется зависеть от внешнего API, можно заменить fetch_markdown на один из альтернативных вариантов:</p><ul><li><a href="https://github.com/adbar/trafilatura" rel="noopener noreferrer">trafilatura</a> — библиотека на Python для извлечения главного текста.</li><li><a href="https://r.jina.ai/http://example.com" rel="noopener noreferrer">r.jina.ai/http://URL</a> — бесплатный сервис без ключа.</li><li><a href="https://www.firecrawl.dev/" rel="noopener noreferrer">Firecrawl</a> — API с поддержкой сканирования сайтов целиком.</li><li><a href="https://github.com/scrapingbee" rel="noopener noreferrer">ScrapingBee</a> — прокси + рендеринг для сложных страниц.</li></ul><p>Для российских разработчиков локальная Ollama особенно удобна: модели качаются бесплатно, не нужны иностранные карты, а инференс идёт на своём железе.</p><h2>Куда развивать</h2><ul><li>Направьте бота на документацию, чейнджлог или блог конкурента и задавайте вопросы по ним.</li><li>Замените Ollama на OpenAI, Anthropic, Gemini или российские модели — функция answer меняется в двух строках.</li><li>Сохраняйте эмбеддинги в векторную БД: Chroma, Qdrant или FAISS, чтобы индексировать сразу много страниц.</li><li>Используйте формат json вместо Markdown, если нужна структура: параграфы, заголовки, ссылки — отдельно.</li></ul><h2>Выводы</h2><p>RAG — не магия, а последовательность простых шагов: получить чистый текст, разрезать его на фрагменты, найти ближайшие к вопросу и отдать их модели. Весь минимальный пайплайн укладывается в короткий Python-скрипт, который можно запустить на своём ноутбуке.</p><blockquote>RAG работает ровно так хорошо, каков текст, который вы ему скармливаете. Уберите самую утомительную часть — очистку HTML, — и останется интересное: поиск и генерация.</blockquote><p>Исходник идеи — статья <a href="https://dev.to/bao001_xiao_37db0a18ce6b2/chat-with-any-website-build-a-rag-bot-in-60-lines-of-python-1m1k" rel="noopener noreferrer">«Chat With Any Website: Build a RAG Bot in ~60 Lines of Python»</a>. Попробуйте собрать бота на своей странице и посмотрите, где он справляется, а где начинает фантазировать.</p>]]></content:encoded>
    </item>
    <item>
      <title>Llama 2 на DigitalOcean за $12: self-hosting гид</title>
      <link>https://tproger.ru/articles/llama-2-na-digitalocean-za-12-self-hosting-gid</link>
      <comments>https://tproger.ru/articles/llama-2-na-digitalocean-za-12-self-hosting-gid?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/llama-2-na-digitalocean-za-12-self-hosting-gid</guid>
      <description><![CDATA[<p>Разбираем, как запустить Llama 2 7B в Docker на DigitalOcean с FastAPI API. Реальная смета, пошаговые команды и советы по безопасности.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/llama-2-na-digitalocean-za-12-self-hosting-gid">Llama 2 на DigitalOcean за $12: self-hosting гид</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Облачные технологии]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 05 Aug 2026 10:02:46 GMT</pubDate>
      <content:encoded><![CDATA[<p>Плата за OpenAI API для небольшого pet-проекта легко переваливает за несколько сотен долларов в месяц. Альтернатива — развернуть открытую языковую модель самостоятельно и платить только за виртуальный сервер. В этом гайде разбираем, как запустить <b>Llama 2 7B</b> в собственном Docker-контейнере на DigitalOcean и получить production-ready HTTP API меньше чем за 15 минут работы с терминалом.</p><p>Автор оригинального руководства указывает заголовок «за $5/месяц», но на практике минимально жизнеспособная конфигурация обходится в <b>$6–12/месяц</b>. За $5 дроплет даёт всего 1 ГБ ОЗУ — для семимиллиардной модели этого катастрофически мало. Ниже — реальные цифры и честная смета.</p><h2>Что такое Llama 2 и зачем её self-hostить</h2><p><b>Llama 2</b> — семейство открытых больших языковых моделей от Meta. Версия 7B содержит 7 миллиардов параметров, понимает английский и ряд других языков, умеет писать код, отвечать на вопросы и дополнять текст. Лицензия разрешает коммерческое использование при соблюдении простых правил, а веса можно скачать с Hugging Face.</p><p><b>Self-hosting</b> в данном случае означает, что вы сами устанавливаете модель на арендованный сервер, контролируете окружение, не делитесь данными пользователей с третьими лицами и не зависите от чужих rate limit. Главная экономика: при интенсивном использовании собственный инференс обходится в десятки раз дешевле облачных API.</p><ul><li>Llama 2 7B можно запустить на DigitalOcean Droplet с 2 vCPU и 4 ГБ ОЗУ при 4-битном квантовании.</li><li>Реальная стоимость — $12/месяц за Droplet плюс около $0,50 за трафик, а не $5.</li><li>Docker + FastAPI дают воспроизводимое окружение и HTTP API из коробки.</li><li>Для загрузки весов с Hugging Face нужен токен и принятая лицензия на Llama 2.</li><li>Для публичного доступа обязательны HTTPS, базовая аутентификация и rate limiting.</li></ul><h2>Что понадобится для развёртывания</h2><h3>Аппаратная часть</h3><ul><li>DigitalOcean Droplet на Ubuntu 22.04 LTS.</li><li>Минимум: 1 vCPU / 2 ГБ ОЗУ ($6/месяц) — только для экспериментов.</li><li>Рекомендуемый: 2 vCPU / 4 ГБ ОЗУ ($12/месяц) — стабильная работа с квантованием.</li><li>80 ГБ SSD: ~13 ГБ уйдёт на Docker-образ, модель и кэш.</li></ul><h3>Программная часть</h3><ul><li>SSH-ключ и базовое умение работать в терминале.</li><li>Docker и Docker Compose для управления контейнером.</li><li>Аккаунт на <a href="https://huggingface.co">Hugging Face</a> с принятой лицензией Llama 2 и API-токеном.</li><li>Nginx или аналогичный reverse proxy для вывода API в интернет.</li></ul><p><b>Российская специфика:</b> сайт DigitalOcean периодически попадает под блокировки Роскомнадзора. Для регистрации и управления Droplet может понадобиться VPN. Альтернативы — Hetzner, Selectel, Yandex Cloud или другие европейские и российские провайдеры; логика развёртывания от этого почти не меняется.</p><h2>Шаг 1. Создаём Droplet и подключаемся по SSH</h2><p>В панели DigitalOcean нажимаем <b>Create → Droplet</b>. Выбираем ближайший к целевой аудитории регион — для России это обычно Франкфурт или Амстердам. В качестве образа указываем Ubuntu 22.04 x64, тип — Basic Shared CPU, конфигурацию — 2 vCPU / 4 ГБ RAM. Авторизацию настраиваем через SSH-ключ, парольный вход отключаем.</p><p>Сгенерировать ключ и подключиться можно так:</p><h2>Шаг 2. Устанавливаем Docker и зависимости</h2><p>После подключения обновляем пакеты и ставим Docker официальным скриптом. Добавляем root в группу docker, чтобы не писать sudo перед каждой командой.</p><h2>Шаг 3. Собираем Docker-образ с FastAPI</h2><p>Приложение состоит из трёх файлов: Dockerfile, requirements.txt и app.py. Ключевой трюк — CPU-версия PyTorch и библиотека bitsandbytes, которая позволяет загрузить 7-миллиардную модель в 4 ГБ видеопамяти/ОЗУ.</p><h3>Dockerfile</h3><h3>requirements.txt</h3><h2>Шаг 4. Пишем FastAPI-приложение</h2><p>Приложение загружает модель при старте, кэширует её в /app/models и предоставляет три endpoint: /health, /info и /generate. Квантование в 4 бита снижает точность незначительно, но уменьшает потребление памяти в 3–4 раза.</p><p>Создаём файл с токеном Hugging Face. Никогда не коммитьте его в репозиторий: в продакшене используйте переменные окружения или секрет-менеджер.</p><h2>Шаг 5. Собираем и запускаем контейнер</h2><p>Первый запуск занимает 10–15 минут: скачиваются зависимости PyTorch и веса модели. Чтобы не качать веса при каждом перезапуске, подключаем Docker volume.</p><p>Для удобного управления добавляем docker-compose.yml:</p><h2>Шаг 6. Проверяем API</h2><p>Когда в логах появится Uvicorn running on http://0.0.0.0:8000, тестируем endpoint'ы:</p><p>Ответ должен содержать сгенерированный текст, количество токенов и время инференса. На CPU одна генерация из 100 токенов занимает 4–10 секунд — это нормально для бюджетного дроплета.</p><h2>Шаг 7. Безопасно выводим API в интернет</h2><p>Открывать порт 8000 напрямую в интернет небезопасно. Ставим Nginx как reverse proxy, настраиваем HTTPS через Let's Encrypt и базовую аутентификацию. Для rate limiting используем limit_req в Nginx или облачный firewall DigitalOcean.</p><p>Минимальная конфигурация Nginx выглядит так:</p><p><b>Про безопасность:</b> Llama 2 — мощная модель, способная генерировать вредоносные инструкции, персональные данные или нежелательный контент. Не выставляйте публичный API без аутентификации, логируйте запросы и рассмотрите фильтрацию промптов на уровне приложения.</p><h2>Выводы</h2><p>Self-hosted Llama 2 — рабочий способ снизить затраты на генеративный ИИ в pet-проектах и прототипах. При правильном квантовании семимиллиардная модель помещается в бюджетный дроплет, а Docker + FastAPI превращают развёртывание в рутинную процедуру. Главное — не экономить на RAM и не забывать про безопасность публичного API.</p><blockquote>Собственный инференс — не волшебная кнопка, а инструмент с чёткой областью применения: он выигрывает там, где важны предсказуемость расходов, приватность данных и отсутствие лимитов.</blockquote><p>Если соберётесь повторить гайд, начните с $12 Droplet и протестируйте нагрузку вручную. А если DigitalOcean недоступен — переносите тот же Docker Compose на Hetzner, Selectel или Yandex Cloud без изменения кода.</p><p><b>Источник:</b> <a href="https://dev.to/ramosai/how-to-deploy-llama-2-on-digitalocean-for-5month-complete-self-hosting-guide-13dl">How to Deploy Llama 2 on DigitalOcean for $5/Month: Complete Self-Hosting Guide</a> — RamosAI, Dev.to.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как я написал AI-бота на aiogram 3 с помощью нейросетей, выжил при 2500+ пользователей и почему SQLite "всё ещё торт"</title>
      <link>https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p</link>
      <comments>https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Нуя]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p</guid>
      <description><![CDATA[<p>Технический кейс создания Telegram-бота Щёлк-ГДЗ (ИИ-репетитор по фото). Разбор архитектуры на Python (aiogram 3, aiosqlite), работы с API Gemini и решения проблем под нагрузкой 2500+ пользователей. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p">Как я написал AI-бота на aiogram 3 с помощью нейросетей, выжил при 2500+ пользователей и почему SQLite "всё ещё торт"</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[Flash]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Маркетинг]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Пет-проект]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[VPS]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 26 Jul 2026 15:47:08 GMT</pubDate>
      <content:encoded><![CDATA[<p>На дворе 2026 год. Очередной постмортем микро-SaaS'а в Телеге.</p><p>Без маркетинга и успешного успеха. Только боль, костыли и суровый прод! Мой пет-проект - бот «Щёлк-ГДЗ». Это ИИ-помощник, который решает школьные задачки по фоткам, видео, гс, тг-кружкам и PDF. Под капотом <b>Python 3.12.3</b>, <b>aiogram 3</b>, <b>aiosqlite</b>,<b> апи OpenRouter </b>(модель Gemini 3.0 Flash) и <b>Робокасса</b>. Крутится всё это на дешёвом VPS в Нидерландах. Держит 2500+ пользователей и не падает.</p><p>В этой статье расскажу, как за 9 месяцев построил логичную архитектуру, победил ошибку FloodWait при стриминге ответа ИИ и почему в условиях 1 гига RAM обычная SQLite - отличное решение.</p><h2>Нейронка вместо джуна и Уроборос багов</h2><p>Сразу признаюсь... С нуля я это не писал :) Синтаксис мне генерили LLM-ки. Начинал с Gemini 2.5 Pro, затем перешел на 3.0 Pro, а сейчас использую 3.1 Pro.</p><p>Многие думают, что нейронка сама напишет проект "под ключ", но это миф. Я <b>никогда </b>не доверял ИИ проектирование архитектуры и использовал его как продвинутый<i> StackOverflow</i> (скармливал конкретную задачу (например, написать SQL-миграцию) и получал кусок кода).</p><p><i>ИИ — это не архитектор, а джун на спидах. </i></p><p>Как только логика усложнялась - гемини ловил <b>«Уроборос багов»</b>. Кидаешь баг <b>А</b> - он его фиксит, но появляется ошибка <b>Б</b>. Скармливаешь и её - фиксит, но возвращается баг <b>А</b>. Цикл замкнулся. Лечилось только созданием новых чатов, в которые я кидал код и писал запросы типа "Найди критические ошибки, логические дыры и баги".</p><p>Про продуктовую логику нейронка вообще не слышала. В первой версии рефералки был баг, где любой(если его аккаунта ещё нет в БД) мог написать в конце ссылки что любые 9 цифр (<i>?start=1234567890</i>) и получить бонусы.</p><p>Также были ошибки с гонкой состояний при оплатах и активациях промокодов, которые тоже фиксил запросами в новые чаты с ИИ. Так я исправил около 20 архитектурных дыр.</p><h2>Немного про архитектуру.</h2><p>Чтобы код не превратился в нечитаемую лапшу на 5000 строк, я жестко разбил всё на модули. Архитектура бота выглядит так:</p><figure><img src="https://media.tproger.ru/user-uploads/139416/2026-07-08/dfc8dd70-365f-4f70-bbbf-2b00b503bef4.webp" alt="" /></figure><p><b>main.py</b> - это точка входа с настройкой логгеров, коннетами aiohttp и запуском APScheduler</p><p><b>database.py</b> - вся работа с БД</p><p><b>neiro.py</b> - логика общения с апи OpenRouter (стриминг, сжатие фоток через Pillow, JSON-контекст)</p><p><b>states.py</b> - классы состояний aiogram.fsm.state (например, PaymentProcess, GdzMode)</p><p><b>utils.py</b> - легковесные утилиты. Например лок пользователей (защита от спама запросами):</p><p>Хэндлеры вынесены в отдельную папку handlers/:</p><p><b>handlers/common_handlers.py</b> - главное меню с обработкой /start (рефералки, utm-метки).</p><p><b>handlers/gdz_handlers.py</b> - сам процесс ИИ-решения (вход в GdzMode, прием фото, видео, кружочков).</p><p><b>handlers/pay_handlers.py</b> - это логика платежей (Robokassa) и "Умная корзина".</p><p><b>handlers/tasks_handlers.py</b> - квесты (выдача премиума за подписку на каналы спонсоров).</p><p>Сборку интерфейсов вынес в <b>all_def.py</b>. Не люблю, когда в хэндлерах генерится полотно текста с кнопками. Там же лежат функции склонения слов (1 запрос, 2 запроса, 5 запросов). А в <b>settings.py </b>лежат списки с рандомными ответами бота, чтоб казался живым.</p><p>Так же в <b>settings.py</b> я сделал кэширование картинок, тоесть при первом запуске бот грузит фото меню как <b>BufferedInputFile</b>, сохраняет <b>file_id</b> от Телеграма и дальше шлет картинки моментально по ID. Сервак говорит спасибо за сэкономленный трафик)</p><p>В <b>handlers/common_handlers.py</b> находится первичная маршрутизация. Вот так обрабатываются рефералки, переходы с сайта, с рекламы и другое при старте:</p><h2>Диета по токенам</h2><p>Хранить бесконечную историю диалогов дорого и бессмысленно. В бесплатной версии храню <b>10 последних сообщений</b> (5 пар вопрос-ответ), а в преме — <b>30. </b></p><p>Но фотки весят большое кол-во токенов. Если премиум-юзер закинет 30 фоток, OpenRouter выставит мне огромный счет. В итоге я прикрутил ограничение: Из <b>30 сообщений</b> ИИ видит только <b>10 последних картинок. </b></p><p>Старые фотки тупо вырезаю из JSON. Подменяю на системный промпт:</p><p>С довольно неплохой моделью (gemini 3.0 flash) это работает как часы (она честно признается, что забыла картинку, а не выдумывает что-то из воздуха).</p><h2>Стриминг, FloodWait и защита баланса</h2><p>Чтобы бот не выглядел тормозом, я сделал стриминг ответа от ИИ в <b>neiro.py</b>. Я обновляю сообщение в Телеграме чанками по мере получения их от ОпенРоутера.</p><p>Но если делать <b>message.edit_text </b>слишком часто, ловишь <b>FloodWait</b>. В итоге я выставил интервал в 0.7 секунд и обернул всё в жесткий<b> try/except</b>:</p><p>Стрим при этом не прерывается. Поспали и погнали дальше :) Если на этапе обработки файла или стриминга падает критическая ошибка - честно возвращаю юзеру запрос на баланс.</p><p>В <b>handlers/gdz_handlers.py</b> это выглядит так:</p><h2>SQLite тащит</h2><p>Почему не <b>Postgre</b>? Потому что для микро-SaaS с 2,5к пользователей<b> SQLite</b> хватает за глаза. Но в асинхронной среде она любит кидать ошибку <b>database is locked</b>.</p><p>Чтобы этого избежать, я включил <b>WAL-режим</b> при инициализации пула, разделил коннекты на <b>db_writer</b> и <b>db_reader</b>, а сложные операции доверил самому <b>SQL</b>.</p><p>Например, 00:00 запускается крон-таска, которая собирает огромную аналитику, начисляет всем активным юзерам +1 ежедневный запрос и сбрасывает просроченные подписки. И это всё это работает атомарно внутри <b>database.py</b>:</p><h2>Умная корзина на APScheduler</h2><p>Когда дело дошло до монетизации, всплыли две проблемы:</p><p>Первая: юзер оплатил, но забыл нажать кнопку <b>«✅ Я оплатил»</b> в боте. Бот ждет, юзер ждет, товар не выдается, поддержка кипит.</p><p>Вторая: юзер сформировал счёт и передумал ("брошенная корзина").</p><p>Вместе с LLM я с нуля изучил <b>apscheduler </b>и убил двух зайцев фоновыми задачами. Теперь в <b>handlers/pay_handlers.py </b>при генерации ссылки на оплату я создаю две отложенные таски:</p><h4>Как это работает?</h4><p>Через 5 минут срабатывает <b>async def auto_check_payment()</b>, которая тихо стучится в робокассу. Если статус<b> success</b> - бот сам начисляет запросы на баланс и радует клиента. Если статус <b>pending</b> - таска умирает, и в дело вступает 30-минутная таска.</p><p>Но она не шлёт спам вслепую, а лезит в БД и проверяет 3 бизнес-правила:</p><p>1. <b>await db.has_successful_payment_recently</b> - Не купил ли он другой товар за последние 3 часа?</p><p>2. <b>await db.get_latest_invoice_id</b> - А это точно самый последний сгенерированный им счет?</p><p>3. <b>await db.can_send_agitation</b> - Не присылали ли мы ему агитацию недавно?</p><p>Если проверки пройдены, то юзер получает сообщение: <i>"⏳ Домашка сама себя не решит! Ты начал оформлять покупку, но оплата так и не прошла..."</i>. Это поднимает конверсию оплат.</p><h2>Финал</h2><p>Почему я не использую <b>Redis </b>для стейтов? Ответ банален: мой дешевый VPS имеет всего 1 гб оперативки. Пул <b>aiohttp</b>, In-Memory стейты и асинхронные таски и так жрут 70% RAM. Редис тупо не влезет.</p><p>Как говорится, <i>работает — не трогай.</i></p><p>Сейчас проект обзавелся сайтом-витриной и продолжает развиваться. В планах - искать и чинить новые баги. Перееду на более мощное железо, когда сервак начнет физически задыхаться.</p><p>Готов ответить на вопросы по архитектуре и послушать советы в комментариях \(^^)/</p><p><i>P.s. вот ссылка на первую статью о моём проекте: </i></p><p>P.s. Если кто-то хочет потестить вживую(не реклама), то юз бота в тг <a>@gdzshchelk_bot</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Своя self-hosted двухсерверная VPN-архитектура. Подробный путь разработки и ошибки, с которыми вы можете столкнуться</title>
      <link>https://tproger.ru/articles/svoya-self-hosted-dvuhservernaya-vpn-arhitektura-podrobnyj-put-r</link>
      <comments>https://tproger.ru/articles/svoya-self-hosted-dvuhservernaya-vpn-arhitektura-podrobnyj-put-r?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Роман Фролов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/svoya-self-hosted-dvuhservernaya-vpn-arhitektura-podrobnyj-put-r</guid>
      <description><![CDATA[<p>Проект представляет из себя быстрый способ воссоздать архитектуру состоящую из 2 серверов (локальный + удаленный) с определенными сервисами, которые решают специфические задачи. Проект сделан прежде всего для меня, а также для людей которые хотят свой готовый self-hosted сервер из коробки с полной системой обслуживания</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/svoya-self-hosted-dvuhservernaya-vpn-arhitektura-podrobnyj-put-r">Своя self-hosted двухсерверная VPN-архитектура. Подробный путь разработки и ошибки, с которыми вы можете столкнуться</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Ruby]]></category>
      <category><![CDATA[unix]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Музыка]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[DIY]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[VPN]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Cloudflare]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Мессенджер]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[NFT]]></category>
      <category><![CDATA[Машин]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[DeFi]]></category>
      <category><![CDATA[Grafana]]></category>
      <category><![CDATA[VPS]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 26 Jul 2026 15:46:26 GMT</pubDate>
      <content:encoded><![CDATA[<p>Всё
 началось с того, что меня перестал устраивать мой подход к 
развертыванию личных сервисов. Я решил переписать всё с нуля. К тому же 
это был отличный повод получить новые навыки и попробовать инструменты, 
до которых давно не доходили руки.</p><p>Арендовать мощные VPS под 
ресурсоемкие задачи в текущих реалиях выходит неоправданно дорого. При 
этом дома у меня уже был выделенный неттоп (мини-ПК) с 16 ГБ оперативной
 памяти на базе энергоэффективного процессора Intel N95.</p><p>Пройдя 
путь от простых Bash-скриптов и сторонних туннелей до полностью 
автоматизированной инфраструктуры, я создал проект ServeHub-2. В этой 
статье я подробно разберу, как эволюционировала сеть проекта, почему 
декларативный подход победил императивный и с какими неочевидными багами
 пришлось столкнуться в процессе автоматизации.</p><h2>История настройки сетевой архитектуры</h2><h3>Использование Tuna</h3><p>В
 самом начале проекта я еще не думал о VPN как о способе пробития NAT и 
основе, на которой будет строиться вся система. Первое, к чему я пришел —
 сервис Tuna. Если кратко, это аналог Cloudflare Tunnel за условные 300 
рублей. У него очень приятный веб-интерфейс и невероятно простая 
настройка. Однако для полноценной независимой архитектуры он не подошел 
по двум причинам:</p><ul><li>Сервера Tuna находятся вне контроля пользователя.</li><li>На удаленном сервере нельзя развернуть свои сопутствующие сервисы.</li></ul><p>В целом сервис действительно удобный, но для моих задач он оказался слишком ограничивающим.</p><p>Вот
 пример конфига для tuna (все максимально просто, создаем контейнеры для
 ssh туннеля, чтобы был доступ ssh, а также основной http туннель с 
привязкой к nginx или любому другому прокси если такой есть):</p><h3>В поисках гибкости: тесты Pangolin и переход к FRP</h3><p>После Tuna я решил двигаться в сторону собственного контроля и попробовал Pangolin.
 Однако решение быстро отвалилось: для дешёвого сервера оно оказалось 
слишком «тяжёлым» и избыточным. Главные минусы — ощутимый оверхед по 
ресурсам и лишний слой принудительной веб-аутентификации перед самими 
сервисами. Это ломало нормальное взаимодействие с родными мобильными 
клиентами (вроде Element для Matrix или Bitwarden для паролей), где 
повторная авторизация в браузере просто не нужна.</p><p>На смену пришел FRP (Fast Reverse Proxy).
 Он выполнял ту же функцию проброса, но хостил я его уже на собственном 
арендованном VPS в режиме Layer 4 (TCP). Это дало абсолютную гибкость: 
внешний VPS не заглядывал внутрь пакетов и ничего не расшифровывал, а 
просто пересылал сырой поток домой. Кроме того, это отлично 
оптимизировало расходы: вместо 300 рублей за Tuna и ещё 300 рублей за 
отдельный VPN, я стал платить всего 500 рублей за один стойкий VPS, 
который мог настраивать как хочу. (к тому же можно было обойтись даже 
дешевле, так как VPS с 4 гб оперативки загружен всего лишь на 40%, 
процессор всего на 20%-40%)</p><p>FRP состоит из 2 конфигурационных 
файлов (один на удаленном сервере frp server, другой на локальном frp 
client), все также довольно просто, однако его можно использовать на 
разных слоях. У меня он брал трафик за стандартный TCP и передавал его 
на локальный сервер.</p><p>Также доп. фишка в том что основная 
конфигурация происходит именно в frpc, который, в свою очередь, передает
 часть настроек на frp на удаленном сервере.</p><p>frpc.toml:</p><p>frps.toml:</p><h3>Полноценный переезд на WireGuard (AmneziaWG) и 8 часов отладки</h3><p>Со временем архитектура эволюционировала в сторону полноценной VPN-сети на базе WireGuard, а точнее — его модификации AmneziaWG. Причин для этого шага было несколько:</p><ul><li>Безопасность: FRP всё же открывал внутренние ресурсы в публичный интернет, оставляя их доступными для сканеров портов.</li><li>Удобство маршрутизации:
 Все участники сети стали равноправными узлами в одной виртуальной 
локальной подсети. Больше не нужно было настраивать постоянные 
односторонние пробросы.</li><li>Дополнительный бонус:
 Поскольку удаленный VPS был куплен в Нидерландах, через этот же VPN я 
автоматически получил безопасный доступ ко всем зарубежным ресурсам.</li></ul><p>Для реализации схемы на удаленном VPS был развернут контейнер wg-easy с поддержкой AmneziaWG, а на домашнем мини-ПК — клиентский контейнер Amnezia (сборка из Dockerfile с использованием amnezia-tools  и модулем ядра хоста).</p><p>И
 именно здесь я поймал самый изнурительный баг проекта. После 
развертывания трафик упорно шёл только в одну сторону. Часов 8 ушло на 
диагностику iptables, маршрутов и чтение зарубежных форумов (что 
бесполезно, учитывая специфику наших блокировок). Оказалось, провайдер 
просто дропал обратный трафик стандартного WireGuard, так как пакеты шли
 без маскировки. Причина крылась в docker-compose.yml: у меня было прописано image: ghcr.io/wg-easy/wg-easy:latest. Как выяснилось, тег latest  на Docker Hub намертво прилип к старой 14-й версии, а поддержка параметров AmneziaWG появилась только в ветке 15.x. Изменение тега на конкретную версию (15.3) решило проблему за секунду.</p><p>Благодаря
 переходу конфигурация получилась максимально простой, основные отличия 
от стандартной документации выделил в коде: (в основном то, на что 
пришлось долго рыть информацию)</p><h3>Настройка Nginx</h3><p>Чтобы
 сервисы были доступны исключительно внутри подсети VPN, я задействовал 
Nginx. Доступ к приложениям был жестко ограничен на уровне конфигурации —
 веб-сервер принимает запросы только из диапазона IP-адресов 10.8.0.0/24. Любые попытки постучаться на сервер из внешнего интернета без активного VPN-туннеля автоматически сбрасываются Nginx.</p><p>Логика
 распределения завязана на proxy-протоколе: Nginx на удаленном VPS 
выступает основным входным узлом, принимает зашифрованный трафик, 
заворачивает его в заголовки с реальным IP-адресом клиента и через 
туннель перекидывает на локальный Nginx домашнего сервера. Локальный 
веб-сервер уже сам расшифровывает SSL и распределяет трафик по конечным 
Docker-контейнерам, сохраняя реальные IP в логах безопасности.</p><p>Вставлю
 один кусок кода из nginx на удаленной машине для примера, так все 
остальное примерно похоже (конфиги использовались в виде .template):</p><h3>SSL-сертификаты</h3><p>Для получения валидных SSL-сертификатов я настроил работу через автоматический Certbot по challenge-валидации DNS-01 c API Webnames. Сам домен привязан к внутреннему IP-адресу 10.8.0.1.
 Проверка через DNS позволила выпустить единый wildcard-сертификат на 
весь домен и его поддомены без необходимости держать открытым 80-й порт 
веб-сервера наружу.</p><p>Уточню, что certbot запускается автоматически 
во время выполнения Ansible плейбука, поэтому самому кроме указания 
переменных ничего делать не нужно.</p><p>В
 итоге получилась схема, при которой все сервисы доступны по красивым 
доменным именам с HTTPS, но абсолютно невидимы для внешнего интернета.</p><p>Вот настройка certbot:</p><h2>История софта</h2><p>Параллельно
 с сетевой структурой развивался и сам набор приложений. Изначально я 
хотел собрать в одном месте утилиты, которыми пользуюсь каждый день, но в
 процессе селфхостинга быстро понимаешь: нельзя просто накидать 
контейнеров и надеяться, что мини-ПК справится, а конфигурационные файлы
 не превратятся в кашу.</p><h3>Первый стек и оптимизация</h3><p>Первыми на домашнем сервере прижились медиа-сервисы: Navidrome для стриминга музыки и Audiobookshelf
 для аудиокниг и подкастов. Они легковесные, имеют отличные мобильные 
клиенты с синхронизацией прогресса и полностью закрывают мои 
потребности. Позже к ним добавился Nextcloud как единое независимое облако для файлов, контактов и семейных документов.</p><p>Затем встал вопрос безопасного хранения паролей. Сначала я смотрел в сторону оригинального Bitwarden, но в итоге я выбрал Vaultwarden
 — альтернативный сервер на Rust, полностью совместимый с API Bitwarden.
 Он потребляет считанные мегабайты оперативной памяти и работает 
идеально. Дополнительно для удобства управления всей этой распределенной
 Docker-инфраструктурой в локальный стек был добавлен Portainer. (+ Portainer Agent на удаленный сервер)</p><p>Из интересных моментов, где мне пришлось немного больше возиться, чем с остальными сервисами это nextcloud настройка:</p><h3>Ошибки проектирования: почему я удалил Matrix (Synapse)</h3><p>Не все решения прошли проверку временем. На этапе использования Tuna и FRP я развернул сервер Matrix (Synapse)
 для защищенного обмена сообщениями. Мне казалось это крутой идеей, но 
когда я окончательно перешел на AmneziaWG, целесообразность мессенджера 
внутри закрытого туннеля сошла на нет.</p><p>Synapse требовал слишком 
много ресурсов, впустую расходовал оперативку домашнего ПК и усложнял 
конфиг Nginx. При этом реальной пользы для семьи он не приносил. Для 
критических алертов инфраструктуры и повседневного общения проще и 
эффективнее оказалось использовать Telegram. (плюс алертинг настроен 
именно через него) В итоге я полностью выпилил Synapse из стека, 
освободив ресурсы.</p><p>Вместо него я добавил в связку к wg-easy локальный AdGuard Home.
 Теперь он работает прямо внутри VPN-сети: очищает весь трафик от 
рекламы и трекеров на лету, кэширует DNS-запросы и не дает истории 
веб-серфинга улетать внешним провайдерам.</p><p>Основной проблемой с 
которой я столкнулся при использовании AdGuard Home, так это то что я 
так и не понял как заставить использовать AmneziaVPN клиент AdGuard как 
основной DNS, при этом AmneziaWG работает прекрасно. Как я понимаю дело в
 том что AmneziaWG работает намного проще на уровне ip и у него нету 
никаких доп фильтров, настроек и подобного, поэтому он просто берет 
данные из конфига.</p><h3>От костылей на Bash к декларативному Ansible</h3><p>Весь
 стек на обоих серверах разворачивался через bash скрипты, что было уж 
очень плохо с точки зрения идемпотентности. Во первых из-за bash мне 
постоянно приходилось очищать сервера, так как нормальных проверок у 
меня не было и писать я их не хотел, а также было много костылей с 
импортом переменных, записями в файлы и подобным.</p><p>Так я пришел к декларативному подходу и Ansible.
 Теперь вся конфигурация описывается в виде плейбуков и ролей, 
отражающих конечное желаемое состояние серверов. Конфиденциальные данные
 перенесены в файл secrets.yml, а хрупкие конструкции 
автоматизированы через шаблоны Jinja2. Проект стал идемпотентным: если 
шаги уже выполнены, Ansible их просто пропускает.</p><p>В итоге 
получилось несколько yaml файлов для стандартной настройки системы 
(bootstrap_os.yml) и для настройки каждого из хостов (setup_local.yml и 
setup_remote.yml). В итоге теперь все что нужно чтобы полностью с нуля 
развернуть проект - скачать Ansible, несколько других зависимостей на 
свой рабочий пк и запустить один manage_deploy.sh, в котором можно будет
 выбрать сценарий как будет вести себя Ansible и спокойно дождаться 
разворачивания сервисов.</p><p>Также благодаря Ansible я удобно 
реализовал переносимость проекта. Так как я решил не использовать тома 
docker, и вместо этого храню все в папках, чтобы перенести старые данные
 проекта нужно просто скопировать папку apps-data и положить ее в нужное
 место и все само заработает после повторного развертывания проекта. В 
Ansible выделяется отдельная пауза для этого.</p><p>Вот пример основного плейбука deploy.yml:</p><h3>Тестирование мультидистрибутивности с помощью Vagrant</h3><p>Проект
 изначально затачивался под работу на трех дистрибутивах: Ubuntu, Debian
 и Arch Linux. Тестировать Ansible-плейбуки прямо на рабочей локальной 
машине (в моем случае — EndeavourOS) слишком рискованно, а создавать 
виртуальные машины руками — долго и неудобно.</p><p>Решением стал Vagrant,
 позволяющий за пару минут развернуть чистые ОС в VirtualBox из готовых 
образов. Но в процессе настройки мультивендорного стенда в режиме 
сетевого моста (public_network) всплыли две критические проблемы:</p><ol><li>Конфликт DNS:
 По умолчанию Vagrant создает NAT-интерфейс для управления нодой. При 
включении второго (публичного) интерфейса для локальной сети ломался 
дефолтный DNS-резолвер. Проблему пришлось решать принудительной очисткой
 и перезаписью файла /etc/resolv.conf через inline-скрипт автоматизации Vagrant.</li><li>Проблема с GRUB на Debian: В используемом базовом образе generic/debian12
 конфигурация GRUB сохраняла жесткую привязку к конкретному имени диска 
из окружения сборщика. При повторном развертывании плейбуков на тестовом
 стенде это приводило к сбоям загрузчика. Чтобы автоматизировать 
очистку, пришлось внедрить скрипт, который на лету определяет имя 
системного диска через lsblk и автоматически передает правильные параметры в загрузчик через утилиту debconf-set-selections.</li></ol><p>Вот код Vagrantfile: (в node.vm.provision происходит основное решение ошибок)</p><p>Надежная система бэкапов на базе BorgmaticДля создания резервных копий я внедрил Borgmatic
 (удобную надстройку над дедуплицирующим инструментом Borg Backup). Весь
 процесс автоматизирован с помощью связки системных юнитов borgmatic.service и borgmatic.timer. В бэкап уходят две ключевые директории: apps-data (конфигурации приложений и баз данных) и PersonalData (медиатека: музыка, книги, подкасты, файлы Nextcloud).</p><p>Развертывание
 системы бэкапов полностью берет на себя Ansible. Мне достаточно указать
 UUID внешнего жесткого диска — скрипт сам проверит его наличие в 
системе, примонтирует в нужную директорию, создаст зашифрованный 
репозиторий и настроит политику ротации (хранение 7 ежедневных, 4 
еженедельных и 6 ежемесячных копий). Также в Prometheus выведен 
мониторинг самого репозитория .borg для отслеживания его размера и статуса успешности архивации.</p><p>Главная
 проблема при бэкапе работающих Docker-контейнеров — риск скопировать 
базу данных в «битом» или неконсистентном состоянии, если в момент 
создания архива в нее шла активная запись. Чтобы решить эту проблему, я 
задействовал механизм хуков в конфигурации Borgmatic.</p><p>Перед началом резервного копирования автоматически срабатывает команда остановки контейнеров проекта (docker-compose down),
 а после успешного завершения (или в случае возникновения непредвиденной
 ошибки) контейнеры автоматически поднимаются обратно в фоновом режиме. 
Для удобного просмотра архивов и быстрого восстановления файлов я 
развернул веб-интерфейс Borg UI.</p><p>Конфигурация borgmatic: (использую как Jinja2 шаблон, чтобы Ansible в плейбуках сам подставил переменные)</p><h2>Наблюдаемость (Observability) уровня Enterprise</h2><p>В последних релизах (v1.2.0 и v1.3.0) фокус проекта сместился на мониторинг и работу с логами.</p><h3>Эволюция алертинга и переход на Gatus</h3><p>Изначально для мониторинга доступности я смотрел на Uptime Kuma, но отказался из-за отсутствия удобной декларативной настройки через конфиги. Затем я развернул связку Blackbox Exporter и Alertmanager
 с уведомлениями в Matrix. Но тут крылась логическая несостыковка: весь 
алертинг был завязан на локальном ПК, и в случае его аппаратного отказа я
 бы просто лишился уведомлений.</p><p>Тогда я решил вернуть Uptime Kuma,
 но развернуть его на удаленном VPS в качестве внешнего «сторожа» и 
автоматизировать его настройку через Python-скрипт с библиотекой uptime-kuma-api. Но и тут ждали «грабли» — библиотека не обновлялась три года и намертво ломалась на свежих версиях Kuma.</p><p>В итоге идеальным решением стал Gatus.
 Он изначально проектировался под управление через YAML-конфиги и 
поддерживает отправку алертов, если сервер не отвечает. Из-за специфики 
фронтенда Gatus (он не умеет работать из подкаталога типа /gatus), мне пришлось перенести его и wg-easy на полноценные субдомены gatus. и wireguard.</p><p>Для Gatus получился простой конфиг:</p><p>Для мониторинга аппаратных ресурсов удаленного и локального серверов была развернута связка node-exporter + cAdvisor. Все уведомления теперь приходят мгновенно в Telegram-бота. Чтобы 
избежать лавины одинаковых сообщений (например, при перезагрузке хоста),
 в Alertmanager настроена жесткая группировка и дедупликация событий. 
Также добавлен экспортер для AdGuard Home, выводящий статистику 
заблокированных запросов в Grafana.</p><h3>Централизованные логи: Loki + Grafana Alloy</h3><p>В релизе v1.3.0 в стек была добавлена централизованная система сбора логов Loki. Вместо устаревшего Promtail в качестве агента сбора я применил Grafana Alloy.</p><p>Он
 эффективно собирает логи со всех запущенных Docker-контейнеров, парсит 
их и передает в Loki. Теперь вся история событий, ошибок веб-сервера 
Nginx или падений внутренних приложений доступна в едином интерфейсе 
Grafana с возможностью удобной фильтрации через LogQL, что значительно 
упрощает отладку.</p><p>Конфиг Loki:</p><p>Конфиг Grafana Alloy: (локальный конфиг)</p><h2>Интерфейс: переход на Homepage</h2><p>Изначально для 
домашней страницы я написал кастомную минималистичную HTML-панель с 
Glassmorphism-дизайном. Выглядело это красиво, но добавлять новые 
сервисы вручную через постоянную правку исходного кода было крайне 
неудобно.</p><figure><img src="https://media.tproger.ru/user-uploads/139471/2026-07-15/8a8f2752-8aac-4aa5-898e-98affd01cdff.webp" alt="" /><figcaption>HTML + CSS</figcaption></figure><p>В итоге я заменил самописную страницу на полноценный комьюнити-проект Homepage.
 Это дало некоторую гибкость: вся панель настраивается через простые 
YAML-файлы и поддерживает встроенные виджеты интеграции. Пока что она 
простенькая, но возможно в будущем сделаю что-то более продвинутое.</p><figure><img src="https://media.tproger.ru/user-uploads/139471/2026-07-15/90c65d4a-1310-4a30-b1ad-9e4d3083959d.webp" alt="" /><figcaption>Homepage</figcaption></figure><h2>Заключение</h2><p>Постарался
 подробно рассказать о структуре проекта и как я к этому пришел, очень 
много я не добавил, если пост зайдет, то я обязательно более подробно 
пройдусь по некоторым моментам: как я настраивал Matrix и на каком 
моменте я решил его убрать, как собирал локальный amnezia 
контейнер-клиент, что нового я добавил в релизе 1.4.0 и другое.</p><p>Также
 я перешел с manage_deploy.sh на отдельный GUI клиент, который 
будет полностью автоматизировать процесс подготовки к deploy. (написан на Go Wails). Про это тоже возможно сделаю статью.</p><p>Вся кодовая база проекта, подробная документация, инструкции по развертыванию открыты и доступны для сообщества:</p><p>🔗 GitHub-репозиторий: <a href="https://github.com/canntstand/ServeHub-2" rel="noopener noreferrer nofollow">https://github.com/canntstand/ServeHub-2</a></p><p>Это
 моя первая статья на этом сайте и в то же время первый личный проект, которому я 
отдал так много времени (3 месяца). Буду рад вашему фидбеку в 
комментариях!</p>]]></content:encoded>
    </item>
    <item>
      <title>OpenCode: бесплатный ИИ-агент для Python прямо в терминале</title>
      <link>https://tproger.ru/articles/opencode-besplatnyj-ii-agent-dlya-python-pryamo-v-terminale</link>
      <comments>https://tproger.ru/articles/opencode-besplatnyj-ii-agent-dlya-python-pryamo-v-terminale?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/opencode-besplatnyj-ii-agent-dlya-python-pryamo-v-terminale</guid>
      <description><![CDATA[<p>Разбираем OpenCode — open-source ИИ-агент для Python. Установка, подключение Gemini, режимы Plan/Build, AGENTS.md и рефакторинг кода. Проверьте в деле!</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/opencode-besplatnyj-ii-agent-dlya-python-pryamo-v-terminale">OpenCode: бесплатный ИИ-агент для Python прямо в терминале</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 23 Jul 2026 10:39:14 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы проводите в терминале больше времени, чем в браузере, а Cursor и GitHub Copilot кажутся слишком тяжёлыми, есть альтернатива. <b>OpenCode</b> — это open-source ИИ-агент, который работает прямо в консоли, понимает контекст Python-проекта и умеет рефакторить код по запросу. При этом базовая версия обходится без подписки: достаточно бесплатного ключа Gemini от Google.</p><p>OpenCode запускается в терминале как текстовый интерфейс (TUI) и ведёт себя как собеседник, которого вы явно направляете. Попросите проанализировать функцию, объяснить баг, добавить типизацию или переписать модуль — и агент сделает это с учётом всего проекта, а не только вставленного фрагмента. Поддерживается более 75 провайдеров, включая Anthropic, OpenAI и Google Gemini.</p><p>В отличие от облачных IDE с ИИ, OpenCode не заставляет загружать код на чужие серверы: он общается с моделью из вашей локальной среды, а файлы остаются на вашей машине. Для командной работы есть файл AGENTS.md — аналог инструкции для агента, который загружается в начале каждой сессии.</p><ul><li>OpenCode — бесплатный open-source ИИ-агент для терминала с поддержкой 75+ моделей.</li><li>Устанавливается одной командой curl, через Homebrew или npm; для старта достаточно ключа Gemini.</li><li>Plan-режим предлагает план правок без изменения файлов, Build-режим сразу применяет их.</li><li>AGENTS.md задаёт правила проекта: стиль кода, архитектуру и команды запуска.</li><li>Встроенные LSP-серверы (Pyright для Python) и веб-интерфейс отличают OpenCode от конкурентов.</li></ul><h2>Установка и первый запуск</h2><p>Самый быстрый способ — официальный скрипт. Он определяет платформу, скачивает бинарник и прописывает путь в PATH. Альтернативы — Homebrew для macOS/Linux и npm-пакет, если Node.js уже стоит.</p><h3>Другие способы установки</h3><p>После установки проверьте версию. На момент публикации гайда актуальна 1.14.20, но у вас может быть свежее:</p><p>Если команда не найдена, перезагрузите конфигурацию shell:</p><p>Для Windows лучший опыт — WSL. Храните проекты внутри файловой системы WSL, а не на диске C:, иначе производительность упадёт.</p><h2>Подключаем модель</h2><p>Запустите opencode без аргументов — откроется TUI. Чтобы добавить провайдера, введите слэш-команду /connect и выберите Google. Ключ можно получить бесплатно в Google AI Studio: вход через Google-аккаунт, пара кликов — и ключ готов.</p><p>После подключения выберите модель. Для повседневных задач подходит Gemini 3 Flash Preview — быстрая и дешевая (в данном случае бесплатная) модель. Переключить модель позже можно через /models.</p><p><b>Где хранятся ключи:</b><br />Токены сохраняются в ~/.local/share/opencode/auth.json. Скрипт запросит ключ повторно, только если вы захотите сменить провайдера.</p><p>Проверьте работу простым вопросом:</p><h2>AGENTS.md — память проекта</h2><p>Главная фишка OpenCode — понимание контекста. Каждый раз при запуске агент читает файл AGENTS.md из корня проекта. Там можно описать архитектуру, стиль кода, команды тестирования и форматирования, а также специфические требования к проекту.</p><p>Создать заготовку помогает команда /init. Она проанализирует структуру проекта — файлы, импорты, директории — и сгенерирует черновик. После этого файл стоит дополнить своими правилами.</p><p>Важно: после ручного изменения AGENTS.md перезапустите сессию или попросите агента перечитать файл. Также полезно периодически вызывать /init, чтобы документ актуализировался под текущее состояние кода.</p><h2>Plan и Build: два режима работы</h2><p>OpenCode работает в двух режимах, между которыми переключаются клавишей Tab. Это защита от импульсивных правок.</p><ul><li><b>Plan</b> — агент предлагает план изменений, но не трогает файлы. Это аналог предварительного просмотра diff.</li><li><b>Build</b> — агент сразу вносит правки в проект. Используйте, когда уверены в задаче.</li></ul><p>Ещё есть варианты модели: default, low и high. Режим default автоматически регулирует глубину рассуждений под сложность задачи, что экономит токены на простых вопросах и не даёт ошибиться на сложных. Переключать вручную можно через Ctrl + T.</p><h3>Контекст одним символом</h3><p>Чтобы добавить файл в контекст, используйте символ @. Начните печатать имя файла — откроется fuzzy-поиск. А если сообщение начинается с восклицательного знака !, OpenCode выполнит shell-команду и включит её вывод в беседу.</p><h2>Рефакторим Python-проект</h2><p>Разберём реальный сценарий: у нас есть скрипт обработки CSV, который «работает, но стыдно показать». Попросим OpenCode привести его в порядок.</p><p>Сначала переключитесь в Plan-режим и задайте запрос с контекстом файла:</p><p>OpenCode, опираясь на AGENTS.md, найдёт типичные проблемы: отсутствие if __name__ == "__main__":, нет type hints, магические числа, неинформативные сообщения об ошибках. В конце он предложит план — например, выделить main(), добавить аннотации, заменить SystemExit на ValueError и ввести константы.</p><p>Если план устраивает, переключитесь в Build и подтвердите — агент применит изменения. После этого останется запустить тесты:</p><p>Если что-то пошло не так, команда /undo откатывает последнее изменение. Можно вызывать её несколько раз, чтобы вернуться к нужному состоянию.</p><h2>Что отличает OpenCode от конкурентов</h2><h3>Переключение моделей на лету</h3><p>Claude Code и Cursor привязаны к своим моделям. В OpenCode можно подключить сразу несколько провайдеров и переключаться между ними через /models без перезапуска. Это удобно, когда одна модель лучше справляется с архитектурой, а другая — с мелкими правками.</p><h3>Встроенные LSP-диагностики</h3><p>OpenCode использует Language Server Protocol и поставляется с более чем 30 встроенными языковыми серверами. Для Python по умолчанию работает Pyright, который подсвечивает ошибки типов, неопределённые имена и проблемы с импортами. Эти диагностики передаются модели, поэтому агент видит то же, что и ваш редактор.</p><h3>Веб-интерфейс</h3><p>Если терминал кажется тесным для длинных ответов, запустите opencode web в отдельной вкладке. Поднимется локальный сервер на 127.0.0.1, откроется браузер, и вы получите ту же беседу в графическом виде. Терминал и веб-интерфейс работают одновременно и делят одно состояние сессии.</p><h2>Практические советы</h2><ul><li>Начинайте каждую сессию с проверки AGENTS.md — это экономит токены и повышает качество ответов.</li><li>Сложные правки сначала прогоняйте в Plan-режиме: так проще поймать неверные предположения агента.</li><li>Для российских разработчиков бесплатный Gemini — самый доступный вариант: не требует иностранной карты и работает из РФ с ограничениями.</li><li>Не забывайте про /compact: длинные сессии занимают контекст, и свёртка истории ускоряет работу.</li><li>Коммитьте AGENTS.md в Git: так вся команда получает единые инструкции для ИИ-помощника.</li></ul><h2>Выводы</h2><p>OpenCode — удобный вариант для разработчиков, которые хотят ИИ-помощника в терминале без привязки к платным подпискам. Особенно он выигрывает там, где важен контекст проекта: благодаря AGENTS.md и режимам Plan/Build агент не превращается в слепого исполнителя, а работает как внимательный напарник.</p><blockquote>Инструменты вроде OpenCode меняют не то, чтобы мы писали меньше кода, а то, как мы думаем о правках. Планирование перед изменением файлов — привычка, которую стоит перенести и в обычную разработку.</blockquote><p>Если вы ещё не пробовали терминальных ИИ-агентов, OpenCode — хорошая точка входа. Установка занимает минуту, первый ключ Gemini бесплатный, а дальше можно решать: оставить его как основной инструмент или использовать в связке с привычным редактором.</p><p><b>Источники:</b></p><ul><li><a href="https://realpython.com/opencode-guide/" rel="noopener noreferrer">How to Use OpenCode for AI-Assisted Python Coding — Real Python</a></li><li><a href="https://opencode.ai/" rel="noopener noreferrer">Официальный сайт OpenCode</a></li></ul>]]></content:encoded>
    </item>
    <item>
      <title>itertools в Python: ленивые итераторы без лишних циклов</title>
      <link>https://tproger.ru/articles/itertools-v-python-lenivye-iteratory-bez-liwnih-ciklov</link>
      <comments>https://tproger.ru/articles/itertools-v-python-lenivye-iteratory-bez-liwnih-ciklov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/itertools-v-python-lenivye-iteratory-bez-liwnih-ciklov</guid>
      <description><![CDATA[<p>Разбираем модуль itertools из стандартной библиотеки Python: как ленивые итераторы экономят память, какие функции использовать чаще всего и где поджидают подводные камни.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/itertools-v-python-lenivye-iteratory-bez-liwnih-ciklov">itertools в Python: ленивые итераторы без лишних циклов</a>»</p>]]></description>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Функциональное программирование]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 12 Jul 2026 17:20:36 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если ваш код регулярно превращается в пять вложенных for и огромный список, который держится в памяти только ради того, чтобы тут же быть выброшенным в мусор, — скорее всего, вам не хватает itertools. Этот модуль стандартной библиотеки Python собрал ленивые итераторы, которые превращают обработку данных в конвейер: элементы поступают, преобразуются и уходят дальше без лишних промежуточных коллекций.</p><p>itertools — это не просто набор функций, а способ мыслить о данных как о потоке. Вместо того чтобы сначала строить список, а потом его перебирать, вы описываете, что должно произойти с каждым элементом, и получаете результат по мере необходимости. В статье разберём три семейства инструментов, покажем рабочие примеры и укажем на типичные ловушки.</p><p>itertools — модуль стандартной библиотеки Python, который предоставляет ленивые итераторы для композиции цепочек обработки данных.</p><p>Ленивость означает, что элементы создаются по запросу, поэтому можно работать с большими файлами и бесконечными потоками, не загружая всё в память.</p><p>Функции разделены на три группы: бесконечные итераторы (count, cycle, repeat), конечные итераторы (chain, islice, groupby и др.) и комбинаторные (product, permutations, combinations).</p><p>Многие рутинные задачи — flatten, батчинг, скользящее окно, группировка — решаются в одну строку, если знать правильную комбинацию функций.</p><p>Подводные камни: groupby требует предварительной сортировки по тому же ключу; итераторы одноразовые; tee() может неэкономно расходовать память.</p><h2>Зачем вообще ленивые итераторы?</h2><p>Обычный подход в Python — сгенерировать список и пройтись по нему циклом. Это просто и читаемо, пока данных немного. Когда файл весит несколько гигабайт, а строки приходят из сети, список становится дорогим: он требует памяти и времени, хотя в каждый момент вам нужен лишь один текущий элемент.</p><p>Ленивый итератор не строит коллекцию целиком. Он помнит текущее состояние и умеет «добыть» следующий элемент. Поэтому itertools работает с любыми итерируемыми источниками — файлами, генераторами, сетевыми потоками — и не требует, чтобы весь объём данных поместился в оперативную память.</p><p><b>Память vs скорость:</b><br />Ленивость экономит память, но не всегда ускоряет код. Если данные всё равно нужны целиком — например, для сортировки — список может оказаться быстрее. Используйте итераторы там, где важна потоковая обработка.</p><h2>Три семейства инструментов</h2><p>Документация Python делит функции модуля на три группы. Такое разделение помогает быстро выбрать инструмент: нужна бесконечная последовательность, преобразование конечной или перебор комбинаций.</p><h3>Бесконечные итераторы: count, cycle, repeat</h3><p>count — это range без конца. Ему можно задать начальное значение и шаг, и он будет выдавать числа до тех пор, пока его не остановят снаружи. cycle бесконечно повторяет переданную последовательность, а repeat — бесконечно или заданное число раз возвращает один объект.</p><p>Классический трюк — сочетание map и count: map(f, count()) работает как математическое табулирование tabulate(f), знакомое из SML и Haskell.</p><h3>Конечные итераторы: chain, islice, groupby и другие</h3><p>Это самая многолюдная группа. Здесь есть функции для склейки последовательностей, фильтрации, нарезки, группировки и пакетной обработки. Их объединяет одно: на вход подаётся конечный или контролируемый итератор, на выходе — тоже итератор.</p><p>batched появился в Python 3.12 и сразу стал незаменимым инструментом: партии запросов к API, пакеты строк для вставки в базу, страницы данных. Последняя партия может быть короче — это поведение по умолчанию.</p><p>groupby часто путают с SQL-аналогом, но это не тот же инструмент. Python-версия группирует только подряд идущие одинаковые ключи, поэтому перед ней обычно нужна сортировка по тому же ключу. Если забыть про это, результат покажется случайным.</p><h3>Комбинаторные итераторы: product, permutations, combinations</h3><p>Эти функции генерируют декартово произведение, перестановки и сочетания. Они незаменимы в тестировании, алгоритмах на графах, задачах оптимизации и даже в простых играх. Все они ленивые, поэтому можно перебирать комбинации по одной, не строя гигантский список.</p><p>product с аргументом repeat удобен для перебора многомерных конфигураций: product([0, 1], repeat=3) даст все двоичные triples, как в таблице истинности.</p><h2>Подводные камни, за которые хватаются новички</h2><p>Несмотря на простоту отдельных функций, у модуля есть несколько особенностей, которые легко превратить в баг.</p><ul><li>groupby работает только с подряд идущими одинаковыми ключами. Перед вызовом сортируйте данные по тому же ключу, иначе группы разобьются.</li><li>Итераторы одноразовые. После list(iterator) исходный итератор опустошён, и второй проход по нему даст пустой результат.</li><li>tee копирует данные во внутренний буфер, пока все производные итераторы не прочитают их. Если один итератор сильно отстаёт, память может расти не хуже списка.</li><li>zip_longest с бесконечным итератором никогда не остановится. Ограничивайте такие комбинации islice или takewhile.</li><li>product полностью потребляет входные итераторы, чтобы построить пулы значений. С бесконечными последовательностями его использовать нельзя.</li></ul><h2>Рецепты: от простого к составному</h2><p>Документация Python включает раздел рецептов — готовые комбинации функций, которые решают частые задачи. Некоторые из них настолько удобны, что со временем превращаются в полноценные функции модуля: так появились accumulate, compress и pairwise.</p><p>Для sliding_window сейчас часто используют рецепт из документации или аналог из more-itertools, где функция уже реализована и хорошо протестирована.</p><h2>Выводы</h2><p>itertools — это не библиотека для красивых однострочников, а инструмент для правильного мышления о данных. Он помогает отказаться от лишних промежуточных списков, писать компактные конвейеры и работать с потоками, которые не помещаются в память. Главное — не гнаться за краткостью любой ценой: иногда явный цикл понятнее, чем цепочка из пяти функций.</p><blockquote>Together, they form an iterator algebra making it possible to construct specialized tools succinctly and efficiently in pure Python.</blockquote><p>Источник: <a href="https://docs.python.org/3/library/itertools.html">itertools — Functions creating iterators for efficient looping</a>. Если в вашем коде до сих пор царят вложенные циклы и огромные списки — попробуйте заменить их на поток. Скорее всего, получится короче, быстрее и понятнее.</p>]]></content:encoded>
    </item>
    <item>
      <title>Метаклассы в Python: как работает фабрика классов</title>
      <link>https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov</link>
      <comments>https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov</guid>
      <description><![CDATA[<p>Разбираем, что такое метаклассы в Python, как type создаёт классы и когда стоит писать свой метакласс. Примеры кода и практические рекомендации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov">Метаклассы в Python: как работает фабрика классов</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Объектно-ориентированное программирование]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 09 Jul 2026 12:12:32 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если класс в Python — это шаблон, по которому создаются объекты, то метакласс — это шаблон, по которому создаются сами классы. Идея звучит рекурсивно, но именно в ней скрывается ответ на вопрос, почему в Python всё — объект: даже определение класса можно собрать, изменить или зарегистрировать программно.</p><p>В этой статье разберём, как внутри устроены метаклассы, зачем нужен встроенный type с тремя аргументами и когда стоит писать свой метакласс, а когда — обойтись наследованием или декоратором.</p><h2>Класс — тоже объект</h2><p>В Python 3 любой класс является экземпляром метакласса. По умолчанию этим метаклассом выступает type. Поэтому запись class Foo: pass интерпретатор превращает примерно в такой вызов:</p><p>С точки зрения языка разницы почти нет: и в том, и в другом случае получается объект класса Foo, у которого есть атрибуты, методы и собственный тип. Проверить это можно прямо в интерпретаторе:</p><p>type сам является экземпляром себя — это одна из тех особенностей Python, которые сначала удивляют, а потом помогают понять, что классы и объекты в языке живут по одним правилам.</p><p>Метакласс — это класс, экземплярами которого являются другие классы.</p><p>В Python 3 по умолчанию метаклассом любого класса является type.</p><p>Вызов type(name, bases, dict) создаёт класс динамически.</p><p>Собственный метакласс наследуется от type и переопределяет __new__ или __init__.</p><p>Чаще всего задачу решают наследованием или декоратором класса — метакласс нужен редко.</p><h2>type — не только функция проверки типа</h2><p>Всем знаком вызов type(x), который возвращает тип объекта. Но если передать type три аргумента, он превращается в фабрику классов:</p><ul><li>первый аргумент — имя класса (становится __name__);</li><li>второй — кортеж базовых классов (становится __bases__);</li><li>третий — словарь пространства имён (становится __dict__).</li></ul><p>Этот механизм лежит в основе любого определения класса. Когда Python видит ключевое слово class, он сначала собирает тело класса в словарь, а затем вызывает метакласс, чтобы создать сам класс.</p><p>Пример — класс с методом, собранный вручную:</p><p>Такой код редко пишут в production, но он наглядно показывает, что класс — это всего лишь объект, созданный вызовом фабрики. А значит, эту фабрику можно заменить на свою.</p><h2>Как написать свой метакласс</h2><p>Чтобы изменить процесс создания класса, наследуемся от type и переопределяем __new__. Этот метод отвечает за создание самого класса, поэтому в нём можно добавить общие атрибуты, проверить имя или зарегистрировать класс в каталоге.</p><p>Здесь AutoAttrMeta автоматически добавляет атрибут version каждому классу, который её использует. То же самое можно сделать через наследование, но метакласс действует на этапе создания класса, а не при вызове методов.</p><p>Помимо __new__, часто переопределяют __init__ метакласса: он получает уже созданный класс и может его донастроить. А __call__ контролирует создание экземпляров класса — именно он вызывает __new__ и __init__ объекта.</p><h2>А точно нужен метакласс?</h2><p>Тим Петерс, автор «Дзена Python», как-то сказал, что метаклассы — это магия, которую 99% разработчиков никогда не понадобится. Если вы сомневаетесь, нужны ли они вам, — скорее всего, не нужны.</p><blockquote>Метаклассы — это более глубокая магия, чем та, о которой 99% пользователей должны беспокоиться. Если вы сомневаетесь, нужны ли они вам, значит, не нужны.</blockquote><p>Для сравнения — три способа дать классам общий атрибут:</p><h3>Наследование</h3><h3>Декоратор класса</h3><h3>Метакласс</h3><p>Наследование и декоратор читаются проще и не лезут в механику создания класса. Метакласс выигрывает, когда поведение должно быть неизбежным для всех наследников или когда логика завязана на сам процесс построения класса.</p><p><b>Когда метакласс действительно уместен:</b><br />— автоматическая регистрация подклассов в плагиновой системе;<br />— валидация объявлений полей (например, ORM проверяют типы атрибутов);<br />— принудительный единый API для большого семейства классов;<br />— реализация паттернов вроде Singleton на уровне класса.</p><h2>Практический пример: автоматическая регистрация плагинов</h2><p>Представьте, что вы пишете расширяемую систему: каждый новый адаптер должен попадать в общий реестр. Метакласс может добавлять класс в реестр сразу при определении, без ручного вызова регистрации.</p><p>Такой подход удобен, потому что автор плагина просто объявляет класс, а реестр обновляется сам. В реальных фреймворках — например, в старых версиях Django — похожая идея используется для построения моделей данных.</p><h2>Выводы</h2><p>Метаклассы — не ежедневный инструмент, но понимание их работы делает код Python прозрачнее. Вы начинаете видеть, что class — это тоже объект, а type — всего лишь фабрика, которую при желании можно заменить.</p><p>Главное правило: если задачу решает наследование или декоратор класса, не тащите метакласс. А если без контроля за созданием класса никуда — смело берите type и наследуйтесь от него.</p><p>Источник и дополнительное чтение: <a href="https://realpython.com/python-metaclasses/">Python Metaclasses</a> на Real Python.</p>]]></content:encoded>
    </item>
    <item>
      <title>Git в Telegram: как я избавился от JSON, победил Markdown и получил security by design</title>
      <link>https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu</link>
      <comments>https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Robin Gad]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu</guid>
      <description><![CDATA[<p>Git в Telegram? Без JSON, с SQLite, победой над Markdown и security by design. Код, схема БД, факапы и ссылка на бота.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu">Git в Telegram: как я избавился от JSON, победил Markdown и получил security by design</a>»</p>]]></description>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[GitLab]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[IBM]]></category>
      <category><![CDATA[Мессенджер]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Markdown]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Jul 2026 06:39:38 GMT</pubDate>
      <content:encoded><![CDATA[<p>В своём Telegram-канале я время от времени предлагаю подписчикам выбрать очередную «бредовую» идею для реализации. На этот раз победил Git в Telegram: чтобы можно было инитить проекты, пушить файлы, коммитить — и всё это прямо в мессенджере.</p><p>С практической точки зрения проект на**й не нужен. Есть GitHub, GitLab и куча нормальных инструментов. Но как эксперимент — почему бы и нет? Чисто посмотреть, можно ли заставить Telegram работать как VCS.</p><h2>Почему не JSON</h2><p>На старте я думал: «Положу всё в JSON, на кой мне база данных?» Проектов мало, пользователей немного, файлы текстовые — чего заморачиваться?</p><p>Подергал JSON туда-сюда пару дней и понял: не варик.</p><ol><li>Конкурентный доступ. Два юзера одновременно коммитят — один перезаписывает файл другого.</li><li>Целостность данных. Если бот упал в середине записи — JSON остаётся в невалидном состоянии.</li><li>Версионность. Хранить историю изменений в JSON — это просто перенести проблему из кода в структуру файла.</li></ol><p>Вывод: JSON — для конфигов, а не для данных, которые меняются каждую секунду.</p><h2>Выбор SQLite и схема БД</h2><p>Выбрал SQLite, потому что:</p><ul><li>Не надо поднимать отдельный сервер</li><li>Целостность данных на уровне движка (транзакции, foreign keys, rollback)</li><li>Всё в одном файле — скопировал и унёс</li></ul><p>Сущности:</p><ul><li>users — telegram_id, username, current_project_id</li><li>projects — owner_id, name, thread_id (каждый проект живёт в своём треде канала)</li><li>files — filename, current_version, флаг modified (файл изменился и готов к коммиту)</li><li>file_versions — мясо. Каждая версия файла с полным содержимым. Привязана к file_id и опционально к commit_id</li><li>commits — сообщение, время, ссылка на проект</li><li>commit_files — связка коммитов с версиями файлов (many-to-many, чтобы поддержать ветки)</li></ul><p>Почему так: я хотел иметь возможность откатиться к любой версии любого файла. Да, база распухнет, но текстовые файлы — это не гигабайты видео. Плюс наличие file_versions и commit_files позволяет делать diff между версиями и смотреть историю изменений.</p><p>В коде вместо голых кортежей из SQL — датаклассы:</p><h2>Маркдауновый ад</h2><p>Казалось бы: взял код, обернул в тройные апострофы, кинул в Telegram. Telegram сам подсветит синтаксис, если указать язык. Красота. В теории.</p><p>На практике Telegram использует свой диалект Markdown, где куча служебных символов: _ * [ ] ( ) ~ &gt; # + - = | { } . !</p><p>Попытка 1: заэкранировать всё подряд. Результат: код превращается в кашу. Вместо<b> </b><i>def  __init__- </i> получается <i>def |_|_init|_|_ </i> — уже не запустишь, и в канале выглядит как говно.</p><p>Попытка 2: не экранировать вообще. Telegram шлёт на**й с ошибкой «can't parse entities».</p><p>Попытка 3: экранировать только то, что реально ломает разметку. Выяснилось, что порядок важен. Сначала экранируем точки и подчеркивания, потом обратную косую черту. Но и это не панацея — последовательности типа \*</p><p>после экранирования превращаются в \\*, и Telegram снова недоволен.</p><p>Попытка 4: разбивать на части. Для больших файлов делаю превью (первые 50 строк), экранирую их, отправляю как код, а полную версию — файлом. И тут начался ад: Telegram находил ошибки в тех частях кода, которых в превью вообще не было. Оказывается, он всё равно парсил полный код, даже если отправлялась только его часть.</p><p>Попытка 5 (финал): забил на Markdown и перешёл на HTML. Telegram умеет его принимать. Да, он не такой красивый, но зато предсказуемый:</p><p>Никаких точек, подчеркиваний, обратных слешей. Просто экранируем три символа — и код летит как надо.</p><h2>Security by design</h2><p>Когда бот начал обрастать функциями, я задумался о безопасности. Чтобы никто не мог коммитить или удалять чужие файлы, начал писать проверки в каждую команду:</p><p>Добавил в /commit. Потом решил с другого аккаунта потестировать команды на чужих файлах. И тут бот на каждую команду стал выдавать «файл не найден» или «проект не найден». И я понял: безопасность уже работает. С самого начала. Из коробки. Без единой строчки кода.</p><p>Как так вышло? В таблице projects с самого начала было поле owner_id. При создании проекта я писал туда telegram_id  владельца. Все запросы к БД фильтруются по этому полю:</p><p>Показать проекты — только свои. Найти файл — только в своих проектах. Выбрать проект — только из своих. Никаких лишних проверок. Просто SQL-запросы, которые с самого начала учитывали владельца.</p><h2>Команды: от семи до двух десятков</h2><p>Изначально казалось, что команд будет немного. Но...</p><p>Жизнь рассудила иначе.</p><ul><li>База: /start, /init, /use, /list, /ls, /commit, /log, /status</li><li>Удаление: /rm, /rmproject + подтверждение</li><li>Игнор: /ignore, /ignored, /unignore</li><li>Ветки: /branch, /branches, /checkout</li><li>Диффы и просмотр: /diff, /cat</li></ul><p>Итого — уже под два десятка. И это не предел.</p><h2>Простота &gt; абстракции</h2><p>В моём коде нет абстрактных базовых классов. Совсем. Потому что они нужны только когда у тебя есть минимум две разные реализации одного и того же. В GitGram всё проще: один способ работать с БД, один способ шифровать, один способ парсить .gitignore.</p><p>Если завтра появится вторая реализация — тогда и буду делать интерфейс. А пока это просто оверхед.</p><p>В GitGram:</p><ul><li>Хочешь понять, как работает add_file — идёшь в database.py и читаешь 10 строк кода.</li><li>Хочешь увидеть обработчик /commit — открываешь bot.py и смотришь.</li></ul><p>Никаких AbstractMinerShieldEventProcessor, BaseGitGramManager, InterfaceProviderFactory.</p><p>Код должен быть тупым. Чем тупее — тем проще его читать и отлаживать.</p><h2>Что дальше</h2><p>В планах:</p><ul><li>Докрутить коллаборацию (несколько человек над одним проектом)</li><li>Приватные репозитории</li><li>Кодспейс прямо в боте (да да знаю я сошёл с ума и бла бла бла..)</li></ul><h2>Итог</h2><p>GitGram принимает файлы, режет их на куски если надо, постит в канал с подсветкой. Коммиты ходят, ветки переключаются, диффы показываются. Всё это в тредах, каждый проект отдельно.</p><p>На практике GitGram ни к чему. Но сама задумка — Git в Telegram — это же так прикольно. Просто посмотреть, можно ли такое вообще запилить.</p><h2>Если хочешь поучаствовать</h2><ul><li><a href="https://t.me/Git_Gram/314" rel="nofollow">GitGram</a></li><li>Чат для хардкорных: <a href="http://t.me/sandbox_hardcore" rel="nofollow">@sandbox_hardcore</a> — вся сырая разработка, факапы и обсуждения (без цензуры)</li></ul><p>Подписывайся на мой <a href="https://t.me/+q0NBWy428y1hODEy" rel="nofollow">TГ-канал</a> — там я публикую все свои эксперименты, код и приглашаю к обсуждению. Только факты, мат и никакой политоты.</p>]]></content:encoded>
    </item>
    <item>
      <title>Кнопка «наверх» в Django: почему в проде это уже не три строки JavaScript</title>
      <link>https://tproger.ru/articles/knopka-naverh-v-django-pochemu-v-prode-eto-uzhe-ne-tri-stroki-j</link>
      <comments>https://tproger.ru/articles/knopka-naverh-v-django-pochemu-v-prode-eto-uzhe-ne-tri-stroki-j?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Фёдор Малков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/knopka-naverh-v-django-pochemu-v-prode-eto-uzhe-ne-tri-stroki-j</guid>
      <description><![CDATA[<p>Как добавить кнопку «наверх» в Django-сайт и Django Admin: настройка, CSP, доступность, мобильная версия и работа рядом с cookie-баннерами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/knopka-naverh-v-django-pochemu-v-prode-eto-uzhe-ne-tri-stroki-j">Кнопка «наверх» в Django: почему в проде это уже не три строки JavaScript</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[jQuery]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[CDN]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 01 Jul 2026 13:01:06 GMT</pubDate>
      <content:encoded><![CDATA[<p>Как сделать scroll-to-top для сайта и Django Admin, не забыв про мобильные устройства, CSP, доступность, плавающие виджеты и нормальную настройку без правки шаблонов.</p><p>На первый взгляд кнопка «наверх» — задача на пять минут. Добавил<i> position: fixed</i>, обработчик window.scrollTo()  — готово.</p><p>Но стоит этой кнопке появиться в живом проекте, как выясняется, что она пересекается с cookie-баннером, мешает чату поддержки, выглядит иначе в мобильной версии, не дружит со строгим CSP или пропадает из Django Admin.</p><p>В итоге маленькая UI-деталь начинает обрастать условиями. Я решил собрать их в отдельный Django-пакет — django-scroll-to-top</p><figure><img src="https://media.tproger.ru/user-uploads/139342/2026-06-29/000bf08b-be8e-4252-82ce-dbef3556426e.webp" alt="Пример кнопки &quot;Наверх&quot; на демо сайте" /><figcaption>Пример кнопки "Наверх" на демо сайте</figcaption></figure><h2>Когда трёх строк JavaScript достаточно</h2><p>Для небольшого сайта, где нет сложной верстки, админки, CSP и требований к повторному использованию, самый простой вариант действительно выглядит примерно так:</p><p>Это нормальное решение. Не всегда стоит тянуть пакет ради одной кнопки.</p><p>Но в реальном Django-проекте быстро появляются дополнительные вопросы:</p><ul><li>когда именно показывать кнопку: после 300 пикселей, одного экрана или только при прокрутке вверх;</li><li>что делать на коротких страницах;</li><li>как не перекрыть cookie-баннер, чат, toast-уведомления или нижнюю мобильную навигацию;</li><li>как дать пользователю закрыть кнопку;</li><li>как не сломать клавиатурную навигацию и режим reduced motion;</li><li>как сделать отдельное оформление для сайта и Django Admin;</li><li>как не заставлять проект добавлять unsafe-inline в Content Security Policy;</li><li>как позволить редактору или администратору изменить цвет, положение и иконку без нового деплоя.</li></ul><p>Именно в этот момент «три строки JavaScript» превращаются в отдельный компонент.</p><h2>Что я хотел получить</h2><p>Цель была не в том, чтобы сделать ещё одну стрелку в правом нижнем углу. Хотелось собрать переиспользуемый компонент со следующими свойствами:</p><ol><li>Подключение сайта одной template-тегом.</li><li>Отдельная поддержка обычных страниц и стандартного Django Admin.</li><li>Настройка внешнего вида через админку, а не через постоянную правку CSS.</li><li>Без jQuery, CDN, фронтенд-фреймворка и обязательной сборки.</li><li>Безопасная работа при строгой CSP.</li><li>Прогрессивное улучшение: без JavaScript остаётся обычная ссылка в начало страницы.</li><li>Возможность жить рядом с другими фиксированными элементами интерфейса.</li></ol><p>Пакет в итоге хранит обычные настройки установки в settings.py, а визуальное поведение — в базе данных. Это позволяет менять кнопку через Django Admin, публиковать новую версию настроек и при необходимости откатываться на предыдущую. В проекте есть отдельные профили для публичного сайта и Django Admin, а ревизии могут быть черновыми, опубликованными или архивными.</p><h2>Быстрое подключение</h2><p>Базовый сценарий начинается с установки:</p><p>В settings.py добавляем приложение. Если нужна поддержка стандартной админки, пакет должен идти раньше django.contrib.admin:</p><p>Включаем области, где должна работать кнопка:</p><p>Для публичной части добавляем URLConf пакета:</p><p>А в общий шаблон сайта — один тег:</p><p>На стандартном Django Admin ничего дополнительно вставлять не нужно: пакет использует обычный механизм разрешения шаблонов Django. Если же в проекте переопределён admin/base_site.html  тег можно добавить вручную в блок footer</p><h2>Настройка без превращения админки в редактор CSS</h2><p>Мне не хотелось хранить в базе шаблоны, произвольный CSS или JavaScript. Это неудобно для сопровождения и создаёт лишнюю поверхность для ошибок.</p><p>Поэтому визуальная часть собрана из контролируемых вариантов:</p><ul><li>круг, квадрат, скруглённый квадрат или pill;</li><li>заливка solid, outline, soft, ghost, glass или gradient;</li><li>положение в любом углу экрана;</li><li>отдельные размеры для desktop и mobile;</li><li>светлая и тёмная тема;</li><li>встроенные иконки, иконки от разработчика или загружаемые SVG;</li><li>настройки тени, границы, opacity и focus ring.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/139342/2026-06-29/90107109-2271-460a-9dea-815592d468e7.webp" alt="Настройки кнопки" /><figcaption>Настройки кнопки</figcaption></figure><h2>Что происходит, когда рядом есть cookie-баннер или чат</h2><p>Нижний правый угол страницы редко бывает свободен. Там часто живут:</p><ul><li>cookie-баннер;</li><li>компактная кнопка после закрытия баннера;</li><li>чат поддержки;</li><li>кнопка обратного звонка;</li><li>мобильная навигация;</li><li>toast-уведомления.</li></ul><p>Пакет умеет рассматривать такие элементы как препятствия. Для этого можно пометить элемент атрибутом:</p><p>Дальше для кнопки можно выбрать поведение: игнорировать препятствия, сдвинуться вдоль края, попробовать другой угол или скрыться, если безопасного места не осталось.</p><p>Для сложных виджетов есть отдельный адаптер: он может отслеживать появление и исчезновение элементов, например компактного launcher после закрытия cookie-баннера. При этом ни cookie-пакет, ни чат не становятся зависимостями  django-scroll-to-top</p><h2>Доступность — не отдельная галочка в конце</h2><p>У кнопки есть понятное имя для screen reader, поддержка клавиатуры, видимый focus-visible, минимальный размер области нажатия и режим prefers-reduced-motion.</p><p>Если пользователь отключил анимации на уровне системы, плавная прокрутка не будет навязываться. Если JavaScript не загрузился, кнопка остаётся обычной ссылкой на начало документа.</p><p>Полный независимый аудит WCAG 2.2 AA и тестирование масштабирования 200% и 400% пока находятся в roadmap, поэтому называть компонент полностью сертифицированным по WCAG было бы неправильно. Но структурные требования — клавиатурная доступность, фокус, reduced motion, forced-colors и безопасная работа без JavaScript — уже заложены в компонент и покрываются тестами.</p><h2>CSP и загружаемые SVG</h2><p>В корпоративных проектах часто нельзя просто добавить inline-скрипт и включить unsafe-inline ради одной кнопки.</p><p>По умолчанию компонент использует same-origin CSS и JavaScript. Для него подходит политика такого вида:</p><p>Настраиваемые цвета и размеры отдаются не через inline-стили, а через версионированный stylesheet endpoint. Это позволяет сохранить простой контракт с одним template-тегом и не ослаблять CSP.</p><p>Отдельно пришлось подумать о загружаемых SVG. Админ не рендерит исходный файл как есть: SVG проходит санитарную обработку. Скрипты, обработчики событий, внешние ресурсы, встроенные документы и небезопасные namespace отклоняются. Для загружаемых иконок также хранится информация об авторе, источнике и лицензии.</p><h2>Ревизии, публикация и откат</h2><p>Одна из самых полезных вещей в пакете — не сама кнопка, а жизненный цикл её настроек.</p><p>Можно создать черновик, посмотреть результат в live preview, опубликовать изменения или вернуться к предыдущей версии. Это особенно удобно, когда кнопку настраивает не разработчик, а контент-менеджер или дизайнер.</p><figure><img src="https://media.tproger.ru/user-uploads/139342/2026-06-29/c9840966-4e32-4506-807a-ac78830fecfa.webp" alt="Живой предпросмотр" /><figcaption>Живой предпросмотр</figcaption></figure><p>У ревизий есть три состояния:</p><ul><li>draft — редактируемый черновик;</li><li>published — текущая активная конфигурация;</li><li>archived — сохранённая версия для отката.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/139342/2026-06-29/2fcf0cfc-b29e-4ce1-b1de-9c07aac1fce0.webp" alt="настройки ревизий профиля кнопки" /><figcaption>настройки ревизий профиля кнопки</figcaption></figure><h2>Где пакет уместен, а где нет</h2><p>django-scroll-to-top имеет смысл, когда кнопка нужна в нескольких проектах, должна работать в Django Admin, настраиваться без деплоя или жить в окружении со строгими требованиями к CSP и интерфейсу.</p><p>Для лендинга на одну страницу проще и правильнее написать несколько строк самостоятельно. Это будет быстрее, понятнее и дешевле в сопровождении.</p><p>Но если такая маленькая деталь начинает повторяться в нескольких продуктах, появляется необходимость поддерживать мобильную версию, доступность, независимые настройки для сайтов и админки, то отдельный компонент уже перестаёт быть избыточным.</p><p>Сейчас пакет выпущен как beta-версия 0.2.0, требует Python 3.10+ и поддерживает Django 4.2 LTS, 5.x и 6.0. Лицензия — MIT.</p><p>Исходный код, документация и примеры использования доступны в GitHub-репозитории проекта.</p><p>Пакет опубликован в PyPI под именем django-scroll-to-top.</p><p>Обратная связь, баг-репорты и предложения по интеграции с кастомными Django Admin-темами приветствуются в Issues.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как усыпить Python: полный гид по time.sleep(), asyncio.sleep() и паузам в потоках</title>
      <link>https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep</link>
      <comments>https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep</guid>
      <description><![CDATA[<p>Разбираем, как добавлять задержки в Python: от time.sleep() до asyncio.sleep(), Event.wait() и GUI-методов вроде Tkinter.after(). С примерами кода и ловушками.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep">Как усыпить Python: полный гид по time.sleep(), asyncio.sleep() и паузам в потоках</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Jun 2026 16:32:37 GMT</pubDate>
      <content:encoded><![CDATA[<p>Иногда Python должен подождать. Задержка между запросами к API, пауза перед повторной проверкой сервера, ритмичный вывод в терминал — за всё это отвечает функция time.sleep(). Она кажется элементарной, но в разных контекстах — синхронном коде, потоках, асинхронности и GUI — пауза устроена по-разному. Разбираем, как не «усыпить» приложение.</p><h2>Что делает time.sleep()</h2><p>Функция time.sleep(seconds) приостанавливает выполнение текущего потока на указанное время. Аргумент — число секунд, включая дробные значения. Например, 0.001 — это одна миллисекунда, а 1.5 — полторы секунды.</p><p>Важный нюанс: time.sleep() гарантирует лишь минимальную задержку. Реальная пауза почти всегда чуть дольше из-за планировщика операционной системы и текущей нагрузки. Для большинства задач это не критично, но для высокоточного тайминга лучше искать другие инструменты.</p><h2>Типичные сценарии использования</h2><p>Паузы чаще всего нужны в трёх случаях: чтобы не перегружать внешний сервис, чтобы дать интерфейсу время прогрузиться и чтобы сделать вывод в консоли более читаемым.</p><ul><li>Распределение запросов к API с ограничением rate limit.</li><li>Повторные попытки выполнения ненадёжных операций.</li><li>Пауза между итерациями мониторинга или парсинга.</li></ul><h3>Мониторинг сайта с интервалом в 60 секунд</h3><p>Простой аптайм-бот опрашивает страницу и засыпает на минуту. Без задержки скрипт уйдёт в бесконечный цикл и может получить бан по IP.</p><p><b>Совет:</b><br />Для реального мониторинга добавьте логирование или уведомления, иначе ошибки останутся только в консоли.</p><h2>Декоратор для повторных попыток</h2><p>Ненадёжные операции — загрузка файла, запрос к внешнему API — иногда падают из-за временной проблемы. Вместо того чтобы оборачивать каждый вызов в цикл, удобно сделать декоратор, который повторяет функцию с задержкой.</p><p>Обратите внимание на raise внутри блока except: если его убрать, исключение не пойдёт наружу, и декоратор не поймёт, что нужна повторная попытка.</p><h2>Паузы в потоках: Event.wait()</h2><p>В потоках можно использовать time.sleep(), но он непрерываен: поток не сможет быстро завершиться, пока спит. Для корректной остановки лучше применять threading.Event().wait(). Этот метод прерывается, как только событие установлено через event.set().</p><p>Такой подход позволяет аккуратно завершить все потоки по Ctrl+C: event.set() мгновенно будит все ожидающие wait().</p><h2>Неблокирующие паузы в asyncio</h2><p>Асинхронный код в Python появился в версии 3.4 модуля asyncio и получил синтаксис async/await в 3.5. Внутри корутин нельзя использовать обычный time.sleep(): он заморозит весь event loop. Для паузы в асинхронном коде есть asyncio.sleep().</p><p>Ключевое отличие: asyncio.sleep() приостанавливает только текущую корутину, уступая управление event loop. Остальные задачи продолжают выполняться. Это особенно важно при соблюдении rate limit при параллельных запросах.</p><h2>Как не заморозить GUI</h2><p>В GUI-фреймворках весь интерфейс работает в главном потоке с собственным циклом событий. Если вызвать time.sleep() в обработчике кнопки, окно повиснет: не будет отрисовки, не сработают клики, а в Windows может появиться предупреждение о неответствии.</p><p>Метод .after() в Tkinter планирует callback через заданное число миллисекунд и сразу возвращает управление циклу событий. Аналог в wxPython — wx.CallLater(). Паттерн универсален: не блокируйте поток, планируйте отложенный вызов.</p><ul><li>time.sleep() — универсальный способ поставить синхронную паузу, но он блокирует поток целиком.</li><li>Для повторных попыток удобен декоратор с задержкой, который ловит исключения и делает retry.</li><li>В потоках предпочитайте Event.wait(): его можно прервать через event.set().</li><li>В асинхронном коде используйте asyncio.sleep(), иначе заморозите весь event loop.</li><li>В GUI применяйте фреймворковые методы вроде Tkinter.after(), чтобы интерфейс оставался отзывчивым.</li></ul><h2>Выводы</h2><p>Пауза в коде — это не просто «подождать секунду». В синхронном скрипте достаточно time.sleep(), в потоке важно уметь выходить по сигналу, в асинхронном коде нельзя блокировать event loop, а в GUI нужно работать в рамках фреймворкового цикла событий. Правильный инструмент зависит от контекста, и выбор влияет на отзывчивость и стабильность приложения.</p><blockquote>Не блокируйте поток без причины. Если можно уступить управление — уступите.</blockquote><p>Источник и дополнительные материалы: <a href="https://realpython.com/python-sleep/">Real Python — Python sleep(): How to Add Time Delays to Your Code</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Airflow, n8n и Make: что выбрать для API-оркестрации</title>
      <link>https://tproger.ru/articles/airflow-n8n-i-make-chto-vybrat-dlya-api-orkestracii</link>
      <comments>https://tproger.ru/articles/airflow-n8n-i-make-chto-vybrat-dlya-api-orkestracii?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/airflow-n8n-i-make-chto-vybrat-dlya-api-orkestracii</guid>
      <description><![CDATA[<p>Сравниваем Apache Airflow, n8n и Make для оркестрации API: плюсы, минусы, стоимость и российская специфика. Выберите инструмент под свою задачу.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/airflow-n8n-i-make-chto-vybrat-dlya-api-orkestracii">Airflow, n8n и Make: что выбрать для API-оркестрации</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Jun 2026 13:25:47 GMT</pubDate>
      <content:encoded><![CDATA[<p>Современное приложение редко живёт в вакууме: платёжка общается с банком, CRM — с телефонией, а аналитика собирает данные из десятка источников. Когда одного curl мало, на сцену выходит API-оркестрация — последовательность вызовов, обработка ошибок, преобразование данных и контроль за выполнением. Выбор инструмента для этой работы определяет, сколько времени вы потратите на поддержку и сколько он будет стоить при росте нагрузки.</p><ul><li><b>Apache Airflow</b> — мощный оркестратор для сложных Python-конвейеров, но требует инфраструктуры и DevOps-культуры.</li><li><b>n8n</b> — визуальный конструктор с открытым исходным кодом: удобен для средних нагрузок, можно бесплатно хостить на своём сервере.</li><li><b>Make</b> (бывший Integromat) — облачный визуальный автоматизатор для бизнес-команд, дешевле Zapier, но тарифицирует каждый шаг сценария.</li><li>Для российских команд критичны: возможность self-hosting, стоимость инфраструктуры и риски доступности зарубежного SaaS.</li><li>Нет универсального победителя — выбор зависит от размера команды, сложности логики и требований к контролю над данными.</li></ul><p><b>API-оркестрация</b> — это не просто «позвонить в API», а построить надёжный конвейер: вызвать один сервис, передать результат в другой, обработать ошибку, повторить при сбое и записать лог. Иногда это пара запросов в час, а иногда — десятки тысяч операций в день. Отсюда и разница в подходах: где-то важна гибкость кода, где-то — скорость запуска, а где-то — цена за операцию.</p><h2>Apache Airflow: тяжёлый артиллерист</h2><p>Airflow появился в Airbnb и за десять лет стал стандартом для data-пайплайнов. Его используют Netflix, Spotify и Uber — там, где нужно управлять сотнями зависимых задач с контролем SLA и аудитом. Главная идея: вы описываете workflow как Python-код в виде направленного ациклического графа (DAG), а scheduler отвечает за порядок, ретраи и мониторинг.</p><h3>Зачем выбирать Airflow</h3><ul><li>Сложная логика: ветвления, условия, ретраи, расписания через cron.</li><li>Полный контроль над кодом: любые библиотеки Python, кастомные операторы, unit-тесты.</li><li>Зрелая экосистема: SLA-мониторинг, audit trail, интеграция с Kubernetes и Spark.</li><li>Open Source: можно развернуть на своей инфраструктуре, в том числе на российских облаках.</li></ul><h3>Честный взгляд на минусы</h3><ul><li>Высокий порог входа: нужно разворачивать БД, Redis, воркеры и веб-интерфейс.</li><li>Минимальный кластер на VPS обходится примерно в €50/месяц без учёта времени администратора.</li><li>При 50+ задачах DAG превращается в большой Python-файл, который сложно поддерживать без дисциплины.</li><li>Коммуникация между задачами через XCom требует внимания: забыли очистить — получили memory leak.</li></ul><p>Пример простого DAG, который забирает пользователя, его посты и считает количество:</p><p>Airflow хорош, когда у вас уже есть data-команда, Kubernetes и понимание, зачем нужны DAG. Для стартапа из трёх человек это, скорее, оверинжиниринг.</p><h2>n8n: визуальный middle ground</h2><p>n8n занимает промежуточную нишу между кодом и no-code. Это node-based редактор: вы перетаскиваете блоки, соединяете их стрелками, а логика — в JavaScript-функциях и условиях. Главное преимущество: n8n open-source, его можно поднять на собственном сервере, и за это не придётся платить пошагово.</p><h3>Зачем выбирать n8n</h3><ul><li>Быстрый старт: визуальный конструктор позволяет собрать пайплайн за час, а не день.</li><li>Self-hosting: контейнер на VPS за $5–10/мес может обрабатывать тысячи запусков в день.</li><li>Гибридный подход: 90 % задач решается мышкой, сложную трансформацию можно написать на JS.</li><li>Прозрачная цена облака: плата за выполнение workflow, а не за каждый модуль.</li></ul><h3>Ограничения</h3><ul><li>При сотнях задач в одном workflow визуальная схема становится громоздкой.</li><li>Нативные узлы покрывают не всё: экзотический API придётся звать через HTTP Request.</li><li>Self-hosted версия требует обновлений и бэкапов — это всё ещё инфраструктура, хоть и простая.</li><li>Для России: облако n8n — европейский сервис, но self-hosted можно развернуть на Selectel, Yandex Cloud или любом другом провайдере.</li></ul><p>Пример JSON-экспорта workflow, который забирает пользователей, преобразует данные и пишет в PostgreSQL:</p><p>n8n часто выбирают команды, которым нужна скорость no-code, но не хочется терять контроль над инфраструктурой и платить за каждый шаг сценария.</p><h2>Make: бизнес-автоматизация по шагам</h2><p>Make (ранее Integromat) — это облачный визуальный автоматизатор для бизнес-команд. Его сценарии собираются из модулей: триггер → действие → фильтр → маршрутизатор. По сравнению с Zapier у Make глубже логика, а цена ниже, но он остаётся полностью SaaS-решением.</p><h3>Зачем выбирать Make</h3><ul><li>Быстрая интеграция с популярными сервисами: Google Sheets, Slack, Telegram, CRM, email-рассылки.</li><li>Визуальные маршрутизаторы (routers), итераторы и агрегаторы позволяют строить ветвления без кода.</li><li>Низкий порог входа для маркетологов, менеджеров продукта и операционистов.</li><li>План Core стоит от $9/мес за 10 000 операций — дешевле многих конкурентов.</li></ul><h3>Подводные камни</h3><ul><li>Тарификация по операциям: каждый модуль в сценарии считается отдельно. Десять шагов × тысяча запусков = 10 000 операций.</li><li>Нельзя self-host: данные обрабатываются на серверах Make, что может быть проблемой для чувствительных данных.</li><li>Для России: сервис работает как зарубежный SaaS, есть риски доступности и оплаты.</li><li>Сложные сценарии сложнее отлаживать: приходится кликать по модулям в истории выполнений.</li></ul><p>Типичный сценарий в Make выглядит так: вебхук из формы → проверка дубликата в Google Sheets → запись в CRM → отправка уведомления в Telegram. Всё это строится мышкой, но стоит добавить кастомную логику — и придётся использовать HTTP-модуль или встроенные функции.</p><h2>Сравнение в цифрах</h2><p>Сводная таблица по ключевым параметрам. Вместо абстрактных «плюсов» смотрите на конкретные ограничения:</p><ul><li><b>Airflow</b> — код Python, self-hosted, сложность высокая, стоимость от €50/мес + администрирование, лучше для data-платформ.</li><li><b>n8n</b> — визуальный + JS, self-hosted или cloud, средняя сложность, self-hosted от $5–10/мес, лучше для средних нагрузок и гибридных команд.</li><li><b>Make</b> — визуальный no-code, только cloud, низкая сложность входа, от $9/мес за 10K операций, лучше для бизнес-автоматизации.</li><li><b>Модель оплаты</b>: Airflow — инфраструктура; n8n — инфраструктура или executions в облаке; Make — операции (каждый модуль отдельно).</li><li><b>Контроль данных</b>: максимальный у Airflow и self-hosted n8n; минимальный у Make как SaaS.</li></ul><p><b>Российская специфика:</b><br />Для команд в РФ критичен self-hosted путь: Airflow и n8n можно развернуть на отечественных VPS или в Yandex Cloud/Selectel. Make, Zapier и другие зарубежные SaaS не дают гарантий доступности и оплаты. Если данные не могут покидать страну — выбор очевиден.</p><h2>Как выбрать инструмент</h2><h2>FAQ</h2><h2>Выводы</h2><p>Нет единого лучшего инструмента для API-оркестрации — есть подходящий под ваши ограничения. Airflow остаётся выбором зрелых data-команд, которым нужна масштабируемость и контроль. n8n закрывает большинство задач среднего уровня и даёт свободу self-hosting. Make — удобный SaaS для бизнес-автоматизации, но с привязкой к облаку и пошаговой тарификацией.</p><blockquote>Хороший оркестратор не тот, у кого больше иконок в интерфейсе, а тот, чьи ошибки вы сможете отладить в два часа ночи.</blockquote><p>Источник: материал основан на публикации «Airflow vs n8n vs Make for API orchestration» автора Raizan в DEV Community — <a href="https://dev.to/chasebot/airflow-vs-n8n-vs-make-for-api-orchestration-1lb8" rel="noopener">dev.to/chasebot/airflow-vs-n8n-vs-make-for-api-orchestration-1lb8</a>. Раздел про Make и сравнительный анализ дополнены редакционным контекстом.</p>]]></content:encoded>
    </item>
    <item>
      <title>Fine-tuning LLM в 2026: гид по LoRA, QLoRA и полному дообучению</title>
      <link>https://tproger.ru/articles/fine-tuning-llm-v-2026-gid-po-lora-qlora-i-polnomu-doobucheniyu</link>
      <comments>https://tproger.ru/articles/fine-tuning-llm-v-2026-gid-po-lora-qlora-i-polnomu-doobucheniyu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/fine-tuning-llm-v-2026-gid-po-lora-qlora-i-polnomu-doobucheniyu</guid>
      <description><![CDATA[<p>Разбираем, как в 2026 году дообучать LLM с помощью LoRA, QLoRA и полного fine-tuning. Сравнение методов, параметры, код и практические рекомендации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/fine-tuning-llm-v-2026-gid-po-lora-qlora-i-polnomu-doobucheniyu">Fine-tuning LLM в 2026: гид по LoRA, QLoRA и полному дообучению</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Data Science]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Jun 2026 10:19:58 GMT</pubDate>
      <content:encoded><![CDATA[<p>В 2026 году дообучить 70-миллиардную языковую модель можно на одном игровом GPU. Ещё пять лет назад это звучало как фантастика, а сегодня — рутина для тысяч команд. Но главный вопрос уже не «как», а «стоит ли» и «каким методом».</p><p>В этой статье разберёмся, когда fine-tuning решает проблему, а когда проще обойтись промптами или RAG. Сравним LoRA, QLoRA и полное дообучение, посмотрим на цифры и напишем рабочий пайплайн на Python.</p><h2>Что такое fine-tuning и зачем он нужен</h2><p><b>Fine-tuning</b> — это донастройка уже предобученной модели под конкретную задачу. Вместо того чтобы учить трансформер с нуля на сотнях гигабайт текстов, мы берём готовую LLM и показываем ей тысячи или десятки тысяч примеров, которые важны именно нам: стиль ответов, формат JSON, медицинскую терминологию или правила поведения в чат-боте.</p><p>Важный нюанс: дообучение меняет <b>поведение</b> модели, а не добавляет свежих фактов. Если вам нужно, чтобы бот знал актуальные цены, документацию API или внутреннюю базу знаний — правильный путь это RAG и хороший системный промпт, а не бесконечная перетренировка весов.</p><h2>Когда fine-tuning — правильный выбор</h2><p>Базовые модели 2026 года — GPT-5, Claude 4.5, Llama 3.3, Qwen 3, DeepSeek-V4 — научились следовать инструкциям, вызывать инструменты и выдавать структурированный вывод. Поэтому последовательность действий у большинства команд такая: сначала промптинг, потом RAG, и только потом fine-tuning.</p><p>Есть четыре ситуации, где дообучение действительно оправдано:</p><ul><li><b>Надёжность структурированного вывода.</b> Если модель постоянно ломает JSON-схему или забывает поля, fine-tuning закрепляет правильный формат.</li><li><b>Доменная лексика.</b> Медицинские, юридические или научные термины, которые базовая модель «не осмеливается» использовать, можно вшить через SFT.</li><li><b>Тон и политика отказов.</b> Когда системные инструкции конфликтуют с базовой alignment-моделью, целевое дообучение перестраивает поведение точнее, чем промпты.</li><li><b>Дистилляция и сжатие.</b> Можно перенести способности большой модели на маленькую, чтобы удешевить инференс в продакшене.</li></ul><p><b>Главное правило:</b> не пытайтесь впрыснуть в модель факты через дообучение. Исследования показывают, что RAG превосходит fine-tuning по фактической точности, а знания, зашитые в веса, быстро устаревают и приводят к галлюцинациям.</p><ul><li>LoRA и QLoRA в 2026 году покрывают большинство задач; полное дообучение нужно редко.</li><li>QLoRA позволяет дообучать 70B-модель на одном GPU с 48 ГБ VRAM, уменьшая потребление памяти примерно в 4 раза.</li><li>Fine-tuning меняет поведение, а не факты: знания добавляйте через RAG, стиль и формат — через SFT/DPO.</li><li>Оптимальная стартовая точка: LoRA rank 16, все линейные слои, learning rate 2e-4, 1–3 эпохи.</li><li>Unsloth ускоряет QLoRA в 2–5 раз и снижает потребление VRAM примерно на 70% по сравнению с ванильным TRL.</li></ul><h2>LoRA: низкоранговая адаптация</h2><p><b>Low-Rank Adaptation (LoRA)</b>, предложенная в 2021 году, изменила правила игры. Вместо обновления всех весов модели LoRA добавляет к каждому линейному слою две маленькие обучаемые матрицы A и B. Обучается лишь 0,1–1% параметров, а остальная часть модели заморожена.</p><p>Формула обновления выглядит так:</p><p><b>Ŵ = W + (α / rank) × A × B</b></p><p>Здесь W — замороженные предобученные веса, A и B — адаптерные матрицы, rank задаёт размер скрытого представления, а alpha масштабирует вклад адаптации. Например, LoRA rank 16 на Llama 3.3 70B обучает около 420 миллионов параметров — менее 1% от общего числа — при качестве, близком к полному дообучению.</p><h2>QLoRA: дообучение на потребительском железе</h2><p><b>QLoRA</b> добавляет к LoRA 4-битную квантизацию замороженных весов в формате <b>NF4 (Normal Float 4)</b>. Сами адаптеры остаются в FP16/BF16, поэтому точные градиенты не теряются. Эта комбинация, предложенная Dettmers и соавторами в 2023 году, позволяет разместить 70-миллиардную модель на одной RTX 4090 или A6000.</p><p>Качество при этом остаётся очень близким к обычному LoRA: разница на большинстве бенчмарков укладывается в доли процента. Для разработчиков в России и СНГ это особенно ценно: не нужен кластер A100, достаточно одной мощной локальной карты или аренды GPU у облачного провайдера.</p><h2>Сравнение методов: цифры</h2><p>По данным команд Unsloth, Hugging Face и мета-анализу FinLoRA, разрыв между параметро-эффективными методами и полным дообучением в 2026 году стал минимальным. Вот типичные цифры для Llama 3.3 70B:</p><ul><li><b>MMLU:</b> QLoRA отстаёт от LoRA менее чем на 0,3%, а оба метода в пределах 1% от полного fine-tuning.</li><li><b>HumanEval (код):</b> полное дообучение лидирует на 2–3%, но LoRA догоняет, если таргетировать все семь линейных слоёв: q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj.</li><li><b>GSM8K (математика):</b> при rank ≥ 32 различий между методами практически нет.</li><li><b>MT-Bench / AlpacaEval:</b> QLoRA и LoRA в пределах 0,5% от полного дообучения.</li></ul><p>Практический вывод: для подавляющего большинства задач полное дообучение не стоит 50–100-кратного роста вычислительных затрат. QLoRA на одном GPU даёт продакшен-качество.</p><h2>SFT, DPO и RFT: три подхода к обучению</h2><p>Выбрать метод адаптации — только половина дела. Нужно ещё понять, как учить модель. В 2026 году доминируют три парадигмы.</p><h3>Supervised Fine-Tuning (SFT)</h3><p>Классический вариант: модель учится на парах «запрос → идеальный ответ». SFT лучше всего подходит для обучения формату, переноса стиля и работы со структурированными данными. Главное требование — качественный, размеченный датасет.</p><h3>Direct Preference Optimization (DPO)</h3><p>DPO стала рабочей лошадкой 2026 года. Вместо отдельной reward-модели, как в RLHF, она оптимизирует предпочтения напрямую: у нас есть пары «хороший ответ / плохой ответ», и модель учится выбирать лучший. Это дешевле, стабильнее и проще в настройке. DPO выбирайте для тона, alignment и управления отказами.</p><h3>Reinforcement Fine-Tuning (RFT)</h3><p><b>Reinforcement Fine-Tuning</b> от OpenAI доступен для o-серии и других reasoning-моделей. Модель обучается не по эталонным ответам, а по пользовательской функции-оценщику. Это мощно для задач с верифицируемой наградой: генерация кода, математика, структурированная экстракция. Главный барьер — нужно сначала написать хороший grader.</p><h2>Пошаговый гайд: дообучаем Llama 3.3 с QLoRA</h2><p>Ниже — минимальный рабочий пример на Python с использованием Unsloth и Hugging Face TRL. Мы дообучим адаптеры для Llama 3.3 8B Instruct на собственном датасете инструкций.</p><h3>Шаг 1. Установка зависимостей</h3><h3>Шаг 2. Загружаем базовую модель в 4 бита</h3><h3>Шаг 3. Настраиваем LoRA-адаптеры</h3><h3>Шаг 4. Готовим датасет</h3><h3>Шаг 5. Запускаем обучение</h3><h3>Шаг 6. Сохраняем и при необходимости сливаем адаптер</h3><h2>Гиперпараметры: что влияет на результат</h2><p>Качество дообучения сильно зависит от нескольких ручек. Вот практические рекомендации на 2026 год.</p><h3>LoRA rank</h3><p>Начинайте с rank 16 для большинства задач. Rank 8 хватает для простого обучения формату, rank 32–64 дают небольшой прирост на сложной доменной адаптации, но повышают риск переобучения. Очень высокие rank редко окупаются.</p><h3>Learning rate</h3><p>Для стандартного SFT с LoRA/QLoRA стартовая точка — 2e-4. Для DPO, GRPO и других reinforcement-методов снижайте до 5e-6. Полное дообучение требует ещё меньших скоростей: 1e-5–5e-6.</p><h3>Target modules</h3><p>Всегда таргетируйте все семь линейных слоёв: четыре слоя внимания и три слоя MLP. Исключение модулей даёт минимальную экономию памяти и заметно портит качество.</p><h3>Эпохи и batch size</h3><p>Оптимум — 1–3 эпохи. Больше трёх эпох на инструкционных датасетах обычно ведёт к переобучению. Целевой эффективный batch size — 16–32 (batch size × gradient accumulation).</p><h2>Стек инструментов в 2026 году</h2><p>Экосистема дообучения выросла и во многом стандартизировалась.</p><ul><li><b>Hugging Face PEFT + TRL.</b> Де-факто стандарт: SFTTrainer, DPOTrainer, ORPOTrainer. Работает с любой моделью из Hub.</li><li><b>Unsloth.</b> 2–5× быстрее и на ~70% меньше VRAM для QLoRA. Must-have для одно-GPU сетапов.</li><li><b>Axolotl.</b> YAML-конфиги для мульти-GPU пайплайнов на кластерах A100/H100.</li><li><b>Torchtune.</b> Нативная библиотека от PyTorch с компонентным подходом. Легче TRL, но требует больше ручной работы.</li><li><b>LM Studio / Ollama.</b> Для быстрой локальной проверки дообученной модели.</li></ul><h2>Типичные ошибки и как их избежать</h2><ol><li><b>Дообучение без evals.</b> Если вы не можете измерить, стала ли новая контрольная точка лучше предыдущей, у вас не проблема дообучения — у вас проблема оценки. Пишите метрики до тренировки.</li><li><b>Катастрофическое забывание.</b> Узкий датасет может «выбить» общие способности модели. Добавляйте 10–20% общих инструкционных данных, используйте низкий learning rate и rank ≤ 32.</li><li><b>Переобучение на шум.</b> Если loss стремится к нулю, а eval падает — модель запоминает артефакты. Проверьте датасет на дубликаты и противоречия, добавьте LoRA dropout 0.1.</li><li><b>Устаревание знаний.</b> Факты, зашитые в веса, стареют вместе с датасетом. Для изменяющихся данных используйте RAG, а fine-tuning оставьте для стабильного поведения.</li></ol><h2>Выводы</h2><p>Fine-tuning LLM в 2026 году стал доступным инструментом: QLoRA + Unsloth превращают дообучение 70-миллиардной модели из инфраструктурного проекта в задачу на один вечер. Но техническая возможность не отменяет стратегии: большинство команд всё ещё получают больше пользы от хорошего промпта и RAG.</p><p>Золотое правило остаётся неизменным: дообучение меняет поведение, а не факты. Начинайте с QLoRA, rank 16, всех линейных слоёв, learning rate 2e-4 и 1–3 эпох. Пишите evals до тренировки, смешивайте доменные данные с общими инструкциями и не пытайтесь заменить RAG бесконечным дообучением.</p><blockquote>Fine-tuning — это не способ научить модель фактам, а способ научить её манере. Тот, кто понимает разницу, экономит десятки тысяч долларов на вычислениях.</blockquote><p>Источник: <a href="https://dev.to/techmag/llm-fine-tuning-2026-complete-lora-qlora-full-fine-tuning-guide-3le8">LLM Fine-Tuning 2026: Complete LoRA, QLoRA &amp; Full Fine-Tuning Guide</a> — TechMag на Dev.to.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как приручить legacy-код: безопасная модернизация без заморозки фич</title>
      <link>https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki</link>
      <comments>https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[KODE]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki</guid>
      <description><![CDATA[<p>Как модернизировать legacy-код без остановки продукта: Strangler Fig Pattern, feature flags, shadow testing и безопасная миграция данных. Практика и антипаттерны.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki">Как приручить legacy-код: безопасная модернизация без заморозки фич</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Ruby]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[Финтех]]></category>
      <category><![CDATA[ICO]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Twitter]]></category>
      <category><![CDATA[Игра]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[OpenTelemetry]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[DeFi]]></category>
      <category><![CDATA[Grafana]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Legacy]]></category>
      <category><![CDATA[Техника]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 23 Jun 2026 06:16:57 GMT</pubDate>
      <content:encoded><![CDATA[<p>Legacy-код — одна из самых болезненных тем в инженерных командах. Обычно все понимают, что система устарела: архитектура мешает быстро выпускать изменения, новые фичи приходится встраивать через обходные пути, тесты либо неполные, либо отсутствуют, а любое изменение в одном модуле неожиданно ломает другой.</p><p>Но при этом к такому коду часто боятся прикасаться. И не без причины. В старых системах редко бывает понятная карта зависимостей. Документация устарела, часть знаний живет только в головах нескольких разработчиков, а бизнес при этом продолжает ждать новых релизов, интеграций и продуктовых экспериментов.</p><p>Так появляется классическая ловушка legacy: систему надо модернизировать, но остановить развитие нельзя. Переписать всё с нуля страшно, поддерживать как есть — всё дороже. В результате продукт обрастает временными решениями, скорость разработки падает, а стоимость каждого следующего изменения растет.</p><p>Хорошая новость в том, что модернизация legacy-кода не обязана быть большим взрывом. Старую систему можно менять постепенно, сохраняя рабочий продукт, не замораживая фичи и не устраивая один критический релиз, от которого зависит всё.</p><h2>Почему Big Bang-переписывание чаще всего заканчивается плохо</h2><p>Когда команда долго живет с устаревшей системой, идея переписать всё с нуля выглядит очень соблазнительно. Кажется, что можно наконец избавиться от технического долга, выбрать нормальную архитектуру, перепроектировать модули, покрыть всё тестами и начать «правильно».</p><p>На старте такой план часто звучит логично. Особенно если текущая система действительно мешает развитию. Например, мобильное приложение растет, у него уже миллионы пользователей, бэкенд написан несколько лет назад как монолит, а каждая новая фича требует изменений в десятке мест. Команда устала чинить регрессии, бизнес устал ждать, и всем хочется «один раз нормально переписать».</p><p>Проблема не в самой идее переписывания, а в условиях, при которых оно проваливается. Большой риск возникает, когда совпадают четыре фактора: переписывание занимает много месяцев, в это время бизнес продолжает развивать старую систему, новая версия покрывает сразу большую часть функциональности, а откат связан с миграцией данных. Если все четыре пункта присутствуют, Big Bang почти гарантированно превратится в долгий и дорогой проект.</p><p>Допустим, команда решила переписать модуль заказов в e-commerce-продукте. В старой версии есть корзина, промокоды, доставка и оплата. Команда планирует за полгода сделать новый сервис заказов. Но за эти полгода бизнес добавляет подписки, подарочные сертификаты, частичную оплату бонусами и новую логику возвратов. В итоге новая система, которую проектировали под старые требования, к моменту релиза уже нуждается в доработке.</p><p>Есть и другая проблема: большой релиз почти всегда несет максимальный риск. Если вы заменяете крупный кусок системы целиком, ошибка влияет сразу на большую часть пользователей. Откат тоже становится сложным, потому что новая логика уже связана с новыми данными, контрактами и интеграциями.</p><p>Big Bang всё-таки бывает оправдан — но в узких условиях. Если кодовая база молодая (год-два), пользователей мало, у системы нет критичного состояния в БД и продукт можно временно заморозить или вести в обоих контурах параллельно, полное переписывание может оказаться дешевле постепенной миграции. Это редкая ситуация, и она быстро исчезает по мере роста продукта. В зрелых системах безопаснее работает другой подход — постепенная архитектурная эволюция.</p><h2>Пример: как команда переписала сервис документов и потеряла полгода</h2><p>Команда сопровождала сервис — старый модуль на aiohttp с Pydantic v1, через который проходила вся обработка путевых листов и актов осмотра транспорта. Сервис существовал шесть лет, был покрыт тестами фрагментарно, а его API использовали мобильное приложение водителей, диспетчерская веб-панель и пакетный импорт.</p><p>Команда решила переписать сервис целиком: перейти на FastAPI, обновить Pydantic до v2, заодно почистить контракты и заменить внутреннее хранилище документов с MongoDB на PostgreSQL. План был рассчитан на четыре месяца.</p><p>Через восемь месяцев проект всё ещё не был готов к выкатке, а к десятому месяцу команда откатила миграцию полностью. Причин было несколько.</p><p>Во-первых, переписывание шло параллельно с продуктовой разработкой. За время миграции бизнес добавил два новых типа документов и изменил правила подписи актов. Новая система проектировалась под старые требования и к моменту готовности уже не соответствовала продукту.</p><p>Во-вторых, команда не написала характеристических тестов. Поведение «как есть» нигде не было зафиксировано, и расхождения находились только в продакшене после переключения.</p><p>В-третьих, у старого сервиса были скрытые побочные эффекты, о которых никто не помнил. При смене статуса документа публиковал событие в Kafka, которое читал биллинг и сервис аналитики. В новой реализации это поведение не было воспроизведено, потому что в коде оно выглядело как «лишний» вызов. После переключения биллинг перестал получать события, и расхождение обнаружили только через две недели — по жалобе финансового отдела.</p><p>В-четвёртых, переключение было сделано «в лоб»: маршрут в API Gateway просто перенаправили на новый сервис. Фича-флага не было, теневого запуска не было, плана отката не было. Когда выяснилось, что новый сервис строже валидирует исторические форматы документов и отклоняет часть старых записей, быстро вернуться на старую реализацию не получилось — её к тому моменту уже отключили на стенде, а в БД успели уйти записи в новом формате.</p><p>В итоге миграцию свернули, потратив около десяти человеко-месяцев и потеряв доверие бизнеса. Сервис до сих пор работает в исходной реализации, а команда переходит к плану, описанному ниже.</p><h2>Strangler Fig Pattern: как заменить систему по частям</h2><p>Один из самых практичных подходов к модернизации legacy-кода — Strangler Fig Pattern. В софтверном виде паттерн был сформулирован Мартином Фаулером в 2004 году под названием StranglerFigApplication. Идея проста: не переписывать систему целиком, а постепенно выносить отдельные части в новую реализацию.</p><p>Название пришло из биологии. Фикус-душитель растет вокруг дерева-хозяина и постепенно вытесняет его. В архитектуре принцип похожий: старая система продолжает работать, новая функциональность появляется рядом, а затем отдельные потоки постепенно переводятся на новую реализацию.</p><p>Представим старый монолит интернет-магазина. Внутри него есть каталог, корзина, заказы, платежи, скидки, личный кабинет и уведомления. Переписать всё сразу — рискованно. Но можно начать с относительно изолированного участка, например с уведомлений.</p><p>Сначала команда описывает текущий контракт: какие события приходят в модуль уведомлений, какие каналы используются, какие шаблоны отправляются, какие ошибки считаются допустимыми. Затем рядом создается новый сервис уведомлений, который реализует тот же контракт. На первом этапе он может даже не отправлять реальные сообщения, а только принимать события и логировать результат. После проверки часть трафика переводится на новую реализацию. Когда сервис стабилизируется, старый код уведомлений удаляется из монолита.</p><p>Strangler Fig хорошо работает там, где между старым и новым кодом есть сетевая граница: HTTP, message bus, RPC. Если такой границы нет — например, нужно постепенно заменить функцию или класс внутри одного процесса — используется родственный паттерн Branch by Abstraction: над старой реализацией создается абстракция, рядом пишется новая реализация, переключение происходит через конфигурацию или фича-флаг, после стабилизации старая ветка удаляется. Снаружи это выглядит как Strangler Fig, но без сетевого прокси.</p><p>Такой подход снижает риск. В системе нет одного большого релиза, где всё меняется сразу. Есть серия небольших контролируемых изменений. Каждое можно протестировать, измерить и откатить.</p><h2>Главное правило: сначала повторить поведение, потом улучшать</h2><p>Одна из частых ошибок при модернизации legacy-кода — попытка одновременно переписать систему и улучшить бизнес-логику. Команда смотрит на старый модуль и думает: «Раз уж мы его трогаем, давайте сразу сделаем нормальную архитектуру, изменим контракты, уберем странные кейсы и перепишем поведение».</p><p>Это опасный путь. В legacy-системах странное поведение часто существует не случайно. За ним может стоять неочевидное бизнес-правило, старый клиент, интеграция с внешней системой или исторический баг, на который уже кто-то завязался.</p><p>Например, в системе расчета налогов может быть правило: для контрактов, заключенных до 2018 года, НДС округляется в меньшую сторону до целого рубля, а для всех остальных — по математическим правилам. Новый разработчик может решить, что это ошибка, и «исправить» округление. Но потом выяснится, что часть крупных клиентов держит это поведение в своих сверках, а смена правила приведет к расхождениям в актах и претензиям.</p><p>Прежде чем менять поведение, его нужно зафиксировать. Для этого пишут характеристические тесты (characterization tests, иногда называемые golden master или approval tests). Идея простая: на реальных данных или их обезличенных копиях прогоняется старая реализация, её ответы сохраняются как эталон, и любые будущие изменения, отклоняющиеся от эталона, отлавливаются автоматически. Тесты пишутся не для красоты, а для того, чтобы зафиксировать существующее поведение — даже странное — перед тем, как его трогать. Подробно эта техника описана у Майкла Физерса в книге Working Effectively with Legacy Code; на практике её удобно реализовать через библиотеки семейства approval-tests (approvaltests-python, approvaltests-java и аналоги).</p><p>Поэтому первый этап модернизации — не улучшение, а воспроизведение текущего поведения. Новая реализация должна вести себя так же, как старая. Даже если старое поведение кажется странным. Только после стабилизации можно отдельно обсуждать, что именно стоит менять.</p><h2>Feature toggles: как включать новую логику без риска</h2><p>Feature toggles, или фича-флаги, — один из главных инструментов безопасной миграции. Они позволяют включать и выключать новую логику без деплоя.</p><p>В обычной разработке релиз часто выглядит бинарно: код либо выкатили, либо нет. При миграции legacy это неудобно. Гораздо безопаснее иметь возможность включить новую реализацию для 1% пользователей, затем для 10%, потом для половины аудитории и только после этого для всех.</p><p>Например, команда переносит расчет стоимости доставки из монолита в новый сервис. С помощью фича-флага это выглядит так:</p><p>user_id передается явно, чтобы решение «попал ли пользователь в новый сегмент» было стабильным от запроса к запросу. Иначе один и тот же клиент будет получать разные ответы при обновлении страницы, и поведение системы станет непредсказуемым.</p><p>На первом этапе флаг включают только для внутренней команды. Потом для тестового сегмента пользователей. Затем для небольшой доли реального трафика. Если метрики стабильны, долю увеличивают. Если появляются ошибки, флаг выключают, и пользователи снова идут в старую реализацию.</p><p>Важно различать два разных типа флагов. Флаг постепенной выкатки (rollout flag) меняется редко и контролирует, какой процент пользователей видит новую логику. Kill switch — отдельный флаг, единственная задача которого — мгновенно выключить новую реализацию при инциденте. Kill switch должен опрашиваться на каждом запросе, его кэширование должно жить секунды, а не минуты, и он принципиально не должен зависеть от той системы, которую он выключает. Иначе в момент аварии может оказаться, что выключатель сам недоступен.</p><p>В качестве инфраструктуры для флагов команды обычно берут одну из платформ: LaunchDarkly, Unleash, Flagsmith, GrowthBook, либо собирают собственную поверх Redis или конфигурационного сервиса. Для миграции важны три свойства: быстрое распространение изменений (секунды, а не минуты), поддержка таргетинга по пользователю/сегменту и аудит — кто и когда менял флаг.</p><p>Важно, что фича-флаг — это не просто if в коде. Для серьезной миграции нужны правила: кто может включать флаг, как быстро его можно отключить, какие метрики отслеживаются, когда флаг должен быть удален.</p><p>Последний пункт особенно важен. Если флаги не удалять, система быстро превращается в набор ветвлений, где никто уже не понимает, какая логика актуальна.</p><h2>Shadow testing: как проверить новую систему на реальном трафике</h2><p>Feature toggles помогают безопасно переключать пользователей. Но перед этим хорошо бы понять, совпадает ли новая логика со старой. Для этого используют shadow testing.</p><p>Shadow testing — это запуск новой реализации параллельно старой, но без влияния на пользователя. Пользовательский запрос по-прежнему обрабатывает старая система, а новая получает копию запроса и считает результат «в тени». Пользователю этот результат не показывается. Команда только сравнивает ответы.</p><p>Например, есть старый модуль расчета скидок. Он учитывает промокоды, сегмент пользователя, историю покупок, регион и партнерские условия. Команда пишет новый сервис скидок. Чтобы не переключать пользователей сразу, можно запустить теневой режим:</p><p>Два момента, на которые стоит обратить внимание в этом коде. Теневой вызов запускается через asyncio.create_task — корутина сразу планируется в event loop и начнёт выполняться, как только функция вернёт управление. И весь блок завернут в try/except: исключение в новой логике не должно ронять основной запрос. Без этих двух свойств shadow testing рискует ухудшить продакшен вместо того, чтобы безопасно его проверить.</p><p>Небольшая оговорка для продакшена: event loop держит на task только слабую ссылку, и без сохранённой ссылки задача может быть собрана сборщиком мусора прямо во время выполнения. В реальном коде Task имеет смысл класть в set фоновых задач и удалять оттуда через add_done_callback. В примере выше эта обвязка опущена для читаемости.</p><p>Для критичной доменной логики — платежей, биллинга, расчета тарифов — допустимый уровень расхождения должен быть около нуля: цель в shadow-режиме не «как можно меньше различий», а «понимаем каждое расхождение». Для менее чувствительных доменов (рекомендации, ранжирование результатов поиска) можно жить с расхождением в долях процента, но и там расхождения нужно классифицировать, а не игнорировать. Возможно, это баги новой реализации. А возможно, старая система содержит устаревшую логику, которую нужно отдельно обсудить с бизнесом.</p><p>Shadow testing особенно полезен для критичных доменных частей: платежей, биллинга, расчета тарифов, персональных предложений, транзакций. Там нельзя просто «попробовать на пользователях» и посмотреть, что будет.</p><p>При этом важно отличать теневую проверку чтения от теневой проверки записи. Чтение проверить относительно дёшево: запрос идёт в обе системы, ответы сравниваются, никаких внешних эффектов нет. С записью всё сложнее. Если новая реализация в shadow-режиме действительно создаст заказ, спишет деньги или отправит письмо, у пользователя возникнут двойные эффекты. Поэтому для writes либо вводят идемпотентные ключи и shadow-режим без реальных побочных действий (внешние вызовы заменены no-op-стабами, БД — отдельной shadow-копией), либо вообще отказываются от теневой проверки записи в пользу постепенной выкатки за фича-флагом.</p><p>Сравнение ответов в реальной системе тоже не сводится к одной функции compare. Нужно отдельно решать, как игнорировать «нормальный» шум (метки времени, идентификаторы, порядок коллекций), как сэмплировать трафик, чтобы не утопить хранилище расхождений, и как организовать триаж — кто и в каком ритме разбирает накопившиеся диффы. Готовые решения этого класса — GitHub Scientist (Ruby и его порты в другие языки), Twitter Diffy, либо собственный лёгкий регистратор поверх Kafka и таблицы расхождений.</p><h2>С чего начинать модернизацию</h2><p>Начинать лучше не с самого больного и не с самого центрального модуля. Это звучит контринтуитивно, потому что обычно хочется сразу взяться за главный источник проблем. Но если начать с ядра системы, команда быстро упрется в максимальное количество зависимостей и рисков.</p><p>Удобный способ выбрать первый кусок — оценить кандидатов по двум осям: насколько модуль критичен для бизнеса (low / high) и насколько сильно он связан с остальной системой (low / high). Начинать стоит с квадранта low-criticality + low-coupling: ошибки в нем не уронят бизнес-показатели, а малое количество зависимостей позволит провести миграцию полностью, не утянув за собой смежные модули. Высоко-критичные и сильно связанные части (платежи, ядро авторизации) трогают в последнюю очередь — на этот момент команда уже наберёт опыт безопасной миграции.</p><p>Хорошие точки входа обычно: уведомления, генерация отчетов, поиск, история операций, профиль пользователя, отдельная часть каталога. Важно, чтобы у команды была возможность описать контракт: какие данные входят, какие выходят, какие ошибки возможны, какие внешние системы участвуют.</p><p>Допустим, в банковском приложении есть старый модуль истории операций. Он медленный, сложно расширяется, но при этом не выполняет сами транзакции. Это хороший кандидат для первой миграции. Ошибка в истории операций неприятна, но обычно менее критична, чем ошибка в списании денег.</p><p>Команда может вынести чтение истории в отдельный сервис, сначала запустить его в shadow-режиме, потом включить для части пользователей, затем полностью перевести чтение на новую реализацию. При этом критичная транзакционная логика останется в старой системе до тех пор, пока команда не наберет опыт безопасной миграции.</p><h2>Миграция данных: самая сложная часть</h2><p>Большая часть статьи говорит о маршрутизации запросов и переключении трафика. Но в реальных проектах основная сложность лежит ниже — в данных. Старая и новая реализации почти всегда работают с общим состоянием: одной БД, одним хранилищем документов, одним набором очередей. Переехать туда «одним коммитом» нельзя.</p><p>Базовый рабочий приём — Expand-Contract (он же Parallel Change). Изменение схемы делается в три такта. На этапе expand в БД добавляются новые поля, таблицы или индексы, при этом старое поведение полностью сохраняется. Затем — migrate: обе реализации начинают писать и в старое, и в новое место (dual writes), а отдельный фоновый процесс делает backfill — заполняет новые поля историческими данными. После этого читатели по одному переключаются на новую схему. Только когда никто из читателей не использует старую структуру, наступает contract — удаление лишних колонок и таблиц.</p><p>Несколько практических деталей, которые часто упускают:</p><p>·         Dual writes — это не бесплатная операция. Две записи означают две точки отказа. Если одна из них упала, нужно решать, что делать: продолжать ли работу, ставить ли событие в очередь на повтор, помечать ли запись как несогласованную. Простое «сначала пишем туда, потом сюда» в продакшене на нагрузке приводит к расхождениям.</p><p>·         Backfill часто длиннее, чем кажется. На большой таблице миграция в одном UPDATE блокирует продакшен. Поэтому backfill делают батчами по N тысяч строк с паузами, отслеживают прогресс и предусматривают возможность остановить и продолжить.</p><p>·         Онлайн-изменения схемы на крупных таблицах делаются не штатным ALTER TABLE, а специализированными инструментами: gh-ost или pt-online-schema-change для MySQL, встроенные онлайн-механизмы PostgreSQL для индексов и колонок, Liquibase/Flyway — для управления версионированием изменений в репозитории.</p><p>·         Shadow testing данные не покрывает. Можно сравнить, что новая реализация возвращает то же, что и старая, но если за этим стоит другая схема в БД, проверка корректности самой миграции данных — это отдельная работа: сверки, контрольные суммы, выборочный аудит исторических записей.</p><p>Без этих шагов любая красивая фасадная архитектура наталкивается на разъезжающиеся данные — и тогда даже идеальный Strangler Fig снаружи не спасает.</p><h2>Прокси-слой как точка контроля</h2><p>Чтобы постепенно заменять legacy-код, нужно управлять маршрутизацией запросов. Для этого часто создают прокси-слой, API Gateway или фасад, через который проходит обращение к старой и новой логике. В терминах Domain-Driven Design такой слой часто называют Anti-Corruption Layer: он защищает новую реализацию от старых контрактов и наоборот, позволяя двум моделям сосуществовать без взаимного «загрязнения».</p><p>Без такой точки контроля миграция становится хаотичной. Часть клиентов ходит напрямую в старый модуль, часть — в новый, часть использует обходные пути, а команда теряет возможность централизованно переключать трафик.</p><p>Прокси-слой решает несколько задач. Он скрывает детали реализации от клиентов, позволяет направлять часть запросов в новую систему, поддерживает фича-флаги, собирает метрики и упрощает откат.</p><p>В качестве технической основы команды обычно берут один из трех вариантов: классический API gateway (Kong, AWS API Gateway), service mesh (Envoy, Istio) или более простой reverse proxy (NGINX, HAProxy). Service mesh особенно удобен, когда трафик уже идёт внутри Kubernetes-кластера: маршрутизацию можно менять конфигурацией, без правок кода клиентов и сервисов.</p><p>Например, мобильное приложение обращается к endpoint /orders/history. Раньше этот endpoint напрямую обслуживал монолит. После введения API Gateway приложение продолжает ходить по тому же контракту, но внутри gateway может решать, куда направить запрос: в legacy-модуль или новый сервис истории заказов.</p><p>Управление маршрутизацией обычно делается не «всё или ничего», а на основании атрибутов запроса: значения заголовка (X-Migration-Cohort: new), куки, хэша от user-id (стабильное разбиение пользователей на сегменты) или географического региона. Это позволяет выкатывать новую реализацию сначала на одну страну, на сотрудников самой компании или на тестовый сегмент — и только потом расширять охват.</p><p>Для клиента ничего не меняется. Для команды появляется управляемость.</p><h2>Наблюдаемость: без метрик миграция превращается в гадание</h2><p>Постепенная модернизация невозможна без нормальной наблюдаемости. Если команда не видит, что происходит внутри системы, она не сможет безопасно переключать трафик.</p><p>Минимальный набор — это логи, метрики и распределенная трассировка (distributed tracing). Нужно понимать, сколько запросов идет в старую и новую реализацию, сколько ошибок возникает, как меняется latency, где появляются таймауты, какие статусы возвращаются, какие бизнес-метрики проседают.</p><p>Технические метрики стоит формулировать не как «средний ответ» и «процент ошибок», а в терминах SLI и SLO: целевые показатели вида «99.9% запросов на /orders/history отвечают быстрее 300 ms за 30 дней» с явным error budget. Latency измеряется по перцентилям (p50, p95, p99) — среднее значение почти всегда обманчиво, а хвосты распределения говорят о реальном опыте пользователя. На время миграции имеет смысл выставить отдельные SLO для нового и старого пути и сравнивать их.</p><p>В качестве инструментов де-факто стандартом стал OpenTelemetry для трассировок, метрик и логов — единый протокол, который пишет в практически любое хранилище. Дальше — Prometheus и Grafana для метрик, Jaeger или Tempo для traces, Sentry или аналог для ошибок. Для миграции важна возможность фильтровать метрики по «варианту» — отдельно по старому и новому пути — иначе все цифры смешаются и реальную динамику будет не видно.</p><p>Технических метрик недостаточно. Если команда переносит оформление заказа, важно смотреть не только на 500 ошибки и время ответа, но и на конверсию в оплату, количество брошенных корзин, повторы запросов, обращения в поддержку.</p><p>Пример: новая система формально отвечает быстрее старой и не дает ошибок. Но после включения на 10% пользователей падает конверсия в оплату. Причина может быть не в серверной ошибке, а в изменении порядка полей, другом тексте сообщения или потере какого-то edge-case. Без бизнес-метрик команда может решить, что миграция успешна, хотя для продукта она уже создает проблему.</p><h2>Практическая последовательность миграции</h2><p>Рабочая последовательность обычно выглядит так.</p><p>Сначала команда выбирает ограниченный участок системы. На этом этапе важно не просто назвать модуль, а описать его границы. Какие сценарии он закрывает? Кто его вызывает? Какие данные он читает и пишет? Какие внешние интеграции использует? Какие неочевидные бизнес-правила в нем есть?</p><p>Затем поверх legacy-логики создается стабильный контракт. Это может быть API, фасад, gateway или отдельный слой внутри приложения. Главная задача — сделать так, чтобы клиенты зависели не от внутренней реализации, а от понятного интерфейса. На этом этапе полезно вспомнить про contract testing (Pact, Spring Cloud Contract): автотесты со стороны потребителей фиксируют, что именно они ожидают от API, и предупреждают о ломающих изменениях до того, как они доедут до продакшена.</p><p>После этого рядом пишется новая реализация. Она должна повторять текущее поведение, а не сразу становиться «идеальной версией будущего». На этом этапе полезно фиксировать все расхождения: где старая система работает странно, где требования не описаны, где бизнес-правила требуют уточнения.</p><p>Следующий этап — shadow testing. Новая система получает копии реальных запросов, считает результат, но пользователю по-прежнему возвращается ответ legacy. Команда сравнивает результаты и устраняет расхождения.</p><p>Когда новая реализация достаточно стабильна, начинается постепенное переключение через feature toggles. Сначала внутренние пользователи, потом 1% реального трафика, затем 5–10%, затем 50% и только после этого 100%.</p><p>На каждом этапе команда смотрит на метрики. Если всё стабильно, движение продолжается. Если появляются проблемы, флаг выключается, трафик возвращается в legacy, а команда разбирает причины.</p><p>Последний этап — удаление старого кода. Это не формальность, а обязательная часть миграции. И «удалить старый код» — это не один коммит, а явный Definition of Done: вырезана старая ветка кода, удалён фича-флаг, обновлена документация и схемы архитектуры, переименованы или удалены устаревшие дашборды и алерты, обновлены runbook’и для on-call и проведено короткое внутреннее обучение. Если этого не сделать, через полгода никто уже не вспомнит, какой путь актуален, и легаси-ветвление останется в коде навсегда.</p><h2>Откат миграций: дешёвый только пока не пошли записи</h2><p>Откатить миграцию, в которой ещё не было записи в БД, легко: достаточно переключить фича-флаг, и трафик снова идёт через старую реализацию. Откатить миграцию, в которой новая система уже неделю писала данные в новые таблицы, — отдельный, гораздо более тяжёлый разговор.</p><p>Поэтому ещё на этапе проектирования каждое изменение должно сопровождаться явным планом отката. Удобно различать три типа шагов.</p><p>Полностью обратимые шаги. Чтение через новый сервис, расчёт «в тени», новые метрики. Откат — выключить флаг. Это самый комфортный режим, и в нём стоит держать миграцию как можно дольше.</p><p>Обратимые с компенсацией. Новая реализация пишет дополнительные данные (например, дублирует операции в новую таблицу), но старый источник тоже обновляется. Откат возможен, но требует решить, что делать с уже записанными данными: оставить, очистить, синхронизировать. План этих действий должен быть написан до выкатки, не во время инцидента.</p><p>Forward-only. После некоторой точки откат становится невозможен — например, после того, как старая схема удалена или внешние интеграции перенастроены на новый сервис. Такие шаги допустимы, но к ним нужно приходить отдельно, осознанно, с особенно строгими SLO в предыдущем этапе. До forward-only-перехода имеет смысл подержать систему в режиме параллельной работы дольше, чем по графику.</p><p>Базовое правило: ни один шаг миграции не должен уходить в продакшен, если у команды нет письменного ответа на вопрос «как мы откатываемся в случае проблемы». Иначе при инциденте откатываться будут на ходу — и не факт, что успешно.</p><h2>Пример: как тот же сервис мигрировали со второй попытки</h2><p>После неудачного опыта команда взялась за тот же сервис заново, но изменила подход.</p><p>На первом шаге они зафиксировали поведение существующего сервиса. На самые часто используемые сценарии (создание путевого листа, подпись акта осмотра, выгрузка пакета документов за период) написали характеристические тесты на реальных продакшен-данных, обезличенных и сохранённых как фикстуры. Любое будущее изменение поведения теперь падало в CI как явное расхождение.</p><p>Параллельно команда провела инвентаризацию побочных эффектов. Из исходного кода и логов выяснилось, что сервис не только хранит документы, но и: публикует событие в Kafka при смене статуса, инкрементирует счётчик в Redis для рейтинга водителей, отправляет webhook во внешнюю систему партнёра, пишет в таблицу аудита. Каждый из этих эффектов попал в отдельный пункт чек-листа «что должно остаться» в новой реализации.</p><p>Затем команда выбрала первый кусок для выноса — не весь сервис, а только чтение документов (GET /documents/{id} и GET /documents/by-driver/{driver_id}). Это была наименее рискованная часть: ошибки в чтении неприятны, но не ломают финансовые потоки.</p><p>Новый сервис написали на FastAPI рядом со старым. На уровне API Gateway появилось правило маршрутизации: запросы на чтение шли в старый сервис, но в фоне дублировались в новый. Ответ пользователю всегда возвращал legacy, а ответ нового сервиса сравнивался с эталоном и записывался в отдельную таблицу для разбора. Использовали обёртку поверх asyncio.create_task — на ответ пользователя теневой вызов не влиял.</p><p>За три недели shadow-режима команда нашла четыре расхождения. Два оказались багами новой реализации (округление времени, неправильная сортировка вложений). Два — давно забытыми особенностями старого сервиса (одно поле возвращалось в UTC, другое — в локальной зоне; так было исторически, бизнес не возражал, но в новой реализации захотели единый формат). Все четыре зафиксировали явно: баги — починили, особенности — согласовали с продуктовой командой как осознанное изменение.</p><p>Когда расхождений не осталось, включили фича-флаг на сотрудников самой компании. Через неделю — на 1% реальных водителей. Дальше шаг по 5%, 25%, 50%, 100% с паузой в несколько дней между этапами. На каждом шаге следили не только за HTTP-ошибками и latency, но и за продуктовыми метриками: количество подписанных актов, время от открытия документа до подписи, доля повторных запросов. Один раз пришлось откатиться с 25% на 5% — в одном из регионов выросло время отклика из-за неэффективного запроса. Исправили, выкатили снова.</p><p>Через два месяца чтение полностью перешло в новый сервис. Старый код чтения и фича-флаг удалили в том же релизе. После этого по той же схеме мигрировали запись документов, потом публикацию событий, потом импорт из внешних систем. Полная миграция заняла девять месяцев — почти столько же, сколько провалившийся Big Bang, — но продукт всё это время продолжал развиваться, инцидентов не было, и в конце команда осталась с системой, которую понимает.</p><h2>Типичные ошибки при работе с legacy</h2><p>Первая ошибка — пытаться улучшить всё сразу. Команда одновременно меняет архитектуру, бизнес-логику, контракты и инфраструктуру. В результате становится невозможно понять, какая именно часть вызвала проблему. Правильнее сначала воспроизвести поведение, стабилизировать новую реализацию и только потом улучшать.</p><p>Вторая ошибка — недооценивать скрытые зависимости и побочные эффекты. Legacy-код часто делает больше, чем кажется. На один и тот же вызов могут быть навешаны: запись в таблицу аудита, инкремент счётчика в кэше, публикация события в очередь, обновление статуса связанной сущности, инвалидация кэша, дёрганье webhook’а во внешнюю систему. Если в новой реализации воспроизвести только явный путь, скрытые потребители молча перестанут получать данные — и узнают об этом через жалобу бизнеса, а не через ошибку в логах. Поэтому перед выносом любого модуля имеет смысл составить инвентаризацию побочных эффектов: пройтись по коду и логам и выписать каждое нелогичное действие отдельным пунктом чек-листа.</p><p>Третья ошибка — отсутствие наблюдаемости. Без логов, метрик и трассировки команда не управляет миграцией, а угадывает. Особенно опасно смотреть только на технические ошибки и игнорировать бизнес-показатели.</p><p>Четвертая ошибка — не договариваться с бизнесом. Модернизация не должна быть невидимой «инженерной активностью в стол». Её нужно встраивать в roadmap, объяснять эффект и договариваться о приоритетах. Если бизнес не понимает, зачем команда тратит время на миграцию, работа будет постоянно проигрывать новым фичам.</p><p>Пятая ошибка — не удалять старый код. Временное сосуществование старой и новой логики нормально. Вечное сосуществование — нет. Если legacy не удаляется, технический долг не уменьшается, а просто меняет форму.</p><p>Шестая ошибка — не удалять фича-флаги после миграции. Флаг, который сыграл свою роль и больше никогда не выключается, превращается в постоянное ветвление в коде. Через год команда не помнит, можно ли удалить такую ветку или там сидит важный edge-case. Через два — кода с такими «мёртвыми» флагами становится больше, чем основной логики. Поэтому каждый флаг должен заводиться с условием удаления («после полной выкатки и двух недель стабильной работы») и иметь ответственного, кто этим удалением займётся.</p><p>Отдельно стоит упомянуть организационную сторону. Закон Конвея работает и в обратную сторону: если новый и старый код владеются разными командами с разными приоритетами, миграция будет тормозиться независимо от выбранного паттерна. На время миграции имеет смысл явно проговорить, кто отвечает за переход, и не разделять старую и новую реализации между несовместимыми roadmap’ами.</p><h2>Компромиссы, к которым нужно быть готовыми</h2><p>Постепенная модернизация безопаснее Big Bang-переписывания, но она не бесплатна. Некоторое время система будет сложнее, чем раньше. В ней появятся старый и новый код, прокси-слой, фича-флаги, дублирование логики, дополнительные метрики.</p><p>Shadow testing увеличит нагрузку на инфраструктуру, потому что часть запросов будет обрабатываться дважды. Команде придется поддерживать дисциплину: документировать контракты, отслеживать флаги, удалять старую реализацию после миграции, поддерживать contract-тесты в актуальном состоянии.</p><p>Но это контролируемая сложность. Она распределена во времени и управляется инженерными практиками. В отличие от Big Bang-риска, где команда долго работает с минимальной обратной связью, а потом выкатывает один большой релиз с максимальной неопределенностью.</p><h2>Когда Strangler Fig особенно оправдан</h2><p>Постепенная миграция особенно хорошо подходит для систем, где downtime невозможен или слишком дорог. Это финтех, e-commerce, биллинг, мобильные бэкенды с большой аудиторией, высоконагруженные продукты, старые монолиты и системы с большим количеством интеграций.</p><p>Если продуктом ежедневно пользуются сотни тысяч или миллионы людей, нельзя позволить себе «переписать и посмотреть, что будет». Нужно менять архитектуру так, чтобы пользователь не замечал процесса миграции.</p><p>Этот подход также полезен там, где бизнес продолжает активно развивать продукт. Если фичи нельзя заморозить на полгода, модернизация должна идти параллельно с продуктовой разработкой.</p><h2>Когда модернизацию лучше не делать</h2><p>Постепенная миграция — мощный инструмент, но у неё тоже есть стоимость, и иногда правильный ответ — оставить систему как есть. Несколько сценариев, в которых модернизация плохо окупается.</p><p>Продукт, который уходит из эксплуатации. Если через год сервис будет выключен или заменён на покупное решение, тратить квартал на его рефакторинг бессмысленно. Достаточно стабилизировать то, что есть.</p><p>Модуль, который никто не трогает. Если код десятилетней давности продолжает работать, не падает, не требует изменений и не вызывает инцидентов, его «уродливость» — не повод его переписывать. Цель модернизации — упростить будущие изменения; если будущих изменений нет, цели тоже нет.</p><p>Регулируемые системы с тяжёлой ресертификацией. В банковских, медицинских и государственных контурах любое изменение в критичной системе может потребовать повторной сертификации, перепрохождения аудитов, обновления договорной обвязки. В таких условиях стоимость модернизации может на порядок превышать стоимость поддержки текущей реализации, и решение нужно принимать вместе с владельцем продукта и юристами, а не только инженерным составом.</p><p>Простой тест: если на вопрос «какой бизнес-сценарий мы откроем после миграции» нет внятного ответа — модернизацию имеет смысл отложить и заняться чем-то другим.</p><h2>Что получает команда</h2><p>Главный результат постепенной модернизации — управляемость. Команда начинает лучше понимать систему, контролировать изменения и снижать риск инцидентов.</p><p>Появляются понятные контракты, наблюдаемость, практика безопасных релизов, культура удаления старого кода. Разработчики перестают бояться legacy, потому что у них появляется метод, а не только желание «когда-нибудь всё переписать».</p><p>Для бизнеса это тоже выгодно. Продукт продолжает развиваться, сроки становятся более прогнозируемыми, риски крупных сбоев снижаются, а технический долг постепенно уменьшается.</p><h2>Модернизация — это процесс, а не проект</h2><p>Legacy нельзя «починить за квартал». Если система развивалась годами, она не станет простой после одного рефакторинга. Но её можно системно улучшать.</p><p>Strangler Fig Pattern, Branch by Abstraction, feature toggles, shadow testing и аккуратная миграция данных дают рабочую модель: выбрать ограниченный участок, описать контракт, реализовать новую версию, проверить её на реальном трафике, постепенно переключить пользователей и удалить старый код.</p><p>Это не самый быстрый путь. Зато он управляемый. А в зрелых продуктах управляемость важнее скорости.</p><p>Потому что цель модернизации — не написать красивую новую систему. Цель — сделать так, чтобы продукт продолжал развиваться, команда могла безопасно вносить изменения, а пользователи не становились участниками инженерного эксперимента.</p>]]></content:encoded>
    </item>
    <item>
      <title>Production-safe агентный цикл: как не дать ИИ сжечь бюджет</title>
      <link>https://tproger.ru/articles/production-safe-agentnyj-cikl-kak-ne-dat-ii-szhech-byudzhet</link>
      <comments>https://tproger.ru/articles/production-safe-agentnyj-cikl-kak-ne-dat-ii-szhech-byudzhet?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/production-safe-agentnyj-cikl-kak-ne-dat-ii-szhech-byudzhet</guid>
      <description><![CDATA[<p>Как построить production-safe агентный цикл на Python, чтобы ИИ не сжигал бюджет в бесконечных итерациях. Разбираем circuit breaker, ledger и human attestation.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/production-safe-agentnyj-cikl-kak-ne-dat-ii-szhech-byudzhet">Production-safe агентный цикл: как не дать ИИ сжечь бюджет</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Фреймворки и библиотеки]]></category>
      <category><![CDATA[Информационная безопасность]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 17 Jun 2026 09:45:21 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если ваш ИИ-агент работает круглосуточно и не может остановиться — это не автономность, а биллинговая авария, которая уже идёт. В июле 2025 года рекурсивный агентный цикл в Claude Code сжёг от 16 000 до 50 000 долларов за пять часов. Агенты не падают и не выдают ошибку: они делают ровно то, что им сказали, — пока кто-то не скажет остановиться.</p><p>Через четыре месяца четырёхагентный пайплайн на LangChain крутился одиннадцать дней и стоил 47 000 долларов. Никто не заметил, пока не пришёл счёт. Тот же паттерн: цикл работал корректно, но у него не было условия выхода.</p><p>Проблема не в моделях, а в отсутствии условия остановки. В этой статье разберём, как собрать минимальный, но production-ready каркас агентного цикла: спецификацию до запуска, предохранитель по токенам и ходам, неизменяемый аудит и поверхность для человеческого согласования. Полный код и 80 тестов с 100% покрытием доступны в <a href="https://github.com/dannwaneri/production-safe-agent-loop">репозитории автора оригинала</a>.</p><p>Агентные циклы сжигают бюджет не из-за плохих моделей, а из-за нечёткого условия остановки.</p><p>Спецификация должна отвечать на три вопроса: что делает, что не делает и что значит «готово» — в одном предложении.</p><p>Circuit breaker режет цикл по жёстким потолкам: число ходов и суммарные токены. Проверка — до вызова модели, а не после.</p><p>Ledger в SQLite фиксирует каждый ход: хеш входа, дельту токенов, время, результат. Это аудит, а не лог для отладки.</p><p>Review surface даёт поверхность для обязательной человеческой аттестации и формирует аудиторскую квитанцию frame_hash, которую вызывающий код может использовать как условие передачи результата в прод.</p><h2>Что такое агентный цикл и почему он уходит в бесконечность</h2><p>Агентный цикл — это конструкция вида while True, внутри которой языковая модель получает задачу, вызывает инструменты, анализирует результат и решает, продолжать или закончить. Такие циклы лежат в основе оркестраторов вроде LangGraph, CrewAI и AutoGen, а также внутри coding-агентов. Если только начинаете разбираться с LLM, полезно сначала понять, <a href="https://tproger.ru/articles/chto-takoe-llm-dlya-nachinayushhih">как устроены большие языковые модели</a>, а для практики — заглянуть в <a href="https://tproger.ru/articles/python-dlya-nachinayushhih">основы Python</a>.</p><p>Цикл уходит в бесконечность не потому, что модель «глупая», а потому, что никто не определил, что значит «готово». Модель видит неоднозначность и пытается быть полезной: перефразирует вызов инструмента, запускает верифицирующего агента, тот находит «проблему», срабатывает корректирующий агент — и так далее. На дашбордах всё выглядит активно: растёт число вызовов инструментов, completion rate держится высоким, а бюджет течёт в пустоту.</p><p><b>Почему дорожает каждая итерация:</b><br />Агент не начинает с чистого листа. Он каждый раз перечитывает всё предыдущее окно контекста — все неудачные попытки, все промежуточные выводы. Итерация 1 стоит 100 токенов, итерация 10 — уже тысячи. Вы платите за каждый провал снова и снова.</p><h2>Почему компании сначала платят за чатбота, а потом за агентный рабочий процесс</h2><p>Gartner фиксирует разрыв в потреблении токенов между пилотными чатботами и production-агентными рабочими процессами в 5–30 раз. А отчёт FinOps Foundation за 2026 год говорит, что 73% компаний превысили изначальный бюджет на ИИ. Цифры взяты из <a href="https://www.freecodecamp.org/news/how-to-build-a-production-safe-agent-loop-from-exit-conditions-to-audit-trails/">оригинального туториала</a>; полные отчёты Gartner и FinOps Foundation доступны по платным подпискам. Причина разрыва — в неправильном масштабировании: команда планировала стоимость чатбота (~0,04 USD за взаимодействие), а в прод ушёл мультиагентный оркестр (~1,20 USD за взаимодействие, до 70x на сложных задачах).</p><blockquote>A loop that runs without an exit condition isn't autonomous. It's a billing event waiting to happen.</blockquote><h2>Пять примитивов, которые ловят большинство отказов</h2><p>Автор оригинального туториала предлагает не монолитный фреймворк, а пять независимых Python-модулей, которые можно встроить в любой проект. Вместе они покрывают три уровня риска: дисциплину до запуска, принудительную остановку во время работы и доказательства после.</p><ol><li><b>Spec writer</b> — заставляет ответить на три вопроса до первого вызова модели.</li><li><b>Circuit breaker</b> — режет цикл, если превышены потолки по ходам или токенам.</li><li><b>Ledger</b> — ведёт append-only журнал каждого хода в SQLite.</li><li><b>Agent loop</b> — связывает три компонента в единый цикл.</li><li><b>Review surface</b> — собирает пятиэлементный фрейм и требует человеческой аттестации перед выдачей результата.</li></ol><h2>Фаза 1. Определить «готово» до первой строчки кода</h2><p>Самая дорогая ошибка в разработке агентов — не выбор модели, а начало кодинга до того, как команда может одним предложением описать условие завершения. «Агент проверит сайт» — не подходит. «Агент обходит целевой URL, извлекает все теги &lt;title&gt; и &lt;meta name="description"&gt;, помечает отсутствующие или слишком длинные и останавливается» — подходит.</p><p>Spec writer интерактивно запрашивает три поля, сохраняет их значения в SQLite и возвращает неизменяемый SpecResult(frozen=True). Полученный session_id связывает спецификацию, строки журнала и итоговый результат в одну трассируемую сессию.</p><p><b>Почему frozen=True:</b><br />Спецификация — это обязательство, а не черновик. frozen=True запрещает переприсваивать поля объекта SpecResult, поэтому код цикла не может «подвинуть» условие завершения посреди запуска.</p><h2>Фаза 2. Принудить «готово» на лету</h2><p>Circuit breaker задаёт два жёстких потолка: turn_limit — максимальное число обращений к модели, и token_limit — суммарное число токенов за всю сессию. Каждый потолок — «строго больше»: если лимит 5 ходов, пятый ещё разрешён, шестой выбросит исключение.</p><p>Ключевое правило: breaker.check() вызывается до запроса к модели, а не после. Постфактум проверка бессмысленна: токены уже сожжены. Исключение, а не код возврата, — чтобы нельзя было промолчать.</p><h3>Как подобрать лимиты для продакшена</h3><p>Демонстрационные значения 5 ходов / 15 000 токенов слишком жёсткие для реальных задач. Для продакшена автор предлагает настроить лимиты под свой бюджет; в туториале приведён пример breaker = CircuitBreaker(turn_limit=10, token_limit=50000). Если одна сессия должна стоить не дороже 1 USD, а средний ход — 0,10 USD, получается порядка 10 ходов. Конкретный token_limit выбирается исходя из прайсинга модели и среднего размера контекста: чем длиннее история диалога, тем раньше сработает потолок.</p><ul><li>Стартуйте с жёсткими лимитами и разрешайте рост только по метрикам, не по интуиции.</li><li>Отдельно лимитируйте retry-политику: каждый повторный запрос увеличивает и turn_count, и объём контекста.</li><li>Не смешивайте лимит токенов с лимитом выходных токенов модели; circuit breaker считает сумму input + output.</li></ul><h2>Фаза 3. Записывать всё, что нельзя подделать</h2><p>Circuit breaker защищает бюджет. Ledger защищает понимание того, что произошло. Это не лог для отладки, а журнал аудита: каждая строка — один ход, append-only, без обновлений и удалений.</p><p>Три решения стоит взять на заметку. Во-первых, вместо исходного текста сохраняется SHA-256 хеш входа: так не утекают персональные данные, а одинаковые входы разных запусков можно сравнивать. Во-вторых, pass_fail хранится как INTEGER (1/0), потому что у SQLite нет булева типа. В-третьих, временная метка — datetime.now(timezone.utc).isoformat(), так как datetime.utcnow() объявлен устаревшим в Python 3.12.</p><h2>Фаза 4. Цикл, который уважает границы</h2><p>Agent loop — единственный компонент, который обращается к языковой модели. Всё остальное работает локально: проверка потолков, запись в журнал, оценка условия выхода.</p><p>Анатомия одного хода простая и строгая: сначала breaker.check(), потом вызов модели, потом ledger.write(), потом проверка stop_reason. Если модель вернула end_turn — возвращаем результат. Если нет — добавляем сообщение continue и идём на следующий круг.</p><p>Этот вариант цикла — минимальный текстовый. Если агент использует инструменты, в Anthropic API stop_reason может быть tool_use: тогда нужно выполнить инструмент, вернуть его результат в messages и только потом решать, продолжать или завершать.</p><p>Системный промпт обязательно включает все три поля спецификации, а не только done_looks_like. Модели нужна негативная область — то, что агент делать не должен (what_it_does_not), — не меньше, чем позитивная: иначе она начнёт «добавлять ценность» за рамками задачи.</p><h2>Фаза 5. Поверхность согласования: цикл бежит к человеку</h2><p>Circuit breaker и ledger решают технические проблемы, но не отвечают на вопрос: «Соответствует ли результат тому, что обещали?» Именно здесь ошибки проходят в прод: вывод выглядит аккуратным, дашборд зелёный, ревьюер ставит галочку.</p><p>Review surface собирает пятиэлементный фрейм из SQLite и требует явной аттестации:</p><ol><li><b>Исходное обещание</b> — три поля спецификации.</li><li><b>Критерий приёмки</b> — поле done_looks_like как явный бенчмарк.</li><li><b>Diff</b> — вход первого хода, выход последнего, число ходов, токены, сработал ли breaker.</li><li><b>Доказательства</b> — все строки ledger за сессию.</li><li><b>Неразрешённые допущения</b> — строки с breach_reason и failed-ходами.</li></ol><p>После согласования ревьюер вызывает attest(). Функция собирает пятиэлементный фрейм в каноническом порядке и считает от него SHA-256 — получается frame_hash. Это аудиторская квитанция: она доказывает, что ревьюер видел именно этот фрейм, а не краткое резюме.</p><h2>Практический пример: SEO-аудит по расписанию</h2><p>Автор приводит пример SEO-аудита. SEO-аудит имеет естественный ритм: обход, выявление проблем, исправление, ожидание переиндексации. Запускать агента 24/7 бессмысленно — он будет сжигать токены в паузах между событиями. Честная архитектура — cron-задача, которая запускает цикл по расписанию.</p><p><b>Пример упрощён:</b><br />В production-варианте стоит проверять URL (допустимые схемы и хосты) и оборачивать requests.get в try/except requests.RequestException, чтобы агент не падал при недоступности сайта.</p><p>Cron-строка выглядит так:</p><p>Агент выполняет работу, записывает ходы в ledger, и если circuit breaker сработал — результат уходит на человеческую проверку, а не в прод.</p><h2>Провайдер-независимость через адаптер</h2><p>Цикл работает с любым клиентом, удовлетворяющим протоколу LLMClient. По умолчанию используется Anthropic, но через адаптер можно подключить OpenAI, Gemini, Ollama, локальные модели или собственный сервер. В репозитории автора показан иллюстративный пример адаптера для OpenAI. Главное — привести ответ к форме, которую ожидает AgentLoop: usage.input_tokens, usage.output_tokens, content[0].text, stop_reason.</p><h2>Выводы: дисциплина дороже модели</h2><p>Большинство аварий с агентными циклами предотвращаются не интеллектом модели, а чёткими границами. Спецификация до запуска, жёсткие потолки ресурсов, неизменяемый аудит и человеческое согласование — это минимальный набор примитивов, который отделяет автономного агента от неконтролируемого биллингового события.</p><p>Для российских команд это означает, что внедрять LLM-агентов без бюджетных предохранителей — всё равно что запускать бесконечный цикл с доступом к корпоративной карте. Начните с пяти модулей, описанных выше, прогоните их на тестовом дубле модели и только после этого открывайте доступ к реальным API.</p><blockquote>Define what done looks like before you start. That's the job, and always has been.</blockquote><p>Полный код и 80 тестов с 100% покрытием доступны в репозитории автора оригинала: <a href="https://github.com/dannwaneri/production-safe-agent-loop">github.com/dannwaneri/production-safe-agent-loop</a>.</p><p><b>Источник:</b> <a href="https://www.freecodecamp.org/news/how-to-build-a-production-safe-agent-loop-from-exit-conditions-to-audit-trails/">How to Build a Production-Safe Agent Loop — From Exit Conditions to Audit Trails</a>, freeCodeCamp.</p>]]></content:encoded>
    </item>
    <item>
      <title>Три CVE в LiteLLM позволяют захватить ИИ-шлюз</title>
      <link>https://tproger.ru/news/tri-cve-v-litellm-pozvolyayut-zahvatit-ai-wlyuz</link>
      <comments>https://tproger.ru/news/tri-cve-v-litellm-pozvolyayut-zahvatit-ai-wlyuz?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/tri-cve-v-litellm-pozvolyayut-zahvatit-ai-wlyuz</guid>
      <description><![CDATA[<p>Три связанные уязвимости LiteLLM оценены в CVSS 9,9. Разбираем, как обычный пользователь становится админом ИИ-шлюза и как защитить свою инфраструктуру.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/tri-cve-v-litellm-pozvolyayut-zahvatit-ai-wlyuz">Три CVE в LiteLLM позволяют захватить ИИ-шлюз</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Информационная безопасность]]></category>
      <category><![CDATA[Уязвимость]]></category>
      <category><![CDATA[Кибербезопасность]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 16 Jun 2026 13:30:50 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если в вашей сети стоит <a href="https://github.com/BerriAI/litellm">LiteLLM</a> — проверьте версию. Исследователи <b>Obsidian Security</b> обнаружили цепочку из трёх уязвимостей, которая позволяет обычному пользователю с минимальными правами стать администратором прокси и выполнять произвольный код на сервере. Полная цепочка оценена в <b>CVSS 9,9</b> — критический уровень.</p><p>LiteLLM — популярный open-source ИИ-шлюз, который выступает единой точкой доступа к свыше ста провайдерам моделей (OpenAI, Anthropic, Google Gemini, AWS Bedrock, Azure и другим). Через него проходят API-ключи, промпты и ответы, поэтому компрометация шлюза равнозначна компрометации всей ИИ-инфраструктуры.</p><p>Три CVE объединяются в цепочку: CVE-2026-47101 (обход авторизации), CVE-2026-47102 (повышение привилегий) и CVE-2026-40217 (выполнение кода).</p><p>Оценка полной цепочки — CVSS 9,9. Отдельная CVE-2026-47102 получила 8,7 по CVSS 4.0 и 8,8 по CVSS 3.1.</p><p>Исправление вошло в релиз LiteLLM v1.83.14-stable, опубликованный 2 мая 2026 года — это первый релиз с полным набором патчей.</p><p>Компрометация открывает доступ к мастер-ключу LiteLLM, salt-ключу для расшифровки сохранённых учётных данных, URL базы данных и всем провайдер-ключам, а также позволяет подменять ответы модели.</p><p>Это не первый серьёзный инцидент с LiteLLM в 2026 году: в марте злоумышленники скомпрометировали PyPI-релизы проекта, а в апреле критическая SQL-инъекция эксплуатировалась менее чем через сутки после раскрытия. Новая цепочка пока не зафиксирована в реальных атаках, но её потенциальная опасность сопоставима с полным захватом сервера.</p><h2>Как работает цепочка</h2><p>Атака строится на том, что разные уровни проверок доверяют данным, которые присылает пользователь. Роль internal_user — это учётная запись с низкими правами по умолчанию, которую часто выдают обычным сотрудникам.</p><ul><li><b>CVE-2026-47101 — обход авторизации.</b> Обычный internal_user при создании виртуального ключа может указать поле allowed_routes без проверки. Значение ["/*"] даёт доступ ко всем маршрутам, включая административные.</li><li><b>CVE-2026-47102 — повышение привилегий.</b> Эндпоинт /user/update позволяет пользователю редактировать собственную запись и записать user_role: "proxy_admin". После этого атакующий становится полным администратором.</li><li><b>CVE-2026-40217 — выполнение кода.</b> Механизм Custom Code Guardrail (он запускает Python-скрипты для проверки запросов) компилирует код администратора через exec() без фильтрации. Если в глобальном пространстве имён (globals) не убран __builtins__, Python автоматически подкладывает встроенные функции — нужно лишь вызвать os.system для обратного шелла.</li></ul><h2>Чем это опасно</h2><p>Шлюз сидит между агентом и моделью, поэтому взломанный прокси читает и может изменять всё, что через него проходит. Атакующий получает мастер-ключ LiteLLM, salt-ключ для расшифровки сохранённых учётных данных, URL СУБД и все провайдер-ключи. Кроме утечки данных, он может подменять ответы модели: в демонстрации Obsidian встроенный обратный вызов LiteLLM (callback) подменил ответ Claude Code на поддельный вызов инструмента. Пользователь напечатал одно слово hello, а агент выполнил код, открывший реверс-шелл на машине разработчика.</p><h2>Что делать</h2><ul><li>Обновиться до LiteLLM v1.83.14-stable или новее — это первый релиз с полным набором патчей.</li><li>Перепроверить всех пользователей с ролью proxy_admin: в LiteLLM эта роль может запускать произвольный код через Custom Code Guardrail и MCP, то есть фактически даёт root-доступ на хост.</li><li>Проверить обратные вызовы (callbacks) в litellm_settings.callbacks в config.yaml: они не видны в интерфейсе, но исполняются на каждом запросе.</li><li>Провести аудит Custom Code Guardrail и убедиться в целостности развёрнутого кода, а не только конфигурации.</li><li>При подозрении на компрометацию сменить провайдер-ключи, учётные данные БД и MCP-токены.</li></ul><p>Контекст: ранее Tproger уже разбирал <a href="https://tproger.ru/news/kak-paket-dlya-raboty-s-nejrosetyami-stal-stilerom--polnyj-razbor-">supply chain-атаку на LiteLLM</a>, в ходе которой вредоносные версии пакета попали на PyPI.</p><p>Если в вашей сети есть LiteLLM — обновление и аудит прав стоит провести до конца недели. Подробнее — в <a href="https://thehackernews.com/2026/06/litellm-vulnerability-chain-lets-low.html">первоисточнике</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как строить диаграммы рассеивания в Python с помощью plt.scatter()</title>
      <link>https://tproger.ru/articles/kak-stroit-diagrammy-rasseyaniya-v-python-s-pomoshhyu-plt-scatter</link>
      <comments>https://tproger.ru/articles/kak-stroit-diagrammy-rasseyaniya-v-python-s-pomoshhyu-plt-scatter?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-stroit-diagrammy-rasseyaniya-v-python-s-pomoshhyu-plt-scatter</guid>
      <description><![CDATA[<p>Разбираем plt.scatter() в Matplotlib. Создаём scatter-графики, настраиваем маркеры по размеру, цвету и форме, используем цветовые карты и фильтруем данные масками.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-stroit-diagrammy-rasseyaniya-v-python-s-pomoshhyu-plt-scatter">Как строить диаграммы рассеивания в Python с помощью plt.scatter()</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Jupyter Notebook]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 14 Jun 2026 10:00:21 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Быстрый старт с plt.scatter()</h2><p>Чтобы начать, установите Matplotlib через pip. После этого достаточно импортировать pyplot под стандартным псевдонимом plt, подготовить два массива данных и вызвать plt.scatter(). В обычном скрипте график нужно явно показать через plt.show(), а в Jupyter Notebook или интерактивной консоли это необязательно.</p><p>Полученный график показывает обратную зависимость: чем выше цена, тем ниже средние продажи. При этом напиток за 4.02 выбивается из общей тенденции, что намекает на его особую популярность и требует дальнейшего анализа.</p><h2>plt.scatter() против plt.plot()</h2><p>Тот же базовый график можно построить функцией plt.plot(), передав маркер "o". Однако замеры через timeit обычно показывают, что plt.plot() работает в несколько раз быстрее. Зачем тогда нужен scatter()? Дело в гибкости: только plt.scatter() позволяет задавать индивидуальный размер, цвет и прозрачность для каждой точки.</p><p>Правило простое: для сырых точек без изысков выбирайте plt.plot(), а если нужно раскодировать дополнительные переменные визуально, используйте plt.scatter().</p><h2>Кастомизация маркеров</h2><h3>Размер точек: параметр s</h3><p>Допустим, владелец кофейни хочет добавить на график информацию о марже каждого напитка. Параметр s отвечает за площадь маркера. Чтобы разница была заметна, удобно передать массив значений и отмасштабировать его, например, умножив на 10.</p><h3>Цвет: параметр c</h3><p>Цвет помогает выделить категории. Можно задать его вручную через RGB-кортежи: зелёный для низкого содержания сахара, жёлтый для среднего и красный для высокого. Параметр c принимает список цветов той же длины, что и данные.</p><h3>Форма: параметр marker</h3><p>Когда на одном полотне оказываются два разных набора, важно различать их на глаз. По умолчанию точки рисуются кругами "o", но для второго набора можно выбрать другой символ, например, ромб "d".</p><h3>Прозрачность: параметр alpha</h3><p>Если точки накладываются друг на друга, часть данных становится невидимой. Параметр alpha задаёт прозрачность от 0 до 1. Значение 0.5 делает маркеры полупрозрачными, и совпадающие наблюдения перестают прятаться.</p><h2>Непрерывный цвет и стили</h2><p>Вместо фиксированных RGB-цветов можно передать в c числовой массив и выбрать цветовую карту через cmap. Это превращает маркеры в градиент, а plt.colorbar() добавит шкалу значений. Кроме того, оформление можно поменять глобально: plt.style.use("seaborn-v0_8") применит стилистику, близкую к Seaborn.</p><p>Список доступных стилей возвращает команда plt.style.available. Экспериментируйте с ними, чтобы быстро привести график к единому виду без ручной настройки каждой детали.</p><h2>Продвинутая техника: маскирование данных</h2><p>С помощью булевых массивов NumPy можно разбить точки на группы прямо на графике. Представьте, что вы моделируете расписание автобусов: сгенерировали случайные минуты и вероятности, наложили теоретическое распределение, а затем оставили только те точки, что попадают под кривую.</p><h2>Шпаргалка по параметрам</h2><ul><li>x, y — координаты точек (обязательные аргументы).</li><li>s — размер маркера: одно число или массив значений для каждой точки.</li><li>c — цвет в формате RGB, название или массив чисел для цветовой карты.</li><li>marker — форма точки: 'o' (круг), 'd' (ромб), 'x' (крест) и другие.</li><li>cmap — название цветовой карты, применяется вместе с числовым c.</li><li>alpha — прозрачность от 0 (полностью прозрачный) до 1 (непрозрачный).</li></ul><h2>Выводы</h2><p>Функция plt.scatter() превращает плоскую диаграмму рассеяния в многослойный инструмент анализа. На одном полотне можно одновременно показать цену, объём продаж, прибыльность, категорию товара и его характеристики через размер, цвет и форму маркеров. Для простых задач по-прежнему удобен plt.plot(), но стоит ли задача исследовать сложные взаимосвязи, scatter() становится незаменим.</p>]]></content:encoded>
    </item>
    <item>
      <title>Вышел PyPy 7.3.23 с исправлениями корутин и C-расширений</title>
      <link>https://tproger.ru/news/vywel-pypy-7-3-23-s-ispravleniyami-korutin-i-c-raswirenij</link>
      <comments>https://tproger.ru/news/vywel-pypy-7-3-23-s-ispravleniyami-korutin-i-c-raswirenij?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/vywel-pypy-7-3-23-s-ispravleniyami-korutin-i-c-raswirenij</guid>
      <description><![CDATA[<p>Команда PyPy выпустила версию 7.3.23 с исправлениями ошибок. Обновление убирает лишние предупреждения о корутинах, чинит множественное наследование в C-расширениях и приближает формат дизассемблера к CPython.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/vywel-pypy-7-3-23-s-ispravleniyami-korutin-i-c-raswirenij">Вышел PyPy 7.3.23 с исправлениями корутин и C-расширений</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 27 May 2026 12:15:24 GMT</pubDate>
      <content:encoded><![CDATA[<p>Команда PyPy выпустила версию 7.3.23. Релиз содержит исправления ошибок с корутинами и C-расширениями, а также изменения в байт-кодовом интерпретаторе.</p><h2>Исправления корутин и C-расширений</h2><p>Обновление устраняет избыточное предупреждение о неиспользуемых корутинах: интерпретатор теперь корректно отслеживает жизненный цикл асинхронных объектов и не выдаёт ложных предупреждений при штатной работе с asyncio. Кроме того, разработчики починили множественное наследование в C-расширениях — ранее комбинация нескольких базовых типов из cpyext могла приводить к некорректному порядку разрешения методов (MRO) и падениям при инициализации объектов. Эти исправления повышают стабильность при работе с асинхронным кодом и нативными библиотеками.</p><h2>Изменения в байт-коде</h2><p>В байт-кодовом интерпретаторе появились таблицы исключений вместо выделенных опкодов. Теперь вывод дизассемблера ближе к формату CPython, что упрощает отладку и сравнение поведения интерпретаторов. На производительности это пока не отразилось, но в будущем изменение позволит унифицировать обработку исключений между реализациями.</p><h2>Версии и совместимость</h2><p>Сборка PyPy3.11 поддерживает стандартную библиотеку CPython 3.11.15, а PyPy2.7 ориентирована на Python 2.7.18+ с бэкпортами исправлений безопасности. Разработчики подтвердили совместимость API с предыдущими выпусками линейки 7.3 и рекомендуют установить патч.</p><h2>Ссылки</h2><p>Подробности обновления — в <a href="https://pypy.org/posts/2025/05/pypy-v7323-release.html">официальном анонсе PyPy 7.3.23</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>GitHub взломали через отравлённое VS Code расширение: похищено ~3 800 внутренних репозиториев</title>
      <link>https://tproger.ru/news/github-vzlomali-cherez-otravlyonnoe-vs-code-raswirenie-pohishheno</link>
      <comments>https://tproger.ru/news/github-vzlomali-cherez-otravlyonnoe-vs-code-raswirenie-pohishheno?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/github-vzlomali-cherez-otravlyonnoe-vs-code-raswirenie-pohishheno</guid>
      <description><![CDATA[<p>GitHub подтвердил взлом через отравлённое расширение VS Code. TeamPCP похитила ~3 800 репозиториев, код продаётся за $50 000. Данные клиентов не пострадали.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/github-vzlomali-cherez-otravlyonnoe-vs-code-raswirenie-pohishheno">GitHub взломали через отравлённое VS Code расширение: похищено ~3 800 внутренних репозиториев</a>»</p>]]></description>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 22 May 2026 05:23:59 GMT</pubDate>
      <content:encoded><![CDATA[<p>GitHub подтвердил: один сотрудник установил отравлённое расширение для VS Code — и в результате хакеры получили доступ к ~3 800 внутренних репозиториев компании. Это первый публично подтверждённый случай, когда атака через маркетплейс расширений IDE привела к взлому самой платформы.</p><p>20 мая 2026 года GitHub официально признал взлом: злоумышленники скомпрометировали устройство сотрудника через вредоносную версию расширения для Visual Studio Code. Ответственность взяла группировка TeamPCP, предлагающая похищенный код на форумах за $50 000.</p><ul><li>GitHub подтвердил несанкционированный доступ к ~3 800 внутренних репозиториев 20 мая 2026 года</li><li>Вектор атаки — отравлённая версия расширения VS Code (предположительно Nx Console v18.95.0), доступная в маркетплейсе ~11 минут</li><li>Ответственность взяла группировка TeamPCP (UNC6780), данные выставлены на продажу за $50 000</li><li>Данные клиентов GitHub не пострадали — взлом затронул только внутренние репозитории</li><li>GitHub ротировал критические ключи, изолировал устройство и удалил вредоносное расширение</li></ul><h2>Как работала атака</h2><p>Схема взлома — многоступенчатая. Хакеры загрузили отравлённую версию популярного расширения в маркетплейс VS Code, которым управляет материнская компания GitHub — Microsoft. Когда сотрудник установил заражённую версию, вредонос получил доступ к устройству.</p><p>Дальше началась классическая цепочка бокового перемещения: компрометация устройства → извлечение секретов из репозитория, к которому был доступ у пользователя → доступ к внутренней инфраструктуре GitHub. Каждый шаг становился плацдармом для следующего.</p><p>Наиболее вероятным кандидатом исследователи называют троянизированную версию <b>Nx Console (nrwl.angular-console) v18.95.0</b>, опубликованную 19 мая 2026 года и провисевшую в маркетплейсе около 11 минут до удаления.</p><p>Проблема маркетплейса VS Code в этом контексте очевидна: разработчик, устанавливающий расширение от внешне легитимного издателя, фактически не имеет способа обнаружить инъекцию вредоноса без активного сканирования секретов или поведенческого мониторинга на уровне эндпоинта.</p><h2>Что похитили и что продают</h2><p>По заявлению TeamPCP на форуме Breached, группа получила доступ к «исходному коду GitHub и внутренним организациям». В объявлении о продаже указано: «Здесь около ~4 000 репозиториев приватного кода, я с удовольствием отправлю образцы заинтересованным покупателям».</p><p>GitHub подтвердил, что заявленный злоумышленниками масштаб «~3 800 репозиториев в целом согласуется» с данными внутреннего расследования — редкий случай, когда жертва сама верифицирует объём утечки. Это существенно повышает достоверность инцидента.</p><p>Что примечательно: TeamPCP не вымогает выкуп. Группа ищет единственного покупателя, после чего обещает уничтожить данные на своей стороне. Однако если покупатель не найдётся — группа <b>угрожает опубликовать данные бесплатно</b>. Дедлайна нет, но угроза утечки есть.</p><h2>Масштаб последствий</h2><p>GitHub — крупнейшая в мире платформа разработки: более 4 миллионов организаций (в том числе 90% компаний из Fortune 100), 180 миллионов разработчиков, 420 миллионов репозиториев. Внутренний исходный код платформы содержит архитектурные решения, реализации систем безопасности и дизайн внутренних API — то, к чему никто за пределами GitHub не должен был иметь доступа.</p><p>Данные клиентских репозиториев, корпоративных аккаунтов и организаций <b>не пострадали</b>. GitHub подтвердил отсутствие свидетельств утечки за пределы внутренних репозиториев компании.</p><h2>Кто такие TeamPCP / UNC6780</h2><p>TeamPCP, отслеживаемая Google Threat Intelligence Group как UNC6780, — финансово мотивированная группировка, специализирующаяся на атаках на цепочки поставок в экосистеме разработки. В 2026 году она успела скомпрометировать несколько крупных инструментов:</p><ul><li><b>Trivy</b> — популярный сканер уязвимостей (CVE-2026-33634): затронуто более 1 000 организаций, включая Cisco</li><li><b>KICS (от Checkmarx) и LiteLLM</b> — целевой сбор учётных данных из CI/CD-пайплайнов</li><li><b>durabletask</b> — официальный Python-клиент Microsoft для Durable Task: три вредоносные версии (1.4.1–1.4.3)</li><li><b>TanStack, MistralAI, Telnyx SDK</b> — серия атак на open-source зависимости через отравленные pull request</li></ul><p>Инструмент группы — само-реплицирующийся вредонос Mini Shai-Hulud, автоматизирующий атаки на цепочки поставок через кражу CI/CD-учётных данных. Он распространяется через компрометированные PyPI-пакеты и вредоносные pull request в популярные GitHub-репозитории.</p><h2>Как GitHub ответил на взлом</h2><p>Реакция была оперативной. В тот же день обнаружения компании выполнила ротацию критических ключей, изолировала заражённое устройство и удалила вредоносную версию расширения из маркетплейса Microsoft. Мониторинг инфраструктуры продолжается.</p><p>GitHub пообещал опубликовать подробный отчёт по завершении расследования и уведомить клиентов, если обнаружится влияние на их данные.</p><h2>Выводы</h2><blockquote>Платформа GitHub используется более чем 4 миллионами организаций, включая 90% компаний Fortune 100. Взлом внутреннего исходного кода GitHub несёт последствия, выходящие далеко за рамки самого инцидента: в руках злоумышленника оказываются архитектурные решения и реализации систем безопасности платформы.</blockquote><p>Этот инцидент наглядно показывает уязвимость поверхности атаки через экосистему расширений IDE. Маркетплейс VS Code содержит десятки тысяч расширений — злоумышленникам достаточно нескольких минут, чтобы загрузить отравлённую версию и поймать нужного разработчика.</p><p>Если вы или ваша команда используете VS Code на корпоративных устройствах: проверьте список установленных расширений, убедитесь, что secret scanning активен, и следите за официальными обновлениями от GitHub. Источник: <a href="https://www.secureblink.com/cyber-security-news/3-800-git-hub-repos-breached-via-poisoned-vs-code-extension-by-team-pcp">Secure Blink</a>, <a href="https://www.helpnetsecurity.com/2026/05/20/github-breached-teampcp/">Help Net Security</a>, <a href="https://hackread.com/github-breach-teampcp-repositories-vs-code-extension/">HackRead</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Создаю альтернативу Microsoft Store — независимый опенсорс-магазин приложений на Python без цензуры и малвари</title>
      <link>https://tproger.ru/articles/sozdayu-alternativu-microsoft-store-nezavisimyj-opensors-magaz</link>
      <comments>https://tproger.ru/articles/sozdayu-alternativu-microsoft-store-nezavisimyj-opensors-magaz?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[PXStudio]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/sozdayu-alternativu-microsoft-store-nezavisimyj-opensors-magaz</guid>
      <description><![CDATA[<p>Рассказ о создании FlowStore — быстрого независимого магазина приложений на Python с открытым кодом под GNU GPL v3. Зачем городить свой GUI поверх WinGet, как очистить популярный софт от бандлов Яндекса и почему этот проект защитит обычных пользователей от малвари и скрытых троянов-лоадеров</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/sozdayu-alternativu-microsoft-store-nezavisimyj-opensors-magaz">Создаю альтернативу Microsoft Store — независимый опенсорс-магазин приложений на Python без цензуры и малвари</a>»</p>]]></description>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 14 May 2026 12:51:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hello World!  Сегодня я хотел бы рассказать вам про свою альтернативу Microsoft Store. Этот проект называется FlowStore, его исходный код открыт на Codeberg под лицензией GNU GPL v3.</p><p>Мне очень важен ваш фидбек, а ссылку на скачивание я с радостью предоставлю в комментариях.</p><p>Расскажу, как мне пришла идея этого проекта. Изначально всё началось с того, что я заметил, как на многих сайтах (например, на том же SourceForge) крутые программы с открытым исходным кодом начали просто без причины удаляться. Мне захотелось создать собственный «мир программ», где можно найти абсолютно всё и ничего не будет удалено. Исключение — вредоносный софт: если в приложении обнаружатся вирусы, скрытые трояны или рекламные бандлы, я сразу отправляю его в бан. Во всех остальных случаях программа будет жить. Не знаю, зайдет вам или нет. Но надеюсь на лучшее :)</p>]]></content:encoded>
    </item>
    <item>
      <title>Атака на npm и PyPI: 404 вредоносные версии в TanStack, Mistral AI, UiPath за пять часов</title>
      <link>https://tproger.ru/news/ataka-na-npm-i-pypi-404-vredonosnye-versii-v-tanstack-mistral</link>
      <comments>https://tproger.ru/news/ataka-na-npm-i-pypi-404-vredonosnye-versii-v-tanstack-mistral?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/ataka-na-npm-i-pypi-404-vredonosnye-versii-v-tanstack-mistral</guid>
      <description><![CDATA[<p>11 мая 2026 опубликовано 404 вредоносные версии в 170+ npm-пакетах и 2 PyPI. Под ударом TanStack, Mistral, UiPath. Что делать прямо сейчас.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/ataka-na-npm-i-pypi-404-vredonosnye-versii-v-tanstack-mistral">Атака на npm и PyPI: 404 вредоносные версии в TanStack, Mistral AI, UiPath за пять часов</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 12 May 2026 12:11:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если у вас в зависимостях есть что-то из @tanstack/*, @mistralai/* или @uipath/* — проверьте lockfile прямо сейчас. В ночь с 11 на 12 мая 2026 года атакующий опубликовал 404 вредоносных версии в 170+ npm-пакетах и двух PyPI-пакетах за пятичасовое окно. Самое неприятное: payload самовоспроизводится, подкладывая .claude/settings.json и .vscode/tasks.json в репозитории жертв.</p><p>Это <a href="https://safedep.io/mass-npm-supply-chain-attack-tanstack-mistral/">одна из крупнейших координированных атак на реестры пакетов в 2026 году</a>, и первая, которая в одной кампании задела сразу и npm, и PyPI. StepSecurity и Socket трекают её как «mini-shai-hulud». Разбираемся, что произошло, кто пострадал и что делать.</p><ul><li>404 вредоносные версии в 170+ npm-пакетах и 2 PyPI-пакетах, опубликованы в течение пятичасового окна 11 мая.</li><li>Под удар попали: @tanstack (42 пакета роутера для React, Vue, Solid), @mistralai (3 SDK), @uipath (65 пакетов автоматизации), @opensearch-project/opensearch (1,3 млн загрузок в неделю), Guardrails AI.</li><li>Два механизма триггера в npm: preinstall-хук (@mistralai) и optionalDependency с prepare-скриптом (@tanstack). На PyPI — инжекция в __init__.py, срабатывает при импорте, а не при установке.</li><li>Payload крадёт GitHub-токены, npm-токены, AWS IAM-credentials и HashiCorp Vault-credentials. Эксфильтрация — через onion-routed мессенджер Session, поэтому C2-домен заблокировать нельзя.</li><li>Самораспространение: payload использует украденный GitHub-токен, чтобы закоммитить .claude/settings.json и .vscode/tasks.json в feature-ветки чужих репозиториев через GraphQL-мутацию createCommitOnBranch.</li><li>Безопасные версии: mistralai ≤ 2.4.5, guardrails-ai ≤ 0.10.0. По npm — пиньте версии и регенерируйте lockfile.</li></ul><h2>Что произошло</h2><p>В ночь с 11 на 12 мая 2026 года автоматическая система детекта SafeDep засекла массовый burst странных публикаций на npm. Атакующий действовал не точечно, как в случае с axios в марте 2026, а целыми scope-ами: внутри одного scope падали сразу все пакеты. К утру стали известны границы кампании — 170 npm-пакетов, 404 вредоносные версии, плюс 2 PyPI-пакета.</p><h3>Кого затронуло</h3><ul><li><b>TanStack</b> (42 пакета, 84 версии): целый экосистем роутера — @tanstack/react-router, @tanstack/vue-router, @tanstack/solid-router, их devtools, SSR-плагины и build-инструменты. У @tanstack/react-router — 3 миллиона загрузок в неделю.</li><li><b>Mistral AI</b> (3 пакета, 9 версий): core SDK @mistralai/mistralai и его обёртки под Azure и GCP. По три вредоносные версии на каждый.</li><li><b>UiPath</b> (65 пакетов, 65 версий): весь scope @uipath — SDK агентов, оркестратор, инструменты RPA, пакеты для интеграции.</li><li><b>OpenSearch</b> (@opensearch-project/opensearch): официальный JavaScript-клиент, 4 затронутые версии — 3.5.3, 3.6.2, 3.7.0, 3.8.0. 1,3 миллиона загрузок в неделю.</li><li><b>Guardrails AI</b> (guardrails-ai==0.10.1 на PyPI): фреймворк validation guardrails для Python.</li><li><b>Mistral AI на PyPI</b> (mistralai==2.4.6): официальный SDK; легитимного релиза 2.4.6 не существовало, последняя версия — 2.4.5 от 7 мая.</li></ul><h2>Как работает атака</h2><h3>На npm — два механизма триггера</h3><p>Для <b>Mistral AI</b> атакующий заменил блок scripts в package.json на preinstall-хук. Этот хук скачивает Bun runtime и запускает payload setup.mjs, который, в свою очередь, запускает основной обфусцированный бинарь router_init.js.</p><p>Для <b>TanStack</b> атакующий пошёл хитрее. В легитимный package.json добавлен optionalDependency, указывающий на вредоносный коммит в реальном репозитории tanstack/router на GitHub (коммит #79ac49eedf774dd4b0cfa308722bc463cfe5885c). В этом коммите лежит prepare-скрипт, который снова тянет Bun и запускает тот же payload.</p><p>Преимущество атакующего: коммит в публичном репозитории, который ссылается из optionalDependency, не вызывает алертов автоматических сканеров — внешне это легитимная зависимость.</p><h3>На PyPI — инжекция в __init__.py</h3><p>PyPI устроен иначе: sandboxed install через pip download и pip wheel не выполняет код пакета. Поэтому атакующий не стал прятать payload в install-хуке. Вместо этого в __init__.py добавили 15 строк, срабатывающих при первом import:</p><p>Никакой обфускации: URL, путь и команда выполнения — открытым текстом. Проверка sys.platform запускает dropper только на Linux. macOS- и Windows-сборки несут трояны, но dropper там не сработает. Cloudflare помечает домен git-tanstack.com как phishing-сайт.</p><h2>Что крадёт payload</h2><p>Внутри обфусцированного router_init.js — модульный credential stealer с дедикейтными провайдерами под разные источники секретов:</p><ul><li>GitHub PAT, OAuth и App-installation токены: паттерны ghp_*, gho_*, ghs_*.</li><li>GitHub Actions OIDC JWT — формата ghs_NNN_xxx.yyy.zzz.</li><li>npm publish tokens (npm_*).</li><li>AWS IAM credentials через зондирование metadata-эндпоинта 169.254.169.254/latest/meta-data/iam/security-credentials/.</li><li>HashiCorp Vault — пробу на localhost:8200.</li></ul><p>Сканер регулярок прогоняется по содержимому переменных окружения, файлам конфигурации и истории shell. Цель — максимизировать lateral movement из CI/CD-окружений и dev-машин в публичное облако.</p><h2>Самораспространение через Claude Code и VS Code</h2><p>Самая интересная и пугающая часть payload — self-replication механизм. Имея украденный GitHub-токен, payload использует GraphQL-мутацию createCommitOnBranch, чтобы закоммитить вредоносные файлы в репозитории жертвы:</p><ul><li>.claude/settings.json и .claude/setup.mjs — конфиг для Claude Code, запускающий payload при открытии проекта.</li><li>.vscode/tasks.json и .vscode/setup.mjs — то же самое для VS Code.</li><li>.claude/router_runtime.js — полная копия 2,2-мегабайтного payload, чтобы следующая стадия не зависела от внешнего хоста.</li></ul><p>Цели коммитов выбираются хитро: payload запрашивает до 50 веток через GraphQL и отфильтровывает main, master, develop и release (типичный набор). Коммиты идут в feature- и topic-ветки, где меньше шансов нарваться на ревью. В коммит-сообщении добавляется случайный Co-authored-by, чтобы выглядеть «коллективно».</p><p>Итог цепочки: любой разработчик, который git pull заражённую ветку и откроет проект в VS Code или Claude Code, запустит payload без явного действия. Затем его токены крадутся — и цикл повторяется.</p><h2>Эксфильтрация через Session — почему домен не закрыть</h2><p>Атакующий не использовал классический C2-домен. Вместо этого payload содержит полную реализацию клиента <a href="https://getsession.org/">Session</a> — onion-routed мессенджера на сети Oxen. Payload связывается с pre-pinned seed-нодами (seed1.getsession.org, seed2.getsession.org, seed3.getsession.org), получает список snode-ов и отправляет украденные креды через peer-to-peer-сеть.</p><p>Для больших данных payload использует filev2.getsession.org/file/ — централизованный файловый сервер Session. Это единственная фиксированная точка инфраструктуры; всё остальное — динамическое peer-to-peer-разрешение через swarm. Защитники не могут отозвать swarm так, как отзывают домен.</p><blockquote>Шифрование на ed25519 и x25519, перебор snode-адресов в рантайме — блокировка C2 на уровне DNS или firewall здесь не работает.</blockquote><h2>Что делать прямо сейчас</h2><h3>Для npm-проектов</h3><p>Проверьте lockfile на наличие вредоносных версий по затронутым scope-ам:</p><p>Если что-то найдено — запиньте пакеты на известные безопасные версии и перегенерируйте lockfile. После этого ротейтьте все секреты, к которым имела доступ ваша dev-машина или CI: GitHub PAT, npm-токены, AWS-ключи, Vault-токены.</p><h3>Для Python-проектов</h3><p>Проверьте установленные версии:</p><p>Если у вас mistralai==2.4.6 или guardrails-ai==0.10.1 — окружение скомпрометировано. Безопасные версии: mistralai ≤ 2.4.5 и guardrails-ai ≤ 0.10.0.</p><h3>Дополнительная гигиена</h3><ul><li>Проверьте feature- и topic-ветки своих репозиториев на свежие коммиты с подложенными файлами в .claude/ и .vscode/ — особенно если у вас под рукой Claude Code или VS Code с активным GitHub-токеном.</li><li>Запретите автоматический запуск preinstall- и postinstall-скриптов через флаг --ignore-scripts.</li><li>Просканируйте репозитории на наличие .claude/router_runtime.js — это копия payload, размер около 2,2 МБ.</li></ul><h2>Indicators of Compromise (IoC)</h2><h2>FAQ</h2><h2>Выводы</h2><p>Эта атака подсветила два архитектурных риска, которые мы коллективно проигнорировали. Первый — preinstall/postinstall-скрипты в npm по дефолту запускаются от лица пользователя; одно зависимое дерево из 100 пакетов даёт атакующему 100 шансов запуститься на dev-машине. Второй — IDE-конфиги в репозиториях. Удобство «открыл проект и сразу работает» оборачивается атакующим прямо тем же удобством: пушнул .claude/setup.mjs в feature-ветку — и payload запускается у каждого, кто склонирует ветку.</p><p>Защита здесь — это --ignore-scripts в npm, изолированные среды для install (контейнеры, devcontainers), review-обязательность для всех веток, отслеживание <i>незапланированных</i> .claude/- и .vscode/-файлов в pull-requests.</p><p>Полный технический разбор с дизассемблированным payload, swarm-логикой и appendix-ом со всеми пакетами — <a href="https://safedep.io/mass-npm-supply-chain-attack-tanstack-mistral/">в посте SafeDep</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Шесть способов сделать flatten в Python — какой быстрее в 500 раз</title>
      <link>https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra</link>
      <comments>https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra</guid>
      <description><![CDATA[<p>Разворачиваем вложенный список в Python: for + extend, list comprehension, itertools.chain, reduce, sum, NumPy. Замер и почему sum() — анти-паттерн.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/west-sposobov-sdelat-flatten-v-python-kakoj-bystree-v-500-ra">Шесть способов сделать flatten в Python — какой быстрее в 500 раз</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 12 May 2026 11:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если в коде встречается список списков и нужно превратить его в один плоский, на ум приходит как минимум пять подходов — от обычного for до itertools.chain и NumPy. Какие из них работают за миллисекунды, а какие — за секунды? Перевод и адаптация туториала Real Python с разбором всех способов и замером производительности.</p><p>Flatten — операция приведения вложенной структуры к одномерной. Самый распространённый кейс: матрица представлена как список списков, а вам нужен плоский список всех значений, чтобы передать в алгоритм или ML-модель. В отличие от других языков, в Python нет встроенного .flatten() у списков — поэтому стандартное «как это сделать?» имеет несколько ответов разной степени читаемости и эффективности.</p><p>Пусть у нас есть матрица 4×4:</p><p>Цель — получить:</p><ul><li>Быстрее всего работает обычный цикл с in-place мутацией: += или .extend(). Время — около 5–7 мс.</li><li>itertools.chain() — около 16 мс, но в 3 раза экономнее по памяти; подходит, когда финальный список не нужен полностью.</li><li>List comprehension — около 24 мс. Читаемо и без побочных эффектов, но создаёт промежуточные объекты.</li><li>functools.reduce() + lambda и sum() — от 2,6 секунд. Это в 400–500 раз медленнее за счёт постоянного создания новых промежуточных списков.</li><li>Для произвольной вложенности — рекурсия или итеративный обход со стеком.</li><li>В data-science для NumPy-массивов используйте np.ndarray.flatten() — он быстрее любого варианта на чистых списках.</li></ul><h2>Способ 1. Цикл for + .extend()</h2><p>Самый прямолинейный и читаемый вариант: проходим по каждой подсписку и добавляем её элементы к итоговому списку через .extend().</p><p>.extend() принимает любой iterable и поэлементно добавляет его в конец списка. Альтернативно можно использовать augmented-concatenation +=:</p><p>Обе функции делают одно и то же. Real Python отмечает: flatten_extend читается чуть лучше, потому что метод named — намекает на смысл операции. По скорости варианты идут в первой тройке.</p><h2>Способ 2. List comprehension</h2><p>Pythonic-альтернатива однострочнику. Двойной for внутри comprehension перебирает сначала строки, потом элементы строк:</p><p>Подход компактнее цикла и не плодит промежуточных переменных. Скорость — заметно медленнее .extend() из-за двух вложенных циклов на уровне comprehension, но всё ещё в диапазоне десятков миллисекунд.</p><h2>Способ 3. itertools.chain() — экономно по памяти</h2><p>chain() возвращает итератор, а не список. Это значит, что вы можете обходить элементы потокового, не материализуя весь массив в памяти — полезно, когда матрица большая и хранить полный flat-список нерационально.</p><p>На большой матрице chain.from_iterable примерно в три раза медленнее цикла .extend(): накладные расходы на материализацию через list(). Но если итоговый список как таковой не нужен — можно отдать сам итератор потребителю и сэкономить и время, и память.</p><h2>Способ 4. functools.reduce() и sum() — почему так медленно</h2><p>Эти варианты часто появляются в туториалах для красоты, но на практике плохо масштабируются. Причина одна: каждая итерация создаёт новый промежуточный список вместо мутации существующего.</p><p>iconcat мутирует аккумулятор и держится в топ-3 по скорости. А вот вариант с lambda и оператором + на каждом шаге копирует обе стороны — отсюда и взрывной рост времени на больших данных.</p><p>С sum() ситуация ровно такая же:</p><p>sum() работает на любых сложениях, но для списков это О(n²) по аллокациям. На матрице 1000×1000 он в ~500 раз медленнее цикла с .extend().</p><h2>Способ 5. Произвольная вложенность — рекурсия и стек</h2><p>Все предыдущие способы предполагают, что вложенность ровно одна. Если структура произвольно глубокая или неоднородная, нужен другой подход:</p><p>Рекурсия удобна, пока глубина не упирается в sys.getrecursionlimit() (по умолчанию 1000). Для очень глубоких структур стоит переписать через итеративный обход со стеком:</p><h2>Способ 6. NumPy для data-science</h2><p>Если вы работаете с матрицами в data-science, у numpy.ndarray есть готовый метод .flatten(). Он быстрее любого варианта на чистых Python-списках, потому что под капотом работает с непрерывным блоком памяти.</p><h2>Замер производительности</h2><p>Real Python прогнали все варианты на матрице 1000×1000 через timeit. Результаты, отсортированные по времени:</p><p>Между первыми тремя и последними четырьмя — пропасть в 400–500 раз. Причина: топовые функции мутируют существующий список, а reduce(+), reduce(operator.add), reduce(lambda x, y: x + y) и sum() на каждой итерации создают копии.</p><h2>FAQ</h2><h2>Выводы</h2><p>Если вам нужен один-в-один поведенческий рецепт: for row in matrix: flat.extend(row). Этот код понятен любому, кто знает Python, и упирается в производительность только когда матрица переваливает за миллионы элементов. Если объёмы такие, что нужна потоковая обработка — переключитесь на itertools.chain. Если данные numerical — используйте NumPy.</p><p>Главное, чего стоит избегать — sum(matrix, []) и reduce с + или lambda. Они смотрятся изящно в туториалах, но превращают O(n) в O(n²) и ставят вас в первую же боттлнек-историю на проде.</p><p>Оригинальный туториал с пошаговыми объяснениями и сравнением — <a href="https://realpython.com/python-flatten-list/">на Real Python</a>, автор — Leodanis Pozo Ramos.</p>]]></content:encoded>
    </item>
    <item>
      <title>Denwer SE: Возрождение легендарного локального веб-сервера на современном стеке</title>
      <link>https://tproger.ru/articles/denwer-se-vozrozhdenie-legendarnogo-lokalnogo-veb-servera-na-so</link>
      <comments>https://tproger.ru/articles/denwer-se-vozrozhdenie-legendarnogo-lokalnogo-veb-servera-na-so?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Александр Тишов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/denwer-se-vozrozhdenie-legendarnogo-lokalnogo-veb-servera-na-so</guid>
      <description><![CDATA[<p>Помните диск Z:, иконку джентльмена и магию Run.exe? Денвер вернулся. Denwer SE: Python вместо Perl, HTTPS без красных экранов, свежий PHP и портативность. И да, он всё ещё помещается на флешку.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/denwer-se-vozrozhdenie-legendarnogo-lokalnogo-veb-servera-na-so">Denwer SE: Возрождение легендарного локального веб-сервера на современном стеке</a>»</p>]]></description>
      <category><![CDATA[C++]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Браузеры]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Для продвинутых]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[CMS]]></category>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 05 May 2026 10:11:34 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы начинали веб-разработку в середине 2000-х, то наверняка помните Denwer — «джентльменский набор веб-разработчика». Иконка в виде человека в шляпе, виртуальный диск Z:, папка <i>home/localhost/www</i> — всё это было ритуалом, который упрощал жизнь тысячам разработчиков. Но оригинальный Denwer безнадёжно устарел: Perl-скрипты, 32-битные сборки, поддержка только древних версий PHP и MySQL. Ему на смену пришли громоздкие комбайны вроде Open Server или сложные для новичков Docker-контейнеры.</p><p>Однако недавно проект получил второе дыхание. Разработчик Александр Тишов (Amro) — создатель <a href="https://seditio.org" rel="nofollow">CMS Seditio</a> и основатель веб-студии <a href="https://avego.org" rel="nofollow">«Авего»</a>  — выпустил <a href="https://seditio.org/dev/denwer-se-lokalnyj-veb-stek-dlya-windows-apache-php-mysql-mariadb" rel="nofollow">Denwer SE (Second Edition)</a>. Это не просто обновление, а полный реинжиниринг с сохранением классической философии: портативность, скорость работы и привычная структура каталогов.</p><p>В этой статье разберём, что изменилось под капотом, почему панель управления переехала с Perl на Python, как работает автоматический HTTPS с собственным корневым сертификатом и зачем нужен зоопарк версий PHP от 5.6 до 8.5.</p><h2>Краткий экскурс: от Denwer 3 до Denwer SE</h2><p>Оригинальный Denwer (сокращение от Джентельменский Набор Веб-разработчика) появился в начале 2000-х. Он представлял собой связку Apache + PHP + MySQL, упакованную в самораспаковывающийся архив. Главные фишки:</p><ul><li>Виртуальный диск (по умолчанию Z:), который монтировался через subst.</li><li>Автоматическое создание виртуальных хостов по именам папок в home.</li><li>Консольные exe-файлы (Run, Stop, Restart) без графического окна.</li></ul><p>Проблемы оригинала:</p><ul><li>Управление на Perl — медленно, тяжело поддерживать в Windows.</li><li>Только 32-битные компоненты.</li><li>Невозможно быстро переключать версии PHP или БД.</li><li>Отсутствие нормального HTTPS (только самоподписанные сертификаты с ошибками в браузере).</li><li>Поддержка прекратилась в 2016 году.</li></ul><p>Denwer SE решает все эти проблемы, оставаясь при этом таким же портативным — достаточно скопировать папку на флешку или в облачный каталог.</p><h2>Архитектура: Python вместо Perl</h2><p>Denwer SE — панель управления написана на Python и скомпилирована в один EXE-файл (через PyInstaller).</p><p>Внутри служебной папки <b>denwer\</b> лежат:</p><ul><li>DLL-версия Python — интерпретатор, который использует основной исполняемый файл.</li><li>Скомпилированные модули .pyd — в том числе GUI на базе Tcl/Tk для оконного интерфейса и системного трея.</li><li>Минимальный набор библиотек для управления службами, правки hosts и генерации сертификатов.</li></ul><p>Это даёт несколько преимуществ:</p><ul><li>Портативность — панель ищет соседние папки home и usr, поэтому каталог со стеком можно переносить куда угодно без переустановки.</li><li>Скорость — Python-скрипты запускаются быстрее, чем Perl, особенно на холодном старте.</li><li>Читаемость кода — разработчику проще поддерживать и расширять функционал.</li></ul><h2>Полный переход на x64</h2><p>Оригинальный Denwer навсегда остался 32-битным, что в современных реалиях просто неприемлемо. Denwer SE собирается исключительно под x64:</p><ul><li>Apache (версия 2.4.x) — 64-битный.</li><li>Все модули PHP (от 5.6 до 8.5) — Thread Safe x64.</li><li>MySQL / MariaDB — 64-битные сборки.</li></ul><p>Системные требования — Windows 7/8/10/11 (x64). Для работы компонентов потребуются Microsoft Visual C++ Redistributable (VC11, VC12, VC14, VC15). Разработчик положил установщики этих пакетов в папку <b>vcredist\</b> — при необходимости можно доустановить вручную.</p><h2>Структура каталогов: преемственность и гибкость</h2><p>Denwer SE сохранил классическую структуру, чтобы старые пользователи не ломали голову:</p><p>Главный конфиг — usr\configuration.txt. В нём задаются пути без жёсткой привязки к букве диска, например:</p><p>При старте панель монтирует виртуальный диск (по умолчанию Z:) и динамически подставляет путь через переменную <b>subst_drive</b>.</p><h2>Управление версиями PHP и БД без танцев с бубном</h2><p>В Denwer SE встроен менеджер версий. Вы просто выбираете из выпадающего списка нужную версию PHP (например, 8.3 или 5.6) — панель сама правит конфигурацию Apache.</p><p>Как это работает под капотом:</p><p>В папке usr/local/apache/php лежат подкаталоги php5.6, php7.4, php8.3 и т.д..</p><p>В каждом из них есть файл php-denwer.conf— шаблон для подключения модуля к Apache. При выборе версии этот файл копируется в <b>conf/extra/httpd-denwer.conf</b>, который затем включается в основной httpd.conf.</p><p>Если вы хотите добавить свою сборку PHP (например, PHP 8.4-rc), достаточно:</p><ul><li>Распаковать x64 Thread Safe версию в отдельный каталог внутри php\.</li><li>Создать php-denwer.conf по образцу.</li><li>Убедиться, что все DLL от VC++ установлены.</li></ul><p>Аналогично для баз данных: переключение между MySQL 5.7 и MariaDB 11.8 происходит через тот же интерфейс. В каталоге СУБД может лежать файл <b>db-denwer.conf</b>, который при старте копируется в <b>my.ini</b>.</p><h2>HTTPS, который не бесит: локальный Root CA</h2><p>Самое болезненное место при локальной разработке это самоподписанные сертификаты. Браузеры постоянно ругаются, приходится кликать «Принять риск». Для командной разработки это вообще катастрофа: каждый участник должен сгенерировать свой сертификат и добавить в исключения.</p><p>Denwer SE решает проблему элегантно — он создаёт собственный корневой центр сертификации (CA) и подписывает им сертификаты для всех ваших локальных доменов.</p><p>Как это работает:</p><ol><li>При первом запуске (если найден OpenSSL) панель генерирует ключи denwer-ca.key и сертификат denwer-ca.crt в папку usr/local/apache/conf/cert/denwer-ca/.</li><li>Для каждого виртуального хоста (папки в home/) автоматически создаётся сертификат в conf/cert/&lt;domain&gt;/.</li><li>Все сертификаты хостов подписаны локальным CA.</li></ol><p>Чтобы браузер доверял им, нужно один раз установить <b>denwer-ca.crt</b> в хранилище «Доверенные корневые центры сертификации» Windows. Для этого в панели есть специальная кнопка (требует прав администратора).</p><p>После этого любые HTTPS-запросы к локальным хостам работают без единого предупреждения.</p><h2>Удобства для разработчика (DX)</h2><p>В версии 1.2.4 добавили несколько фич, которые экономят время каждый день:</p><ul><li>Лог с таймштампами — каждая строка в окне панели имеет префикс [чч:мм:сс]. Теперь видно, сколько секунд сервер поднимается и где возможны задержки.</li><li>Прямой доступ к php.ini и my.cnf — рядом со списками версий появились кнопки, открывающие конфигурацию именно активной версии.</li><li>Автоматическое ведение hosts — панель в реальном времени сканирует home/, находит новые домены и прописывает их в C:\Windows\System32\drivers\etc\hosts. Журнал добавляемых записей сохраняется в usr\AddedHosts.txt. При остановке стека лишние строки удаляются.</li><li>Для смены версии PHP или базы данных панель требует полной остановки всех служб. Вы нажимаете «Стоп», меняете версию в списке, затем «Старт» — и стек поднимается уже с новыми настройками. Автоматический перезапуск без вашего участия работает только для Apache: когда вы добавляете новый домен в папку home/, панель сама переписывает vhosts.conf и перезапускает веб-сервер, не трогая БД.</li></ul><h2>Почему не Open Server или Docker?</h2><p>Этот вопрос закономерно возникает у всех, кто видит очередной локальный веб-сервер. Ведь есть уже давно Open Server Panel, Laragon, XAMPP, а для продвинутых — Docker. Зачем ещё один?</p><p><b>Open Server</b> — мощный и удобный комбайн с десятками версий PHP и настройками «на века». Но он разворачивается в системе не портативно: создаёт папки в ProgramData, пишет в реестр, а запуск может занимать 5–10 секунд. Denwer SE, напротив, полностью переносим: скопировал папку на флешку или в облачный каталог — и всё работает. Запуск стека — буквально 1–2 секунды, что критично, когда вы десятки раз за день перезапускаете сервер для тестов.</p><p><b>Docker</b> — индустриальный стандарт для изоляции и воспроизводимости окружений. Но для локальной разработки простого сайта он часто избыточен. Вам нужно разобраться в образах, контейнерах, пробросе портов, volume’ах и docker-compose.yml. А в Denwer SE вы просто создали папку в home/ — и готово. Никакой работы с командной строкой, никакого потребления гигабайт ОЗУ на фоновую службу Docker Desktop.</p><p><b>Laragon</b> — быстрый, портативный, поддерживает не только PHP, но и Node.js, Python, Go. Но он ориентирован на современные фреймворки, особенно Laravel. Denwer SE же сделан для тех, кто вырос на классическом Денвере: виртуальный диск Z:, папка home/имя_домена/www, минимум настроек. Не нужно переучиваться — просто распаковал и работаешь как 10 лет назад, но с новыми версиями PHP и HTTPS.</p><h2>Как начать пользоваться Denwer SE</h2><ol><li>Скачать архив с <a href="https://seditio.org/dev/denwer-se-lokalnyj-veb-stek-dlya-windows-apache-php-mysql-mariadb" rel="nofollow">официального сайта автора</a>.</li><li>Распаковать в любое место, например C:\web\DenwerSE\.</li><li>Запустить DenwerSE.exe — если нет прав администратора, попросит их для монтирования диска и правки hosts.</li><li>Нажать «Запустить» — появится виртуальный диск Z:, а в системном трее иконка.</li><li>Создать папку сайта — например, home\myproject.local и положить туда index.php.</li><li>Открыть в браузере http://myproject.local/ (или https://myproject.local/). HTTPS будет работать сразу после установки корневого сертификата (кнопка в панели).</li></ol><p>По умолчанию пароль к MySQL/MariaDB — пустая строка (пользователь <b>root</b>). При желании его можно сменить через phpMyAdmin.</p><h2>Заключение</h2><p>Denwer SE — это не просто ностальгический проект. Это действительно современный инструмент, который доказывает, что концепция «локального сервера в одну папку» всё ещё актуальна. Отказ от Perl в пользу Python, менеджер версий PHP/БД, нормальный HTTPS, портативность и мгновенный запуск — всё это делает его отличным выбором для быстрого прототипирования, тестирования легаси-кода или обучения веб-разработке.</p><p>Если вы устали ждать, пока Open Server применит настройки, или не хотите разбираться в Docker Compose — попробуйте <b>Denwer SE</b>. Вероятно, он напомнит вам старые добрые времена, но уже без боли устаревших технологий.</p><ul><li>Автор проекта: Александр Тишов
	(Amro), разработчик CMS Seditio.</li><li>Лицензия: Freeware.</li><li>Совместимость: Windows 7/8/10/11 x64.</li></ul><p>Исходники панели управления не открыты (распространяется скомпилированный EXE), но архитектура и конфиги полностью прозрачны. В планах — добавить поддержку Nginx в качестве альтернативы. Следите за обновлениями.</p>]]></content:encoded>
    </item>
    <item>
      <title>ИТ-аутстаффинг: когда он выгоднее найма в штат</title>
      <link>https://tproger.ru/articles/it-autstaffing-kogda-on-vygodnee-najma-v-wtat</link>
      <comments>https://tproger.ru/articles/it-autstaffing-kogda-on-vygodnee-najma-v-wtat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Андрей Бойко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/it-autstaffing-kogda-on-vygodnee-najma-v-wtat</guid>
      <description><![CDATA[<p>Разбираем, чем ИТ-аутстаффинг отличается от штатного найма: скорость, затраты, риски. Когда провайдер выгоднее — и когда нет.
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/it-autstaffing-kogda-on-vygodnee-najma-v-wtat">ИТ-аутстаффинг: когда он выгоднее найма в штат</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Мобильная разработка]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Фронтенд-разработка с нуля]]></category>
      <category><![CDATA[Фулстек-разработка: полный цикл]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 05 May 2026 05:44:09 GMT</pubDate>
      <content:encoded><![CDATA[<p>ИТ-аутстаффинг — это модель найма, при которой специалист работает на заказчика и отвечает перед его менеджерами, но официально числится в штате другой компании — провайдера. Последний при этом берёт на себя кадровую часть: трудовой договор, налоги, страховые взносы, документооборот.</p><p>Почему эта схема вообще появилась? Из-за дефицита кадров в ИТ, который давно превратился в постоянный фон для компаний, занимающихся цифровыми продуктами. Найти миддла или синьора занимает два-три месяца, а в узких технологических стеках — и того дольше. Пока идёт поиск, проекты стоят или перегружают команду. И тогда возникает спрос на альтернативные способы работы с ИТ-персоналом.</p><p>Разберём, как устроен аутстаффинг, чем он отличается от аутсорсинга ИТ-специалистов и при каких задачах находить кадры через провайдера  – хорошее решение.</p><h2>ИТ-аутстаффинг и штат: в чем разница</h2><p>Штатный найм — это прямые трудовые отношения. Работодатель оформляет сотрудника к себе и ответственен за налоги, взносы, отпуска, больничные и кадровое сопровождение. Специалист становится частью корпоративной структуры и, как правило, видит своё дальнейшее развитие внутри этой компании. Это привычная схема для обеих сторон.</p><p>При ИТ-аутстаффинге компания-провайдер трудоустраивает специалиста у себя и передаёт его заказчику — на срок или под проект. Схема иначе выстраивается юридически, но с точки зрения ежедневной работы разницы нет. Заказчик ставит задачи и требует результатов. Провайдер отвечает за зарплату, кадровый учёт и трудовые гарантии.</p><p>Отличия двух моделей — в таблице:</p><figure><img src="https://media.tproger.ru/user-uploads/115144/2026-04-29/47cc0077-fd40-4ec5-93ea-f60334dd6354.webp" alt="" /><figcaption>Отличия двух моделей</figcaption></figure><p>Ещё один вопрос, который нередко возникает: чем аутстаффинг отличается от аутсорсинга. При аутсорсинге заказчик отдаёт задачу внешней команде и принимает результат — без погружения в то, как команда работает изнутри. При аутстаффинге он получает конкретного человека и управляет им сам. Это разные форматы с разной логикой применения: первый — про готовый результат, второй — про ресурсы.</p><p>А как вообще контролировать человека, которого не нанимал? Да ровно так же — через задачи, метрики, совместные инструменты. Трудовой договор тут ни при чём. Меняется только то, кто его подписывает и кто несёт кадровые обязательства.</p><h2>Преимущества аутстаффинга разработчиков</h2><p>Классический рекрутинг никуда не уходит, но иногда аутстаффинг гораздо эффективнее. Итак, каковы же преимущества этой формы найма?</p><h2>Аутстаффинг в ИТ: быстрое масштабирование без кадровой нагрузки</h2><p>Найти сильного специалиста в команду в среднем занимает два-три месяца — и это при активном поиске. Часто бывает так, что людей в команде не хватает, потому что проект стремительно растет. Запуск нескольких параллельных процессов поиска кадров рискует затянуться. И тогда бизнес обращается к провайдеру, чтобы добрать нужное количество специалистов. Провайдер предлагает кандидатов, имеющих релевантный опыт в конкретном технологическом стеке, уже через одну-две недели, иногда – быстрее. Разница ощутима, особенно когда дата запуска проекта уже стоит в календаре.</p><p>Особенно это важно, когда приходится одновременно масштабировать несколько направлений. Привлечь четверых-пятерых специалистов через провайдера — это один договор и один процесс согласования, а не пять отдельных рекрутинговых потоков с непредсказуемыми сроками.</p><h2>Экономит бюджет и снижает операционные затраты</h2><p>Содержание штатного сотрудника — это не только зарплата. К ней добавляются расходы на рекрутинг (в среднем одна-три месячные выплаты), обустройство рабочего места, обучение, страховые взносы, налоги и кадровое администрирование. При аутстаффинге эти статьи уходят к провайдеру — заказчик платит прозрачную ставку по договору.</p><p>По завершении проекта не нужно думать о выходных пособиях или процедурах сокращения. Вопрос закрывается условиями договора с провайдером. При сравнимой квалификации специалиста совокупные расходы на аутстаффинге нередко оказываются ниже, чем на штатного сотрудника — если считать в полном объёме.</p><h2>Дает доступ к широкому пулу технических компетенций</h2><p>Провайдеры, предоставляющие аутстаф разработки, держат в базе специалистов самых разных профилей — серверные разработчики, системные</p><p>архитекторы, тестировщики, технические аналитики. Для заказчика это означает возможность подобрать человека с нужным стеком без ограничений локального рынка.</p><p>Именно поэтому аутстафф программистов особенно востребован у компаний, работающих с редкими или нестандартными технологиями. Найти такого специалиста «в штат» часто сложнее и дороже, чем привлечь через провайдера с обновляемой базой кандидатов — особенно если речь о нишевых стеках с ограниченным предложением на рынке труда.</p><h2>Специалисты с разнообразными опытом</h2><p>Разработчики на аутстаффинге, как правило, обладают более широким техническим кругозором, чем их штатные коллеги: они успели поработать в разных командах, с разными стеками и типами задач. Это даёт им гибкость мышления и насмотренность, которую сложно получить, годами работая в одном продукте.</p><p>Кроме того, проектный формат держит таких специалистов в тонусе: нет возможности погрязнуть в рутине.</p><h2>Снижение административной нагрузки</h2><p>Прежде чем новый штатный разработчик напишет первую задачу в трекере, кадровый отдел потратит время на оформление, бухгалтер — на налоговый учёт, юрист — проверит трудовой договор. Это нормально для штатного найма — но это реальные часы и ресурсы. При аутстаффинге вся эта работа остаётся за провайдером.</p><p>Для компаний, которые одновременно закрывают несколько направлений, это особенно ощутимо. Пятеро специалистов через провайдера — это пять ставок в одном договоре. Пятеро штатных — это пять кадровых дел, пять налоговых расчётов, пять пакетов документов. Разница в операционной нагрузке очевидна.</p><h2>Когда аутстаффинг выгоднее штатного найма</h2><p>Модель аутстаффинга в ИТ работает лучше всего в нескольких конкретных ситуациях — давайте суммируем, когда лучше всего задуматься именно об этом варианте найма.</p><ul><li>Проектная работа с ограниченным горизонтом. Нанимать разработчика в штат на полгода-год — нецелесообразно: по завершении проекта появляются расходы на сокращение или переобучение. Аутстаффинг закрывает задачу без лишних обязательств на выходе.</li><li>Срочный рост команды. Продукт запускается через три месяца, а нужны ещё три специалиста прямо сейчас. Классический рекрутинг не успеет. Провайдер может дать команду параллельно, без разрыва в темпе разработки.</li></ul><p>Нишевые технологии и редкие стеки. Когда нужен человек с узкой экспертизой, которых на локальном рынке единицы — провайдер с</p><ul><li>широкой базой найдёт быстрее и, скорее всего, дешевле, чем собственный подбор.</li><li>Тест гипотезы без обязательств. Компания запускает новое направление, но не уверена в его горизонте. Аутстаффинг позволяет собрать команду, быстро оценить гипотезу и выйти без кадровых последствий, если направление не пошло.</li></ul><p>У модели есть и ограничения, которые важно учитывать. Главный риск — зависимость от провайдера: если партнёрство прерывается досрочно, специалист уходит вместе с накопленным знанием о проекте. Для критически важных функций это серьёзно. Поэтому разумный подход — смешанный: ключевые роли закрывать штатом, а аутстаффинг подключать для расширения команды под конкретные задачи с понятным сроком.</p><h2>Штатный найм: когда он по-прежнему эффективен</h2><p>Задач, с которыми штатный найм справляется лучше, немало. Особенно это заметно, когда продукт живёт годами и ключевые решения принимаются людьми, которые понимают его историю. Посмотрим на преимущества найма.</p><ul><li>Погружение в продукт. Человек, который работает с одним продуктом год или два, знает его глубже, чем тот, кого привлекли на полгода. Этот контекст нельзя передать через документацию или онбординг — он накапливается постепенно, через участие во всех стадиях и решениях.</li><li>Лояльность. Штатный сотрудник думает о своей карьере внутри компании, заинтересован в её развитии. Это сложно воспроизвести в модели временного сотрудничества — здесь у специалиста другие приоритеты.</li><li>Корпоративная память. Внутренние специалисты накапливают базу знаний, обучают новых коллег, передают экспертизу. При аутстаффинге существует риск, что с окончанием контракта вместе со специалистом уйдут и знания о проекте — если не выстроить процесс передачи.</li><li>Стабильность команды. Долгие проекты требуют предсказуемого состава: меньше перестановок, меньше потерь контекста. Штатный найм даёт более высокую вероятность сохранить команду на горизонте нескольких лет.</li></ul><p>Штатная модель незаменима там, где важны стратегическая непрерывность и инженерная культура: развитие ключевого продукта, техническая архитектура, формирование внутренней экспертизы. Всё это требует людей, которые связывают своё развитие с компанией, — аутстаффинг эту связь не создаёт. При выборе формата стоит честно оценить горизонт задач и то, насколько важна долгосрочная вовлечённость специалиста.</p><h2>Не вместо, а вместе: гибкие форматы и штатный найм техкадров</h2><p>Аутстаффинг не вытеснит штатный найм, так как решает иные задачи. Когда нужны скорость, гибкость, доступ к экспертизе без долгосрочных обязательств — он работает. Когда важны глубина погружения, лояльность и накопление знаний внутри команды — штат не заменить.</p><p>Компании, прошедшие через опыт масштабирования технической команды, как правило, не задаются вопросом «аутстаффинг или штат» — они комбинируют. Устойчивое ядро в штате, расширение под конкретные задачи — через провайдера.</p><p>Подробнее о том, как организована работа с внешними техническими кадрами, — в профильных материалах компаний, например, в разделе <a href="https://selecty.ru/it-outsourcing" rel="follow">ИТ-аутсорсинг</a> на сайте Selecty, где описаны конкретные форматы взаимодействия заказчика с провайдером.</p><p>Гибкие форматы работы с техническими кадрами сегодня — уже не просто тренд, а устойчивая норма рынка. Компании, которые умеют подбирать инструмент под задачу, получают ощутимое операционное преимущество. ИТ-аутстаффинг при правильном применении — один из таких инструментов.</p><p>Реклама. ООО Селекти, ИНН 7736313541, erid: 2W5zFJYUtvD</p>]]></content:encoded>
    </item>
    <item>
      <title>Python в апреле: Packaging Council, GC-реверт, Astral в OpenAI</title>
      <link>https://tproger.ru/news/python-v-aprele-sovet-po-upakovke-revert-gc-i-astral-pod-opena</link>
      <comments>https://tproger.ru/news/python-v-aprele-sovet-po-upakovke-revert-gc-i-astral-pod-opena?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/python-v-aprele-sovet-po-upakovke-revert-gc-i-astral-pod-opena</guid>
      <description><![CDATA[<p>Главные события Python в апреле 2026: PEP 772 принят, появился Packaging Council. Инкрементальный GC откатывают в 3.14.5. OpenAI приобрёл Astral. Разбираем главное.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/python-v-aprele-sovet-po-upakovke-revert-gc-i-astral-pod-opena">Python в апреле: Packaging Council, GC-реверт, Astral в OpenAI</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 04 May 2026 17:18:24 GMT</pubDate>
      <content:encoded><![CDATA[<p>Прошедший месяц в Python-экосистеме оказался редким по плотности институциональных изменений. Python Steering Council 16 апреля принял <a href="https://peps.python.org/pep-0772/">PEP 772</a> — у языка впервые появился отдельный избранный совет по упаковке: пять человек с тем же уровнем полномочий, что и сам Steering Council, но узкоспециализированных. В тот же день core-команда откатила инкрементальный сборщик мусора, который ввели в Python 3.14, — продакшен показал рост памяти до 5 раз. А компания Astral со своими uv и Ruff перешла под крыло OpenAI: 126 миллионов скачиваний uv в месяц теперь под чужим контролем.</p><p>Разбираем главные события апреля 2026 — что меняется для тех, кто пишет на Python, мейнтейнит библиотеки или поддерживает прод-инфраструктуру.</p><p><b>PEP 772 принят.</b> Python получил избранный Packaging Council из пяти человек с двухлетними сроками — теперь у решений про pip, setuptools и PyPI есть формальный орган, не зависящий от PyPA.</p><p><b>Инкрементальный GC откатывают.</b> В 3.14.5 и 3.15 вернётся старый поколенческий сборщик. Память на проде росла до 5×, а паузы 26 мс vs 1,3 мс этого не оправдывали.</p><p><b>OpenAI приобрёл Astral.</b> Создатели uv, Ruff и type-checker ty теперь под OpenAI. Лицензии открытые — форк остаётся страховочным механизмом.</p><p><b>PEP 803 принят.</b> Появится abi3t — стабильный ABI для free-threaded билдов, цель Python 3.15. Решает «wheel-explosion» для Cryptography, SciPy, Pydantic.</p><p><b>Python 3.15.0 beta 1.</b> Первая бета и feature-freeze назначены на 5 мая. После этой даты в 3.15 уже не попадут новые PEP-ы.</p><h2>PEP 772: у Python появился Packaging Council</h2><p>16 апреля Python Software Foundation и Steering Council одобрили <a href="https://peps.python.org/pep-0772/">PEP 772</a> — учреждение Packaging Council из пяти человек, выбираемых голосующими членами PSF. Полномочия совета сравнимы со Steering Council, но узкоспециализированы: стандарты упаковки, инструменты вроде pip и setuptools, эксплуатация PyPI.</p><p>До этого момента Python Packaging Authority (PyPA) координировала упаковочные решения неформально, через делегирование от Steering Council по PEP 609. На практике это значило, что любое крупное изменение в pip или PyPI требовало согласования через несколько групп без чёткого мандата. Совет получает прямые полномочия и работает на двухлетних сроках с шахматным переизбранием — две и три позиции ротируются в разных циклах, чтобы сохранять преемственность.</p><p>Главное последствие — упаковочные вопросы перестают зависать в комитете. По формулировке PEP, решения принимаются консенсусом, как и в Steering Council, но при необходимости совет может действовать голосованием. На повестке у нового органа уже стоят темы вроде упаковки в эпоху LLM-зависимостей и стандартизации lockfile-формата.</p><h2>Реверт инкрементального GC в Python 3.14.5</h2><p>В тот же день, 16 апреля, релиз-менеджер Hugo van Kemenade предложил откатить инкрементальный сборщик мусора, дебютировавший в Python 3.14, и core-команда согласилась. Реверт войдёт в 3.14.5 и 3.15 до feature-freeze.</p><p>Тестирование на продакшене у Neil Schemenauer показало интересную картину. Максимальные паузы инкрементального коллектора действительно упали с 26 мс до 1,3 мс — то самое, ради чего его и вводили. Но пиковое потребление памяти выросло до 5× baseline, а суммарное время выполнения задач увеличилось из-за дополнительной бухгалтерии. Для веб-приложений, дата-пайплайнов и батч-задач длинные паузы — не главная проблема. Память — главная.</p><p>Необычная часть истории — что ревёрт идёт в патч-релизе 3.14.5. На цикле релиза 3.14 предположение было, что инкрементальный GC обоснован. Откат показывает, что «прошёл бенчмарк-сьют» и «работает в проде» — это не одно и то же. Если ваши деплои на 3.14 жгут заметно больше памяти, чем на 3.13, теперь понятно почему. Core-команда не закрывает инкрементальный подход насовсем — вернуться к нему могут в 3.16, но через полноценный PEP-процесс, который оригинальная реализация пропустила.</p><h2>OpenAI приобрёл Astral — uv, Ruff и ty переходят к ChatGPT-вендору</h2><p>В конце марта OpenAI <a href="https://openai.com/index/openai-acquires-astral/">объявил о покупке Astral</a> — компании-разработчика uv, Ruff и type-checker ty. Новость прокатилась по сообществу в апреле, когда разработчики начали считать масштаб сделки. uv в начале года перешагнул отметку в 126 миллионов скачиваний в месяц, Ruff давно стал линтером по умолчанию для существенной доли современных Python-проектов. Перенос обоих инструментов под OpenAI — ощутимый сдвиг в том, кто контролирует критическую инфраструктуру Python.</p><p>Реакция Simon Willison — «осторожно-оптимистичная»: команде Astral можно доверять прямо сейчас, но «product+talent acquisition может со временем превратиться в чисто talent-only». Структурная страховка — открытые лицензии: форк остаётся жизнеспособным запасным вариантом, если будущие владельцы решат развернуть стратегию.</p><p>Первый полный месяц после поглощения прошёл «скучно в хорошем смысле»: uv выпустил патчи 0.11.3–0.11.7, Ruff — 0.15.9–0.15.11, ни один проект курса не сменил. Если кто-то опасался резкого крена в сторону интеграции только с Codex — этого не произошло. На tproger мы <a href="https://tproger.ru/news/astral-zapuskaet-napravlenie-bezopasnosti-dlya-python-sozdateli">писали ранее</a> о том, как Astral в начале апреля запустила направление безопасности — теперь это инициатива внутри OpenAI.</p><h2>PEP 803: стабильный ABI для free-threaded Python</h2><p><a href="https://peps.python.org/pep-0803/">PEP 803</a> приняли 30 марта, цель — Python 3.15. Документ определяет abi3t — отдельный вариант стабильного ABI для free-threaded билдов. Это закрывает «wheel-explosion problem», на которую Cryptography, SciPy и Pydantic жалуются уже больше года.</p><p>Сейчас, чтобы расширение работало на free-threaded Python, мейнтейнерам приходится собирать отдельный wheel для каждой минорной версии каждого Python-релиза. Контракт обычного abi3 (собрал один раз — работает на любой 3.x) для free-threaded не работает: у этих билдов разные инварианты по потокобезопасности.</p><p>Цена стабильности — PyObject становится непрозрачным под abi3t. Расширения, которые сейчас лезут в поля PyObject напрямую, должны мигрировать на новые accessor-API из PEP 697 и PEP 793. Это реальный рефакторинг, но взамен — один wheel на много версий Python. Картинку дополняет FastAPI 0.136.0 от 16 апреля: теперь у фреймворка есть официальная поддержка Python 3.14t. Инфраструктурный слой для GIL-free Python зреет быстро.</p><h2>Что ещё стоило бы заметить</h2><ul><li><b>Python 3.15.0 beta 1 — 5 мая.</b> Альфа 8 от 7 апреля стала последней. Бета означает feature-freeze: новые PEP-ы в 3.15 после 5 мая уже не попадут.</li><li><b>JIT в 3.15 быстрее.</b> На x86-64 Linux замеры показывают +6–7%, на AArch64 macOS — +12–13% по сравнению с tail-calling-интерпретатором из 3.14.</li><li><b>PEP 829 (черновик).</b> Barry Warsaw предложил .start-файлы как замену .pth для startup-хуков пакетов. Старый формат позволяет произвольное исполнение кода при импорте — известный supply-chain-вектор.</li><li><b>Polars 1.40.0 (18 апреля).</b> Streaming-движок дотянули до групповых AsOf-join, статистики (cov, corr, skew, kurtosis, entropy) и strptime; добавили lock-free memory manager со spill-to-disk.</li><li><b>Starlette 1.0 (22 марта).</b> ASGI-фундамент FastAPI наконец стабилен: API теперь не сломается без мажорного релиза.</li><li><b>Gemma 4 day-one в Python.</b> 2 апреля Google открыл веса (2B/4B/31B + 26B MoE). Transformers 5.5.0, vLLM 0.19, Ollama 0.21 — всё с поддержкой в день релиза.</li><li><b>Jazzband сворачивается.</b> Площадка коллективного мейнтейнинга 84 проектов закрывается до конца 2026 года. Причины — «slopocalypse» (поток AI-сгенерированных PR-ов) и выгорание единственного админа.</li></ul><h2>Выводы</h2><p>Апрель 2026 ощутимо сдвинул сразу два слоя Python-экосистемы. Управленческий — у языка появился второй формальный орган, конкретно отвечающий за упаковку, что давно просили десятки участников. Техническая прод-сторона — команда призналась, что инкрементальный GC из 3.14 не проходит проверку реальностью, и откатила его. На фоне этого поглощение Astral смотрится как индустриальный сигнал: ключевая Python-инфраструктура мигрирует в орбиту крупных AI-вендоров, и сообществу остаётся следить, не превратится ли «product+talent» в «talent-only».</p><p>Если поддерживаете C-расширение — следите за abi3t и API-миграцией под PEP 697/793. Если деплоите на 3.14 — планируйте обновление до 3.14.5, как только она выйдет. И если вы в принципе следите за Python — стоит разово посмотреть на состав будущего Packaging Council: пять человек впервые получают мандат принимать решения за всех нас.</p><p>Источник: <a href="https://realpython.com/python-news-may-2026/">Python News — Real Python</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Astral запускает направление безопасности для Python — создатели Ruff и uv займутся supply chain</title>
      <link>https://tproger.ru/news/astral-zapuskaet-napravlenie-bezopasnosti-dlya-python-sozdateli</link>
      <comments>https://tproger.ru/news/astral-zapuskaet-napravlenie-bezopasnosti-dlya-python-sozdateli?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/astral-zapuskaet-napravlenie-bezopasnosti-dlya-python-sozdateli</guid>
      <description><![CDATA[<p>Создатели Ruff и uv запускают open source инструменты безопасности для Python: аудит зависимостей, обнаружение вредоносных пакетов, интеграция с uv. Разбираем.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/astral-zapuskaet-napravlenie-bezopasnosti-dlya-python-sozdateli">Astral запускает направление безопасности для Python — создатели Ruff и uv займутся supply chain</a>»</p>]]></description>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 09 Apr 2026 13:30:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы используете <a href="https://docs.astral.sh/ruff/">Ruff</a> или <a href="https://docs.astral.sh/uv/">uv</a> — их создатели теперь займутся и безопасностью ваших Python-зависимостей. Компания <a href="https://astral.sh">Astral</a> <a href="https://astral.sh/blog">объявила</a> о запуске направления open source безопасности для Python-экосистемы.</p><p>Astral — компания, стоящая за Ruff (линтер для Python, заменяющий flake8, isort и десятки других инструментов) и uv (менеджер пакетов Python, в 10–100 раз быстрее pip). Оба инструмента написаны на Rust и стали стандартом де-факто для многих Python-команд.</p><ul><li>Astral (создатели Ruff и uv) запускает направление безопасности для Python-экосистемы</li><li>Фокус — аудит зависимостей, обнаружение уязвимых и вредоносных пакетов в supply chain</li><li>Инструменты будут open source и интегрированы с существующей экосистемой Astral</li><li>Python — одна из главных мишеней supply chain атак наряду с npm</li></ul><h2>Зачем это нужно</h2><p>Python-экосистема — одна из главных мишеней supply chain атак. Только за последний год в PyPI фиксировали сотни вредоносных пакетов: от криптомайнеров до стилеров. Злоумышленники регулярно публикуют пакеты с опечатками в названиях (typosquatting) или внедряют вредоносный код в легитимные пакеты.</p><p>Существующие инструменты (pip-audit, safety, Snyk) работают, но не интегрированы с современным Python-тулингом. Astral хочет встроить безопасность прямо в рабочий процесс — на уровне uv и Ruff, где разработчик и так проводит время.</p><h2>Что планируется</h2><ul><li>Аудит зависимостей — проверка lock-файлов на известные уязвимости (CVE)</li><li>Обнаружение вредоносных пакетов — статический анализ кода пакетов на подозрительное поведение</li><li>Интеграция с uv — проверка безопасности при установке и обновлении зависимостей</li><li>Open source — все инструменты будут открытыми, как Ruff и uv</li></ul><h2>Почему это важно</h2><p>Astral уже доказали, что могут переизобрести Python-тулинг: Ruff заменил десяток линтеров, uv заменил pip, pip-tools, virtualenv и pyenv. Если они применят тот же подход к безопасности — аудит зависимостей станет таким же быстрым и удобным, как линтинг с Ruff.</p><p>Для Python-разработчиков в России это особенно актуально: многие используют локальные зеркала PyPI или корпоративные реестры, где контроль целостности пакетов — отдельная головная боль.</p><p>Следить за развитием: <a href="https://astral.sh/blog">блог Astral</a>, <a href="https://github.com/astral-sh">GitHub</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>.gitignore: полный гайд с шаблонами для Python, Node.js, Java и Go</title>
      <link>https://tproger.ru/articles/gitignore-polnyj-gajd-s-wablonami-dlya-python-node-js-java-i</link>
      <comments>https://tproger.ru/articles/gitignore-polnyj-gajd-s-wablonami-dlya-python-node-js-java-i?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/gitignore-polnyj-gajd-s-wablonami-dlya-python-node-js-java-i</guid>
      <description><![CDATA[<p>Синтаксис .gitignore, готовые шаблоны для Python, Node.js, Java и Go, глобальный gitignore, git rm --cached и gitignore.io. Разбираем с примерами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/gitignore-polnyj-gajd-s-wablonami-dlya-python-node-js-java-i">.gitignore: полный гайд с шаблонами для Python, Node.js, Java и Go</a>»</p>]]></description>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 07 Apr 2026 15:26:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>.gitignore — это файл, который сообщает Git, какие файлы и директории не нужно отслеживать.</p><p>Файл .gitignore — один из первых, который появляется в любом репозитории. Но многие добавляют его по привычке, не разбираясь в синтаксисе и не думая о глобальных настройках. В итоге в историю коммитов попадают .pyc-файлы, папки node_modules и секреты из .env.</p><p>В этом гайде разберём синтаксис .gitignore с примерами каждого паттерна, дадим готовые шаблоны для Python, Node.js, Java и Go, а также покажем, как исправить ситуацию, если файл уже попал в индекс. Статья рассчитана на тех, кто уже знаком с основами Git — если нужно освежить базу, начните с <a href="https://tproger.ru/translations/beginner-git-cheatsheet">введения в Git</a>.</p><p>— .gitignore поддерживает паттерны *, **, !, / и комментарии через #</p><p>— Готовые шаблоны для Python, Node.js, Java и Go закрывают 90% типичных случаев</p><p>— Глобальный ~/.gitignore_global избавляет от повторения в каждом проекте правил для IDE и ОС</p><p>— Если файл уже в индексе, git rm --cached убирает его без удаления с диска</p><p>— .gitkeep — хак для отслеживания пустых директорий</p><p>Эта статья — часть нашего <a href="https://tproger.ru/articles/git-polnyj-putevoditel-ot-pervogo-kommita-do-prodvinutyh-wor">полного путеводителя по Git</a>. Там — весь маршрут: от первого коммита до продвинутых workflow.</p><h2>Синтаксис .gitignore: разбираем каждый паттерн</h2><p>Git читает .gitignore построчно. Пустые строки игнорируются, строки с # в начале — комментарии. Остальное — паттерны.</p><p><b>* — любое количество символов в имени файла</b> (кроме /):</p><p><b>** — любое количество директорий</b> (рекурсивно):</p><p><b>! — отмена предыдущего правила</b> (исключение из исключений):</p><p><b>Важно:</b> паттерн ! не работает, если родительская директория уже заигнорирована. Например, если в .gitignore есть строка build/, то правило !build/keep-me.txt не поможет — файл всё равно будет игнорироваться. Чтобы исключение сработало, нужно сначала разрешить саму директорию: !build/, а затем уже — конкретный файл.</p><p><b>/ в начале — привязка к корню репозитория</b>:</p><p><b>/ в конце — игнорировать только директорию</b>, не файл с таким же именем:</p><p><b># — комментарий</b>. Используйте для группировки правил:</p><h2>Шаблон .gitignore для Python</h2><p>Python генерирует .pyc-файлы и папки __pycache__ при каждом запуске. Виртуальные окружения (venv, .venv) и файлы с переменными окружения (.env) тоже не должны попадать в репозиторий.</p><h2>Шаблон .gitignore для Node.js</h2><p>Главная причина тяжёлых репозиториев на Node.js — папка node_modules, которую забыли добавить в .gitignore. Её размер легко достигает сотен мегабайт.</p><h2>Шаблон .gitignore для Java</h2><p>Java-проекты собираются Maven или Gradle — у каждого свои папки артефактов. Скомпилированные .class-файлы и .jar-архивы пересобираются при каждом билде и в репозитории не нужны.</p><h2>Шаблон .gitignore для Go</h2><p>Go компилирует проект в бинарник — его хранить в репозитории не нужно. Папка vendor/ содержит копии зависимостей: её включать или нет — зависит от политики команды. Если используете Go modules без vendor-режима, добавьте папку в .gitignore.</p><h2>Глобальный .gitignore: один раз для всех проектов</h2><p>Некоторые файлы мусорят в любом проекте — .DS_Store на macOS, Thumbs.db на Windows, файлы IDE вроде .idea/ или .vscode/. Дублировать эти правила в каждом репозитории неудобно. Для этого есть глобальный .gitignore.</p><p>Настройка через core.excludesFile:</p><p>Пример содержимого ~/.gitignore_global:</p><p>После настройки Git применяет глобальные правила автоматически во всех репозиториях на вашем компьютере — без изменения .gitignore проекта.</p><p>Ещё один способ локально игнорировать файлы — .git/info/exclude. Этот файл работает как .gitignore, но не коммитится в репозиторий и виден только вам. Удобно для временных файлов, специфичных для вашего окружения, которые не стоит выносить в глобальный ~/.gitignore_global.</p><h2>Файл уже в индексе: как перестать его отслеживать</h2><p>Добавить правило в .gitignore недостаточно, если файл уже был закоммичен. Git продолжит его отслеживать. Нужно убрать файл из индекса, сохранив его на диске:</p><p>После этого добавьте правило в .gitignore и закоммитьте оба изменения — удаление из индекса и обновлённый .gitignore. Сам файл останется у вас на диске, но перестанет появляться в git status.</p><p>Если нужно очистить весь индекс и применить правила заново (например, после добавления нескольких паттернов):</p><p><b>Внимание:</b> эта команда пересоздаёт индекс целиком. Не запускайте её при незакоммиченных изменениях.</p><h2>.gitkeep: как отслеживать пустые директории</h2><p>Git не отслеживает директории — только файлы. Если папка пустая, git add её просто проигнорирует. Это проблема, когда структура директорий важна для проекта: например, logs/, uploads/, tmp/.</p><p>Решение — добавить в пустую папку файл-заглушку .gitkeep:</p><p>Файл .gitkeep — неофициальное соглашение. Никакой специальной поддержки в Git нет, это просто пустой файл с понятным именем. Некоторые команды используют .githold или .keep — принципиальной разницы нет.</p><h2>Инструменты: генераторы готовых шаблонов</h2><p>Не обязательно писать .gitignore с нуля — есть готовые инструменты:</p><ul><li><a href="https://www.toptal.com/developers/gitignore">gitignore.io</a> (toptal.com/developers/gitignore) — генератор по языку, IDE и ОС. Введите «Python», «JetBrains», «macOS» — получите готовый файл.</li><li><a href="https://github.com/github/gitignore">GitHub gitignore templates</a> — официальная коллекция шаблонов от GitHub. Шаблоны для 150+ языков и фреймворков. Используется как основа при создании репозитория через интерфейс GitHub.</li><li>gh repo create — при создании репозитория через GitHub CLI можно сразу выбрать шаблон .gitignore флагом --gitignore.</li></ul><p>Для проверки, почему конкретный файл игнорируется (или не игнорируется), используйте встроенную команду:</p><h2>Итог</h2><p>Правильный .gitignore — это не формальность, а часть культуры работы с репозиторием. Он защищает от утечки секретов, не даёт захламить историю коммитов и экономит время при клонировании. Начните с шаблона под ваш стек (используйте gitignore.io или GitHub-коллекцию), добавьте глобальный файл для настроек IDE, и у вас не будет проблем с лишними файлами в git status.</p><p>Если хотите разобраться с Git глубже — изучите <a href="https://tproger.ru/articles/git-polnyj-putevoditel-ot-pervogo-kommita-do-prodvinutyh-wor">полный путеводитель по Git</a> на Tproger: там собраны все ключевые темы от основ до продвинутых техник.</p>]]></content:encoded>
    </item>
    <item>
      <title>Франкенштейн в медицине: как я скрестил ViT и ruGPT-3, чтобы научить ИИ читать рентген на русском</title>
      <link>https://tproger.ru/articles/frankenwtejn-v-medicine--kak-ya-skrestil-vit-i-rugpt-3--chtoby-nau</link>
      <comments>https://tproger.ru/articles/frankenwtejn-v-medicine--kak-ya-skrestil-vit-i-rugpt-3--chtoby-nau?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[максим митин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/frankenwtejn-v-medicine--kak-ya-skrestil-vit-i-rugpt-3--chtoby-nau</guid>
      <description><![CDATA[<p>Практический кейс: создание русскоязычной мультимодальной нейросети (Vision-Language) для анализа рентгеновских снимков. Скрещиваем ViT и ruGPT-3, решаем проблемы с датасетами на Kaggle и выкатываем ИИ в продакшн на Hugging Face. Открытый код на Python.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/frankenwtejn-v-medicine--kak-ya-skrestil-vit-i-rugpt-3--chtoby-nau">Франкенштейн в медицине: как я скрестил ViT и ruGPT-3, чтобы научить ИИ читать рентген на русском</a>»</p>]]></description>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[NVIDIA]]></category>
      <category><![CDATA[Взлом]]></category>
      <category><![CDATA[Сбер]]></category>
      <category><![CDATA[Машин]]></category>
      <category><![CDATA[GPU]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 05 Apr 2026 06:30:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Сейчас из каждого утюга рассказывают про мультимодальные нейросети: GPT-4o смотрит через камеру, Gemini анализирует видео. В медицине тоже есть крутые открытые ИИ-модели для анализа снимков (например, на базе датасетов MIMIC-CXR), но у них всех есть один фатальный недостаток для нашего рынка — они говорят исключительно на английском.</p><p>Мне стало интересно: а можно ли на бесплатных мощностях, буквально "на коленке", собрать русскоязычного ИИ-рентгенолога? Спойлер: можно. В этой статье расскажу, как я скрестил Vision Transformer от Google с ruGPT-3 от Сбера, как боролся с датасетами на Kaggle и что из этого вышло. В конце — ссылки на GitHub и рабочее демо.</p><h2>Архитектура: как пришить глаза к мозгу</h2><p>Чтобы нейросеть могла посмотреть на снимок и написать текст, нужна архитектура Vision-Language Model. Обучать такого монстра с нуля у меня не было ни ресурсов, ни желания. Поэтому я пошел по пути Hugging Face VisionEncoderDecoderModel.</p><p>Идея проста как кирпич:</p><ol><li>Энкодер (Глаза): Берем предобученный google/vit-base-patch16-224-in21k. Он отлично дробит картинку на патчи и извлекает визуальные фичи (понимает, где ребра, а где легкие).</li><li>Декодер (Язык): Берем ai-forever/rugpt3small_based_on_gpt2. У нее нет глаз, она умеет только генерировать текст.</li></ol><p>Чтобы их сшить, пришлось немного "взломать" конфиг ruGPT-3, принудительно сказав ей: «Теперь ты декодер, и у тебя есть слои кросс-внимания» (is_decoder=True, add_cross_attention=True). Hugging Face заботливо создал новые пустые веса между двумя моделями. Именно эти связи мне и предстояло обучить.</p><h2>Data Engineering: боль, страдания и Kaggle</h2><p>Найти 7-10 тысяч рентгеновских снимков с подробными заключениями на русском языке в открытом доступе — задача нереальная.</p><p>Поэтому я взял открытый американский датасет Indiana University Chest X-Ray (IU X-Ray). Там есть картинки и тексты от американских врачей.</p><p>Прямо на Kaggle я поднял пайплайн машинного перевода на базе Helsinki-NLP/opus-mt-en-ru. Закинул тексты в GPU батчами по 32 штуки и за 10 минут перевел более 7000 медицинских заключений на вполне сносный русский медицинский язык.</p><p>Но тут платформа подкинула сюрприз: Kaggle прячет часть файлов в виртуальной файловой системе (снимков 7000, а стандартный скрипт видел только 4). Пришлось писать суровый маппинг с глубоким сканированием (os.walk), отрезать расширения и жестко связывать ID в CSV с реальными путями на диске.</p><h2>Обучение: выжимаем все соки из бесплатных T4</h2><p>Обучение проходило на Kaggle (2x NVIDIA T4). Чтобы модель не умерла от нехватки памяти (OOM), а сессия не отвалилась по тайм-ауту, пришлось шаманить:</p><ul><li>Включил Mixed Precision (fp16) — ускорило обучение в 2 раза.</li><li>Настроил Gradient Accumulation — размер батча на видеокарту был всего 4, но виртуально мы накапливали до 16.</li><li>Столкнулся с тем, что Seq2SeqTrainer крашится при попытке сохранить промежуточный чекпоинт мультимодального "франкенштейна". Решение? Выключить промежуточные сохранения (save_strategy="epoch") и молиться, чтобы Kaggle не завис. (Кстати, спасает JS-скрипт в консоли браузера, делающий клик раз в 60 секунд).</li></ul><p>На 15 эпох ушло около 2.5 часов.</p><h2>Что получилось в итоге? (Потрогать руками)</h2><p>Получилась нейросеть, которая реально понимает, что изображено на рентгене, и сыпет терминами вроде «кальцифицированная гранулема» или «легочная васкулярность». Да, иногда она "галлюцинирует" (датасет в 7к снимков — это капля в море для ML), но базовые вещи вроде чистых легких или пневмоторакса сечет неплохо.</p><p>Я завернул модель в Gradio и выложил на Hugging Face Spaces. Можно зайти с телефона или ПК, загрузить любой снимок рентгена из гугла и посмотреть, что она выдаст.</p><p>👉 Потыкать лайв-демо тут: <a rel="noopener noreferrer" href="https://www.google.com/url?sa=E&amp;q=https%3A%2F%2Fhuggingface.co%2Fspaces%2Flivadies%2FAI-Radiologist-RU">Hugging Face Space</a></p><p>👉 Весь код, пайплайны и веса тут: <a rel="noopener noreferrer" href="https://www.google.com/url?sa=E&amp;q=https%3A%2F%2Fgithub.com%2Flivadies-collab%2FMultimodal-XRay-Analyzer-RU">GitHub Репозиторий</a></p><p>Буду рад, если кому-то этот код сэкономит время при создании своих мультимодальных сеток. Залетайте в репу, ставьте звездочки, форкайте. Если есть идеи, как улучшить датасет (может, прогнать переводы через LLM для чистки медицинского сленга) — пишите в комменты!</p><p>(Дисклеймер: модель обучена в исследовательских целях за вечер. Не суйте ей свои снимки вместо похода к реальному врачу)</p>]]></content:encoded>
    </item>
    <item>
      <title>Python: полный путеводитель для разработчика</title>
      <link>https://tproger.ru/articles/python--polnyj-putevoditel-dlya-razrabotchika</link>
      <comments>https://tproger.ru/articles/python--polnyj-putevoditel-dlya-razrabotchika?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Картофельный Повелитель]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/python--polnyj-putevoditel-dlya-razrabotchika</guid>
      <description><![CDATA[<p>Структурированный гайд по Python: синтаксис, ООП, Django/FastAPI, Data Science, asyncio и GIL. Примеры кода и ссылки на углублённые материалы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/python--polnyj-putevoditel-dlya-razrabotchika">Python: полный путеводитель для разработчика</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[Фреймворки и библиотеки]]></category>
      <category><![CDATA[Data Science]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Apr 2026 11:17:39 GMT</pubDate>
      <content:encoded><![CDATA[<p>Python — высокоуровневый интерпретируемый язык программирования общего назначения, который уверенно входит в тройку самых популярных языков программирования в мире. По данным индекса <a href="https://www.tiobe.com/tiobe-index/">TIOBE</a> на начало 2026 года, он стабильно удерживает первое место, а <a href="https://survey.stackoverflow.co/2024/">Stack Overflow Developer Survey</a> подтверждает: Python остаётся одним из самых желанных языков для изучения. Причина — универсальность: веб-приложения, нейросети, автоматизация, анализ данных, боты и управление инфраструктурой. Чистый синтаксис, читаемый код и гигантская экосистема библиотек позволяют писать рабочие программы уже после нескольких часов знакомства с языком.</p><p>В октябре 2025 года вышел Python 3.14 с экспериментальным JIT-компилятором и поддержкой шаблонных T-строк — язык продолжает ускоряться, не жертвуя простотой. Экосистема тоже не стоит на месте: FastAPI стал стандартом для высокопроизводительных API, а инструменты вроде Ruff и uv радикально ускорили рабочий процесс разработчика. Python Package Index (<a href="https://pypi.org/">PyPI</a>) насчитывает более 600 000 пакетов — для любой задачи, скорее всего, уже существует готовое решение.</p><p>Этот путеводитель — не энциклопедия и не учебник. Это навигационный хаб: мы кратко разберём каждую важную область Python и дадим ссылки на подробные материалы Tproger, где каждая тема раскрыта в деталях. Неважно, только начинаете вы знакомство с языком или уже пишете на нём продакшен-код — здесь вы найдёте структурированную карту для дальнейшего роста. Мы охватим основы синтаксиса, продвинутые концепции, три главных веб-фреймворка, Data Science, последние нововведения в Python 3.14 и карьерные перспективы.</p><p>— Python — язык №1 по TIOBE 2026, более 600 000 пакетов в PyPI</p><p>— Основы языка: динамическая типизация, duck typing, встроенные коллекции (list, tuple, dict, set и другие)</p><p>— Три главных веб-фреймворка: Django (full-stack), Flask (микро), FastAPI (async API)</p><p>— Python 3.14: экспериментальный JIT-компилятор и шаблонные T-строки</p><p>— Медианная зарплата middle Python-разработчика: 250 000—350 000 руб./мес.</p><p>— Функции: замыкания, LEGB, *args/**kwargs, лямбды</p><p>— ООП: классы, наследование, @property, магические методы</p><p>— GIL, threading, asyncio и multiprocessing — когда что использовать</p><p>— Тестирование с pytest, работа с файлами через pathlib</p><h2>Основы языка: с чего начинается Python</h2><p>Python — язык с динамической типизацией и строгим контролем отступов. Если в C++ или Java фигурные скобки определяют блоки кода, то в Python эту роль играют пробелы. Поначалу это непривычно, но на практике делает код единообразным и читаемым — у вас просто нет возможности написать нечитаемую «лапшу». Философия Python описана в «Дзен Python» (<a href="https://peps.python.org/pep-0020/">PEP 20</a>): «Красивое лучше уродливого», «Явное лучше неявного», «Простое лучше сложного». Эти принципы пронизывают весь язык и его стандартную библиотеку.</p><p>Базовые типы данных в Python — это числа (int, float, complex), строки (str), булевы значения (bool), а также коллекции: списки (list), кортежи (tuple), множества (set) и словари (dict). Каждый тип имеет свои особенности. Например, целые числа в Python не ограничены размером: можно спокойно работать с числами в тысячи разрядов без переполнения — попробуйте сделать это в C или Java. Строки неизменяемы, а словари с Python 3.7 гарантированно сохраняют порядок вставки. Множества предоставляют проверку принадлежности элемента за O(1), а операции объединения, пересечения и разности выполняются за линейное время, что делает их незаменимыми для задач на поиск уникальных элементов.</p><p>Одна из ключевых концепций — duck typing: «если объект ходит как утка и крякает как утка, то это утка». Python не проверяет тип объекта заранее — он проверяет, поддерживает ли объект нужную операцию. Это даёт гибкость, но требует дисциплины. Современный Python активно использует аннотации типов (type hints), которые помогают IDE и линтерам находить ошибки ещё до запуска кода. Начиная с Python 3.10 появился оператор match/case (паттерн-матчинг), а в 3.12 — улучшенные дженерики и type aliases, которые делают типизацию ещё удобнее.</p><p>Переменные в Python — это не ячейки памяти, а метки (имена), привязанные к объектам. Понимание этого механизма — ключ к предсказуемой работе с мутабельными типами вроде списков и словарей. Когда вы пишете a = b для списка, вы не копируете данные — обе переменные указывают на один и тот же объект в памяти. Отсюда классические ловушки с изменяемыми аргументами по умолчанию и «неожиданным» изменением данных. Для создания независимой копии нужно использовать срез [:], метод copy() или модуль copy для глубокого копирования.</p><p>Управляющие конструкции в Python минималистичны и выразительны. Условия записываются через if/elif/else, циклы — через for и while. Конструкция for в Python ближе к foreach из других языков: она итерирует по элементам коллекции, а не по индексам. Функция range() генерирует последовательности чисел для случаев, когда нужен числовой цикл. Важно освоить и обработку исключений (try/except/finally) — Python использует исключения не только для ошибок, но и как механизм управления потоком, например StopIteration для завершения итерации.</p><p>Подробнее о типах данных и их поведении — в <a href="https://tproger.ru/translations/python-data-types">нашем гайде по основным типам данных</a>. А если хотите системно пройти все базовые концепции от установки до первого проекта — загляните в <a href="https://tproger.ru/articles/podrobnoe-opisanie-jazyka-python-dlja-nachinajushhih">подробное описание языка для начинающих</a>.</p><h2>Функции в Python</h2><p>Функции — основной инструмент структурирования кода в Python. Ключевое слово def создаёт функцию, return возвращает результат. Python поддерживает позиционные и именованные аргументы, значения по умолчанию, а также распаковку через *args (кортеж позиционных) и **kwargs (словарь именованных). Это позволяет создавать гибкие интерфейсы: от простых утилит до сложных API-обёрток.</p><p>Анонимные функции lambda удобны для коротких выражений — например, в качестве ключа сортировки: sorted(users, key=lambda u: u.age). Однако злоупотреблять ими не стоит: если лямбда не помещается в одну строку, лучше написать обычную функцию с понятным именем.</p><p>Python использует правило LEGB для поиска переменных: Local → Enclosing → Global → Built-in. Это объясняет, почему переменная внутри функции «затеняет» глобальную, и почему для изменения глобальной переменной нужно объявление global. Замыкания (closures) — функции, захватывающие переменные из объемлющей области видимости — лежат в основе декораторов, фабричных функций и callback-паттернов.</p><p>Функции в Python — объекты первого класса: их можно передавать как аргументы, возвращать из других функций и сохранять в структурах данных. Это фундамент для функционального стиля программирования, который активно используется вместе с map(), filter() и functools.</p><h2>ООП в Python</h2><p>Python — мультипарадигменный язык, но ООП в нём реализовано глубоко и последовательно. Классы создаются ключевым словом class, конструктор определяется методом __init__, а первый параметр каждого метода — self, ссылка на текущий экземпляр. В отличие от Java или C#, где this подразумевается неявно, Python требует явного указания — это осознанный выбор в пользу читаемости.</p><p>Python поддерживает множественное наследование через механизм MRO (Method Resolution Order) — алгоритм C3-линеаризации определяет порядок обхода родительских классов. Полиморфизм реализуется через duck typing: нет необходимости в интерфейсах — достаточно, чтобы объект имел нужные методы. Для формальных контрактов есть модуль abc с абстрактными базовыми классами и декоратором @abstractmethod.</p><p>Инкапсуляция в Python — скорее соглашение, чем принуждение. Префикс _ обозначает «приватный» атрибут, __ — активирует механизм name mangling, но ни то ни другое не запрещает доступ. Для контролируемого доступа к атрибутам используют декоратор @property, который превращает метод в вычисляемое свойство с геттером, сеттером и делетером.</p><p>Магические методы (dunder-методы) — мощный механизм, позволяющий объектам вести себя как встроенные типы. __str__ и __repr__ управляют строковым представлением, __eq__ и __hash__ — сравнением, __len__ и __getitem__ — доступом к элементам. Подробный разбор синтаксиса и концепций Python — в <a href="https://tproger.ru/articles/podrobnoe-opisanie-jazyka-python-dlja-nachinajushhih">нашем подробном описании языка для начинающих</a>.</p><h2>Модули и пакеты Python</h2><p>Модульная система Python проста: любой .py-файл — это модуль, а директория с __init__.py — пакет. Импорт осуществляется через import и from ... import. Python ищет модули по путям из sys.path, куда автоматически входят текущая директория, стандартная библиотека и директория site-packages с установленными пакетами.</p><p>Для управления зависимостями экосистема предлагает несколько инструментов. Классический pip + venv решает базовые задачи: создание изолированного окружения и установка пакетов из PyPI. Poetry добавляет управление зависимостями через pyproject.toml, lock-файлы и публикацию пакетов. Новый uv — написанный на Rust менеджер пакетов — работает в 10—100 раз быстрее pip и активно набирает популярность.</p><p>Стандартная библиотека Python (batteries included) — одна из самых богатых: от работы с сетью (http, socket) до сериализации (json, pickle), регулярных выражений (re) и параллелизма (threading, multiprocessing). Полный список из более чем 200 модулей доступен в <a href="https://tproger.ru/translations/10-python-libraries-you-might-not-know">нашей подборке полезных Python-библиотек</a>.</p><h2>Практика: задачи для начинающих</h2><p>Главная ошибка при изучении программирования — читать теорию неделями, не написав ни строчки кода. Python хорош тем, что позволяет начать практиковаться с первого дня: интерактивный интерпретатор (REPL), быстрая обратная связь и понятные сообщения об ошибках снижают барьер входа до минимума. Откройте терминал, напишите python3 — и вы уже можете экспериментировать.</p><p>Начинайте с простых задач: обработка строк, работа со списками, базовые циклы и условия. Затем переходите к задачам на функции, рекурсию и работу с файлами. Важно не просто решать задачу, а разбирать чужие решения — так вы быстрее освоите идиоматический Python. Один грамотно решённый пример научит вас большему, чем десять страниц документации. Обращайте внимание на то, как опытные разработчики используют встроенные функции (enumerate, zip, map), срезы списков и словарные включения — это и есть «питонический» стиль.</p><p>Хорошая практика — вести собственный файл с решениями и заметками. Возвращаясь к задачам через неделю, вы увидите, как вырос ваш уровень. Платформы вроде LeetCode, Codewars и HackerRank предлагают задачи с автоматической проверкой, но начинать лучше с задач, адаптированных под русскоязычную аудиторию. Ещё один совет: не гонитесь за количеством. Пять задач, решённых вдумчиво с разбором альтернативных подходов, ценнее пятидесяти, решённых механически копированием паттернов.</p><p>Мы собрали <a href="https://tproger.ru/problems/python-3-exercises-for-beginners-geekbrains">подборку задач для начинающих</a> — от элементарных до задач средней сложности. Каждая задача тренирует конкретный навык: работу с коллекциями, строковыми операциями, логикой ветвления. Рекомендуем решать их последовательно — сложность нарастает постепенно, и к концу подборки вы будете уверенно владеть основами языка.</p><h2>Продвинутые концепции Python</h2><p>Когда основы освоены, приходит время познакомиться с инструментами, которые отличают код новичка от кода опытного разработчика. В Python таких инструментов много, но есть пять ключевых концепций, без которых сложно писать по-настоящему качественный код: декораторы, генераторы, comprehensions, контекст-менеджеры и механизм *args/**kwargs. Именно владение этими инструментами отличает junior-разработчика от middle — и именно их чаще всего спрашивают на собеседованиях.</p><h3>Декораторы</h3><p>Декоратор — это функция, которая принимает другую функцию и возвращает её модифицированную версию. Звучит абстрактно, но на практике декораторы встречаются повсеместно: @property, @staticmethod, @login_required в Django, @app.route() во Flask. Они позволяют добавлять поведение без изменения исходного кода функции — логирование, кэширование, проверку прав доступа, валидацию аргументов, замер времени выполнения.</p><p>Под капотом декоратор — это обычный паттерн: функция, принимающая функцию и возвращающая функцию. Но синтаксический сахар с символом @ делает код лаконичным и выразительным. Декораторы можно параметризовать, складывать стопкой (применять несколько к одной функции) и даже применять к целым классам. Стандартная библиотека Python включает полезные декораторы: @functools.lru_cache для кэширования результатов, @functools.wraps для сохранения метаданных обёрнутой функции.</p><p>Декораторы — одна из тех тем, которые кажутся сложными ровно до момента, пока не разберёшься. Подробный разбор с примерами — в статье <a href="https://tproger.ru/translations/demystifying-decorators-in-python">«Декораторы в Python: понять и полюбить»</a>.</p><h3>Генераторы и comprehensions</h3><p>Генераторы — это функции с ключевым словом yield, которые возвращают данные лениво, по одному элементу за раз. Вместо того чтобы создавать в памяти список из миллиона элементов, генератор выдаёт их по запросу. Это критически важно при обработке больших файлов, потоков данных и результатов SQL-запросов. Генератор занимает фиксированный объём памяти независимо от количества элементов — хоть миллион, хоть миллиард.</p><p>Comprehensions (списковые, словарные и множественные включения) — это питонический способ создания коллекций в одну строку. Вместо цикла из трёх строк вы пишете выразительную конструкцию: [x ** 2 for x in range(10) if x % 2 == 0]. Они быстрее эквивалентных циклов (Python оптимизирует их на уровне байткода) и читаются проще — если не злоупотреблять вложенностью. Словарные включения {k: v for k, v in pairs} и множественные {x for x in items} работают по тому же принципу.</p><h3>Контекст-менеджеры и *args/**kwargs</h3><p>Конструкция with в Python — это контекст-менеджер, который гарантирует корректное освобождение ресурсов: закрытие файлов, соединений с базой, снятие блокировок. Вместо try/finally вы пишете with open('file.txt') as f: — и ресурс освобождается автоматически, даже если произошла ошибка. Можно создавать собственные контекст-менеджеры через методы __enter__/__exit__ или декоратор @contextmanager из модуля contextlib — это проще, чем кажется.</p><p>*args и **kwargs позволяют создавать функции с переменным числом аргументов. Это основа для написания гибких API, декораторов и обёрток. Понимание того, как Python распаковывает аргументы, открывает путь к элегантным решениям: передача параметров из словаря в функцию одной строкой, объединение конфигураций, создание универсальных обёрток. В комбинации с аннотациями типов (ParamSpec, Concatenate) этот механизм стал ещё мощнее в последних версиях Python.</p><p>Отдельного внимания заслуживает работа с памятью. Python скрывает от разработчика ручное управление памятью, но понимание того, сколько весят разные типы данных и как работает сборщик мусора, помогает писать эффективный код. Например, пустой список в Python занимает 56 байт, а каждый элемент добавляет 8 байт на указатель плюс размер самого объекта. Для числовых задач это означает, что NumPy-массив может быть в 10 раз компактнее обычного списка. Детальный разбор — в статье о <a href="https://tproger.ru/articles/raspredelenie-pamjati-v-python-skolko-i-v-kakih-sluchajah-zanimajut-tipy-dannyh">распределении памяти в Python</a>.</p><h3>Dataclasses и NamedTuple</h3><p>Модуль dataclasses (Python 3.7+) решает классическую проблему: написание классов, которые в основном хранят данные. Декоратор @dataclass автоматически генерирует __init__, __repr__, __eq__ и другие методы. С параметром frozen=True класс становится неизменяемым — удобно для конфигураций и DTO.</p><p>NamedTuple — ещё более лёгкая альтернатива: именованный кортеж занимает меньше памяти, чем dataclass, и автоматически поддерживает распаковку и итерацию. Выбор между ними прост: нужна изменяемость или наследование — dataclass, нужна компактность и совместимость с кортежами — NamedTuple.</p><h2>Многопоточность, асинхронность и GIL</h2><p>GIL (Global Interpreter Lock) — глобальная блокировка интерпретатора CPython, которая гарантирует, что в каждый момент времени только один поток выполняет байткод Python. Это упрощает реализацию интерпретатора и работу с памятью, но ограничивает параллелизм CPU-задач. Важно: GIL не мешает I/O-параллелизму — потоки освобождают блокировку при ожидании сети, диска или sleep.</p><p>Модуль threading подходит для I/O-bound задач: параллельные HTTP-запросы, чтение файлов, работа с базами данных. Для CPU-bound вычислений (обработка изображений, математические расчёты) используйте multiprocessing — он создаёт отдельные процессы, каждый со своим GIL. Пул concurrent.futures.ProcessPoolExecutor упрощает распределение задач по ядрам.</p><p>Модуль asyncio — стандарт для асинхронного программирования в Python. Конструкции async def и await позволяют писать неблокирующий код, который выглядит почти как синхронный. Один поток обрабатывает тысячи соединений — именно поэтому FastAPI и другие ASGI-фреймворки работают быстрее классических WSGI-аналогов.</p><p>Когда что использовать? threading — для I/O-операций с умеренной нагрузкой. asyncio — для высоконагруженных I/O-сценариев (веб-серверы, парсеры). multiprocessing — для CPU-bound задач. На практике они часто комбинируются: например, asyncio для сетевого ввода-вывода и ProcessPool для тяжёлых вычислений внутри одного приложения.</p><p>Важная новость: <a href="https://peps.python.org/pep-0703/">PEP 703</a> предлагает сделать GIL опциональным (free-threaded Python). Экспериментальная сборка без GIL уже доступна в Python 3.13+ через специальную сборку python3.14t (free-threaded build). В Python 3.14 этот режим стал официально поддерживаемым (<a href="https://peps.python.org/pep-0779/">PEP 779</a>). Если эксперимент окажется успешным, в будущих версиях Python сможет полноценно использовать все ядра процессора без обходных путей через multiprocessing.</p><h2>Тестирование в Python</h2><p>Тестирование — не роскошь, а базовая гигиена разработки. В Python стандартом де-факто является pytest — фреймворк, который сочетает простоту написания тестов с мощной системой расширений. Обычные функции с assert — это уже тесты. Не нужны классы, наследование от TestCase и специальные методы.</p><p>Фикстуры (@pytest.fixture) управляют подготовкой и очисткой тестового окружения: подключение к базе данных, создание тестового клиента, временные файлы. Параметризация (@pytest.mark.parametrize) позволяет прогнать один тест с десятками наборов входных данных без дублирования кода.</p><p>Для изоляции внешних зависимостей используйте unittest.mock — встроенный модуль, который позволяет подменять HTTP-запросы, обращения к базе данных и вызовы внешних сервисов. В связке с pytest это покрывает 95% потребностей в тестировании. TDD (Test-Driven Development) — подход, при котором тест пишется раньше кода. Его необязательно практиковать всегда, но для сложной бизнес-логики он существенно снижает количество регрессий.</p><h2>Работа с файлами и данными</h2><p>Python предоставляет удобные инструменты для работы с файлами любых форматов. Конструкция with open(...) гарантирует корректное закрытие файла даже при ошибке. Модуль pathlib (Python 3.4+) — современная замена os.path: объектно-ориентированные пути, кроссплатформенная совместимость и цепочки вызовов.</p><p>Для структурированных данных стандартная библиотека предлагает модули json, csv и configparser. Для YAML понадобится сторонний пакет PyYAML. Работа с Excel-файлами — через openpyxl или pandas. Для больших объёмов данных используйте потоковое чтение: csv.reader или json.JSONDecoder().raw_decode() вместо загрузки всего файла в память.</p><p>Для работы с распределением памяти и внутренним устройством типов данных Python — рекомендуем нашу <a href="https://tproger.ru/articles/raspredelenie-pamjati-v-python-skolko-i-v-kakih-sluchajah-zanimajut-tipy-dannyh">статью о распределении памяти в Python</a>: сколько и в каких случаях занимают типы данных.</p><h2>Веб-разработка на Python</h2><p>Python — один из ключевых языков для серверной веб-разработки. Три фреймворка покрывают практически все сценарии: Django — для полнофункциональных приложений, Flask — для микросервисов и лёгких проектов, FastAPI — для высокопроизводительных API. Каждый из них занимает свою нишу, и выбор зависит от масштаба проекта, требований к производительности и опыта команды. Разберём сильные и слабые стороны каждого, чтобы вы могли сделать осознанный выбор.</p><h3>Django: всё включено</h3><p>Django — это «батарейки в комплекте». ORM, админ-панель, система аутентификации, шаблонизатор, миграции базы данных, защита от CSRF и XSS, формы с валидацией, кэширование, интернационализация — всё работает из коробки и не требует сторонних зависимостей. Это делает Django идеальным выбором для крупных проектов: интернет-магазинов, SaaS-платформ, CRM-систем, внутренних корпоративных порталов. Instagram, Mozilla, Disqus, Pinterest — все они используют Django в продакшене.</p><p>Главное преимущество Django — зрелость. Фреймворку больше 20 лет, он имеет огромное сообщество, тысячи готовых пакетов (django-rest-framework, django-allauth, celery) и предсказуемый цикл релизов. Django ORM позволяет работать с базой данных через Python-объекты, не написав ни строчки SQL, а система миграций автоматически отслеживает изменения в моделях. Обратная сторона — Django навязывает свою архитектуру. Если вам нужен только REST API без шаблонов и админки, значительная часть фреймворка будет «мёртвым грузом».</p><h3>Flask: минималистичный и гибкий</h3><p>Flask — противоположность Django. Микрофреймворк даёт маршрутизацию, обработку запросов и систему расширений — остальное вы выбираете сами. Нужна ORM? Подключите SQLAlchemy. Нужна авторизация? Flask-Login. Хотите WebSocket? Flask-SocketIO. Такой подход идеален для микросервисов, прототипов и проектов, где полный контроль над стеком важнее скорости старта. Flask часто выбирают для внутренних сервисов, API-шлюзов и проектов, которые начинаются маленькими, но могут вырасти.</p><p>Flask отлично сочетается с фронтенд-фреймворками. Например, в статье <a href="https://tproger.ru/translations/developing-app-with-flask-and-vue-js">«Пишем одностраничное приложение с Flask и Vue.js»</a> мы показываем, как построить SPA с Flask-бэкендом — от настройки проекта до деплоя. Это типичный современный стек: Python на сервере, JavaScript-фреймворк на клиенте.</p><h3>FastAPI: скорость и типизация</h3><p>FastAPI — самый молодой из тройки, но уже ставший стандартом для создания API. Он построен на Starlette (ASGI) и Pydantic, поддерживает async/await из коробки и автоматически генерирует документацию OpenAPI (Swagger UI и ReDoc) прямо из аннотаций типов в коде. FastAPI — один из самых быстрых Python-фреймворков благодаря Starlette и uvicorn. Валидация данных через Pydantic-модели ловит ошибки ещё до обработки запроса, а type hints превращаются в живую документацию API. Dependency injection встроен в ядро фреймворка, что упрощает тестирование и переиспользование кода.</p><p>Но скорость — не всё. FastAPI требует понимания асинхронного программирования, а экосистема пока уступает Django и Flask по количеству готовых решений. Для простого CRUD-приложения с админкой Django справится быстрее; для прототипа с нестандартной архитектурой Flask даст больше свободы. О подводных камнях и ситуациях, когда FastAPI — не лучший выбор, мы подробно рассказываем в статье <a href="https://tproger.ru/articles/pochemu-ne-stoit-vybirat-fastapi-samyj-bystryj-frejmvork-na-python">«Почему не стоит выбирать FastAPI»</a> — честный разбор, который поможет принять взвешенное решение.</p><h3>Какой фреймворк выбрать</h3><ul><li>Полноценное веб-приложение с админкой и авторизацией — Django</li><li>Микросервис или прототип с полным контролем над стеком — Flask</li><li>Высокопроизводительный REST/GraphQL API с автодокументацией — FastAPI</li><li>Не уверены — начните с Django: у него самый пологий путь от нуля до продакшена</li></ul><p>На практике многие команды используют несколько фреймворков одновременно: Django для основного приложения, FastAPI для высоконагруженных микросервисов, Flask для внутренних инструментов. Python позволяет комбинировать. Общий навык для всех трёх фреймворков — понимание HTTP-протокола, REST-архитектуры, работы с базами данных и основ безопасности (CORS, CSRF, XSS, SQL-инъекции). Освоив эти концепции на одном фреймворке, вы легко перейдёте на другой.</p><h2>Python для Data Science</h2><p>Если веб-разработка — давняя территория Python, то Data Science — его новая сверхдержава. По данным <a href="https://www.jetbrains.com/lp/devecosystem-2024/python/">JetBrains Developer Ecosystem Survey</a>, Python — самый изучаемый язык программирования и основной инструмент дата-аналитиков и дата-инженеров. R, Julia, Scala — у каждого есть свои преимущества, но ни один не предлагает такую же широту экосистемы. Причина — уникальный набор библиотек, который покрывает весь pipeline работы с данными: от загрузки и очистки до визуализации и развёртывания моделей в продакшен.</p><p>NumPy — фундамент всего стека. Библиотека предоставляет многомерные массивы и математические операции, работающие на порядки быстрее чистого Python — за счёт реализации на C. Умножение матрицы 1000x1000 в NumPy выполняется за миллисекунды, в то время как наивная реализация на чистом Python займёт минуты. Pandas строится поверх NumPy и предлагает удобные таблицы (DataFrame), позволяя загружать, фильтровать, группировать и агрегировать данные в несколько строк кода. Данные из CSV, Excel, SQL, JSON — Pandas читает практически всё.</p><p>Для машинного обучения стандартом остаётся scikit-learn — библиотека с десятками алгоритмов классификации, регрессии и кластеризации. Её главная сила — единый интерфейс: fit(), predict(), score() работают одинаково для любого алгоритма, будь то случайный лес, SVM или градиентный бустинг. Это позволяет быстро сравнивать модели, менять алгоритмы одной строкой и строить пайплайны предобработки данных.</p><p>Для глубокого обучения Python предлагает PyTorch и TensorFlow — два гиганта, на которых построены GPT, Stable Diffusion и другие модели, изменившие индустрию. PyTorch доминирует в исследованиях благодаря динамическим вычислительным графам, а TensorFlow остаётся популярным в продакшене благодаря TensorFlow Serving и TFLite. Визуализация данных — matplotlib для статичных графиков, seaborn для статистических визуализаций и plotly для интерактивных дашбордов. Jupyter Notebook объединяет всё это в единую среду, где код, графики и текст живут рядом.</p><p>Разобраться в ключевых библиотеках поможет наш <a href="https://tproger.ru/translations/top-10-python-bibliotek-dlja-data-science">топ-10 Python-библиотек для Data Science</a>. А если хотите расширить инструментарий за пределы стандартного набора — загляните в подборку <a href="https://tproger.ru/translations/10-python-libraries-you-might-not-know">10 полезных библиотек, о которых вы могли не слышать</a>: там есть настоящие жемчужины для отладки, профилирования и работы с данными.</p><p>Для управления окружениями в Data Science часто используют conda (Anaconda/Miniconda) — он умеет устанавливать не только Python-пакеты, но и системные зависимости вроде CUDA для GPU-вычислений.</p><h2>Практические инструменты Python</h2><p>Помимо веб-разработки и Data Science, Python — незаменимый инструмент для повседневных задач: интеграция с внешними сервисами, извлечение данных из веб-страниц и обработка текста.</p><h3>Работа с API</h3><p>Библиотека requests — стандарт для синхронных HTTP-запросов: лаконичный API, автоматическая сериализация JSON и управление сессиями. Для асинхронных задач и HTTP/2 используйте httpx — он совместим с requests по интерфейсу, но поддерживает async/await.</p><h3>Веб-скрапинг</h3><p>Для извлечения данных из HTML-страниц Python предлагает несколько уровней инструментов. BeautifulSoup — простой парсер для статических страниц: выборка по CSS-селекторам и тегам. Scrapy — полноценный фреймворк для масштабного скрапинга с очередями, ротацией прокси и экспортом данных. Selenium и Playwright — для страниц, где контент рендерится JavaScript'ом.</p><h3>Регулярные выражения</h3><p>Модуль re — мощный инструмент для поиска и трансформации текста по шаблонам. Основные функции: re.findall() для извлечения всех совпадений, re.sub() для замены и re.compile() для предкомпиляции часто используемых паттернов.</p><p>Совет: для сложных паттернов используйте сырые строки (r"...") — они избавляют от двойного экранирования обратных слэшей. А для задач, выходящих за рамки регулярных выражений (парсинг HTML, XML), всегда предпочитайте специализированные парсеры.</p><h2>Что нового в Python 3.14</h2><p>Python 3.14 (<a href="https://docs.python.org/3/whatsnew/3.14.html">что нового</a>) (да, «пи»-релиз — разработчики не упустили возможность пошутить), выпущенный в октябре 2025 года, стал одним из самых значительных релизов за последние годы. Главные нововведения направлены на производительность — область, где Python традиционно уступал компилируемым языкам. Но 3.14 меняет правила игры.</p><p>Экспериментальный JIT-компилятор (<a href="https://peps.python.org/pep-0744/">PEP 744</a>) — пожалуй, главная новость. Он компилирует часто выполняемые участки кода (так называемые «горячие пути») в машинные инструкции прямо во время работы программы. На реальных приложениях это даёт эффект варьируется: от замедления на 10% до ускорения на 20% в зависимости от нагрузки — на некоторых задачах JIT пока замедляет код без каких-либо изменений в коде. JIT пока отключён по умолчанию (в официальных сборках для macOS и Windows JIT уже включён в бинарники — активируется переменной окружения PYTHON_JIT=1), но уже работает стабильно на большинстве платформ. Это первый шаг к тому, чтобы Python перестал считаться «медленным языком».</p><p>Внутренняя архитектура интерпретатора переработана: новый tail-call диспетчер опкодов (на уровне C, не Python-функций) даёт прирост 3—5% на стандартном benchmark suite. Важно: это не оптимизация хвостовой рекурсии в Python-коде — RecursionError при превышении глубины стека по-прежнему возможен.</p><p>T-строки (<a href="https://peps.python.org/pep-0750/">PEP 750</a>) — новый синтаксис шаблонных строк с префиксом t"...". В отличие от f-строк, T-строки не выполняют подстановку сразу, а возвращают объект Template, который можно обработать — экранировать специальные символы, валидировать параметры, преобразовать значения. Это отличное решение для безопасной генерации HTML и SQL без риска инъекций.</p><p>Среди других улучшений — ускоренные операции со словарями (до 40% быстрее в некоторых сценариях), оптимизированная работа сборщика мусора и улучшенные сообщения об ошибках, которые теперь подсказывают возможные причины проблемы. Обновлён синтаксис обработки исключений (<a href="https://peps.python.org/pep-0758/">PEP 758</a>): теперь можно писать except ValueError, TypeError: без скобок (без as — с as по-прежнему нужны скобки: except (ValueError, TypeError) as e:). Также завершён переход на отложенное вычисление аннотаций типов (<a href="https://peps.python.org/pep-0649/">PEP 649</a>/749) — аннотации больше не вычисляются при импорте модуля, что ускоряет запуск. Python продолжает развиваться, сохраняя обратную совместимость и фокус на удобстве разработчика.</p><h2>Куда двигаться дальше</h2><p>Python открывает множество карьерных путей, и выбор зависит от ваших интересов и склонностей. Вот основные направления, в каждом из которых Python — ключевой или один из основных инструментов:</p><ol><li><b>Backend-разработчик</b> — Django, Flask или FastAPI, базы данных (PostgreSQL, Redis), REST API, очереди задач (Celery), контейнеризация (Docker, Kubernetes)</li><li><b>Data Scientist / ML-инженер</b> — pandas, scikit-learn, PyTorch, работа с данными, статистика, построение и деплой моделей</li><li><b>DevOps / SRE</b> — автоматизация инфраструктуры (Ansible, Terraform), скрипты мониторинга, CI/CD пайплайны, облачные провайдеры (AWS, GCP)</li><li><b>ИИ-инженер</b> — LLM, промпт-инжиниринг, RAG-системы, fine-tuning, LangChain, работа с API нейросетей (OpenAI, Anthropic, Mistral)</li><li><b>Автоматизатор / QA-инженер</b> — Selenium, Playwright, pytest, автоматизация тестирования, нагрузочное тестирование (Locust)</li></ol><p>Независимо от выбранного направления, есть навыки, которые пригодятся везде: Git для контроля версий, SQL для работы с базами данных, Docker для контейнеризации, Linux для понимания серверной среды. Python-разработчик в 2026 году — это не просто человек, который знает синтаксис языка, а специалист, владеющий инструментарием вокруг него. Также стоит освоить виртуальные окружения (venv, poetry, uv), линтеры (ruff, mypy) и основы CI/CD — это ожидается от любого профессионального разработчика. Для управления версиями Python-проектов пригодится наш <a href="https://tproger.ru/articles/git-polnyj-putevoditel-ot-pervogo-kommita-do-prodvinutyh-wor">путеводитель по Git</a>.</p><p>Рынок труда подтверждает спрос: по данным <a href="https://hh.ru/">hh.ru</a>, количество вакансий с Python стабильно растёт на 15—20% в год, а медианная зарплата Python-разработчика уровня middle составляет 250 000—350 000 рублей в месяц. Специалисты на стыке Python и Data Science / ML зарабатывают ещё больше. Язык востребован не только в IT-компаниях: банки, телеком, ритейл, промышленность — Python нужен везде, где есть данные и автоматизация.</p><p>Отдельно стоит упомянуть растущий спрос на ИИ-инженеров. С развитием больших языковых моделей Python стал основным языком для интеграции ИИ в продукты: построение RAG-систем, создание агентов, fine-tuning моделей — всё это делается на Python. Библиотеки LangChain, LlamaIndex, Hugging Face Transformers формируют новую экосистему, которая растёт быстрее любого другого направления в IT.</p><p>Если вы в начале пути — начните со структурированной <a href="https://tproger.ru/articles/python-roadmap-2023-ljn8jvxfj">дорожной карты изучения Python</a>. Она поможет не потеряться в обилии материалов и выстроить последовательный план обучения от основ до профессионального уровня. А затем возвращайтесь к этому путеводителю — здесь вы всегда найдёте ссылки на углублённые материалы по каждой теме. Мы регулярно обновляем наши гайды, чтобы они оставались актуальными — так что добавляйте страницу в закладки.</p>]]></content:encoded>
    </item>
    <item>
      <title>PrismML выпустила 1-bit Bonsai — LLM на 8B параметров, которая весит 1 ГБ и работает на смартфоне</title>
      <link>https://tproger.ru/news/prismml-vypustila-1-bit-bonsai---llm-na-8b-parametrov--kotoraya-v</link>
      <comments>https://tproger.ru/news/prismml-vypustila-1-bit-bonsai---llm-na-8b-parametrov--kotoraya-v?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/prismml-vypustila-1-bit-bonsai---llm-na-8b-parametrov--kotoraya-v</guid>
      <description><![CDATA[<p>PrismML выпустила 1-bit Bonsai — первые коммерческие 1-битные LLM. Модель на 8B параметров занимает 1,15 ГБ, работает на iPhone и в 8 раз быстрее аналогов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/prismml-vypustila-1-bit-bonsai---llm-na-8b-parametrov--kotoraya-v">PrismML выпустила 1-bit Bonsai — LLM на 8B параметров, которая весит 1 ГБ и работает на смартфоне</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Сделано с помощью ИИ]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Apr 2026 10:41:33 GMT</pubDate>
      <content:encoded><![CDATA[<p>LLM на 8 миллиардов параметров, которая занимает 1,15 ГБ и выдаёт 368 токенов в секунду на RTX 4090 — это не опечатка.</p><p><a href="https://prismml.com/news/bonsai-8b">PrismML</a> — стартап из Калтеха, поддержанный Khosla Ventures, Cerberus и Google — <a href="https://www.prnewswire.com/news-releases/prismml-launches-worlds-first-1-bit-ai-model-to-redefine-intelligence-at-the-edge-302730568.html">представил</a> семейство 1-bit Bonsai: первые коммерчески жизнеспособные языковые модели, где каждый параметр закодирован одним битом. Модели доступны под лицензией Apache 2.0.</p><ul><li>1-bit Bonsai 8B — LLM на 8,2 млрд параметров с 1-битными весами, занимает 1,15 ГБ (в 14 раз меньше обычной 8B-модели)</li><li>На бенчмарках конкурирует с Llama 3 8B и Qwen3 8B, при этом работает в 8 раз быстрее и потребляет в 4-5 раз меньше энергии</li><li>Запускается на iPhone 17 Pro Max (44 ток/с), M4 Pro (131 ток/с), RTX 4090 (368 ток/с)</li><li>Также выпущены Bonsai 4B (~0,5 ГБ) и Bonsai 1.7B (0,24 ГБ)</li><li>Лицензия Apache 2.0, модели доступны на Hugging Face</li></ul><p>Последние годы развитие ИИ шло по пути увеличения моделей: больше параметров, больше GPU, больше энергии. Это дало мощные модели, но привязало их к дата-центрам. PrismML предлагает альтернативу — не наращивать размер, а <b>концентрировать интеллект</b>: максимум полезной работы на гигабайт модели.</p><h2>Что значит «1-битная модель»</h2><p>В стандартных LLM каждый вес (параметр) хранится в 16-битном числе с плавающей точкой (FP16). В 1-bit Bonsai 8B <b>все</b> веса — эмбеддинги, слои внимания, MLP-слои и LM head — закодированы одним битом. Нет «escape hatches» с повышенной точностью — это настоящая 1-битная модель от начала до конца.</p><p>Результат: модель с 8,2 млрд параметров занимает 1,15 ГБ вместо ~16 ГБ. Это уменьшение в 14 раз — не за счёт обрезки слоёв или дистилляции, а за счёт принципиально иного способа кодирования весов.</p><h2>Производительность: бенчмарки и скорость</h2><p>PrismML <a href="https://prismml.com/news/bonsai-8b">измерял</a> Bonsai 8B на стандартном наборе бенчмарков: IFEval, GSM8K, HumanEval+, BFCL, MuSR, MMLU-Redux.</p><ul><li>По средним оценкам бенчмарков — <b>конкурирует</b> с Llama 3 8B, Qwen3 8B и другими лидерами класса 8B</li><li>Intelligence density (плотность интеллекта, по собственной метрике PrismML): 1,06/ГБ у Bonsai против 0,10/ГБ у Qwen3 8B — разница в 10,6 раз</li><li>Скорость: 131 ток/с на M4 Pro, 368 ток/с на RTX 4090, 44 ток/с на iPhone 17 Pro Max</li><li>Энергоэффективность: 0,074 мВт·ч/токен на M4 Pro — в 4-5 раз лучше FP16-аналогов</li></ul><p>На длинных агентных задачах преимущество ещё заметнее: в демо PrismML Bonsai 8B обработал 50 тикетов за то же время, что обычная 8B-модель — 6.</p><h2>Три модели семейства</h2><p>PrismML выпустил три модели разного размера:</p><ul><li><b>1-bit Bonsai 8B</b> — 8,2 млрд параметров, 1,15 ГБ. Флагман: рассуждения, вызов функций, генерация кода</li><li><b>1-bit Bonsai 4B</b> — ~0,5 ГБ, 132 ток/с на M4 Pro. Баланс скорости и точности</li><li><b>1-bit Bonsai 1.7B</b> — 0,24 ГБ, 130 ток/с на iPhone 17 Pro Max. Ультралёгкая модель для мобильных устройств</li></ul><p>Все три модели сдвигают Парето-фронт «интеллект vs размер» влево — то есть дают больше возможностей при меньшем объёме.</p><h2>Зачем это нужно: от дата-центров к устройствам</h2><p>1-битные модели открывают класс задач, для которых облачные LLM не подходят:</p><ul><li><b>Приватность</b> — данные не покидают устройство</li><li><b>Латентность</b> — нет задержки на сетевой запрос</li><li><b>Автономность</b> — работает офлайн, без интернета</li><li><b>Стоимость</b> — не нужен облачный GPU</li><li><b>Роботика и встраиваемые системы</b> — запуск на edge-устройствах с ограниченной памятью</li></ul><blockquote>Будущее ИИ определит не тот, кто построит самый большой дата-центр, а тот, кто сможет доставить максимум интеллекта на единицу энергии и стоимости.</blockquote><h2>Перспектива: специализированное железо</h2><p>Текущие результаты получены на стандартном потребительском железе, оптимизированном для FP16-арифметики. PrismML <a href="https://prismml.com/news/bonsai-8b">отмечает</a>, что выигрыш пока идёт в основном за счёт уменьшения footprint — полноценная оптимизация 1-битного инференса на уровне железа ещё впереди.</p><p>В 1-битных моделях умножения в линейных слоях можно заменить сложениями. Специализированные чипы для 1-битного инференса могут дать ещё один порядок ускорения.</p><blockquote>Энергия стала главным узким местом для масштабирования ИИ-датацентров. PrismML фундаментально меняет уравнение «мощность/вычисления».</blockquote><h2>Как попробовать</h2><p>Модели доступны на <a href="https://huggingface.co/prism-ml/Bonsai-8B-mlx-1bit">Hugging Face</a> под лицензией Apache 2.0. Поддерживаются:</p><ul><li><b>Apple (Mac, iPhone, iPad)</b> — через MLX</li><li><b>NVIDIA GPU</b> — через llama.cpp CUDA</li><li>Whitepaper с техническими деталями обучения и оценки доступен на <a href="https://prismml.com">сайте PrismML</a></li></ul><h2>Выводы</h2><blockquote>Мы годами разрабатывали математическую теорию, необходимую для сжатия нейронной сети без потери способности к рассуждению. 1-бит — не конечная точка, а отправная.</blockquote><p>1-bit Bonsai — первая серьёзная заявка на то, что 1-битные модели могут быть не компромиссом, а полноценным продуктом. Если результаты бенчмарков подтвердятся независимыми исследователями, это изменит экономику запуска LLM: модель класса 8B, которая помещается в гигабайт и работает на смартфоне, — это другой рынок.</p><p>Скачать модели: <a href="https://huggingface.co/prism-ml/Bonsai-8B-mlx-1bit">Hugging Face</a> | Подробности: <a href="https://prismml.com/news/bonsai-8b">блог PrismML</a></p>]]></content:encoded>
    </item>
    <item>
      <title>LocalStack стал платным — MiniStack заменяет его бесплатно: 33 AWS-сервиса, реальный Postgres и 2 секунды на старт</title>
      <link>https://tproger.ru/news/localstack-stal-platnym---ministack-zamenyaet-ego-besplatno--33-a</link>
      <comments>https://tproger.ru/news/localstack-stal-platnym---ministack-zamenyaet-ego-besplatno--33-a?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/localstack-stal-platnym---ministack-zamenyaet-ego-besplatno--33-a</guid>
      <description><![CDATA[<p>MiniStack эмулирует 33 AWS-сервиса на одном порте с реальным Postgres, Redis и Docker. Запуск за 2 секунды, 30 МБ RAM, MIT-лицензия. Полная замена платного LocalStack.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/localstack-stal-platnym---ministack-zamenyaet-ego-besplatno--33-a">LocalStack стал платным — MiniStack заменяет его бесплатно: 33 AWS-сервиса, реальный Postgres и 2 секунды на старт</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 01 Apr 2026 07:15:20 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://localstack.cloud/">LocalStack</a> долгое время был стандартом для локальной эмуляции AWS-сервисов — бесплатный, удобный, с поддержкой S3, SQS, DynamoDB и десятков других API. Но в начале 2026 года базовые сервисы переехали за пейволл: даже S3 и Lambda теперь требуют платной подписки от $35/мес.</p><p>Разработчик <b>Nahuel Nucera</b> решил эту проблему: <a href="https://github.com/Nahuel990/ministack">MiniStack</a> — полностью бесплатная замена LocalStack с открытым исходным кодом, которая эмулирует 33 AWS-сервиса на одном порте. И, в отличие от LocalStack, запускает <b>настоящие</b> базы данных и контейнеры вместо мок-заглушек.</p><p><b>MiniStack</b> — бесплатная open-source замена LocalStack (MIT-лицензия)</p><p>33 AWS-сервиса на одном порте — S3, SQS, DynamoDB, Lambda, IAM, RDS, ElastiCache, ECS и другие</p><p>Реальная инфраструктура: RDS поднимает настоящий Postgres, ElastiCache — настоящий Redis, ECS — Docker-контейнеры</p><p>Запуск за 2 секунды, 30 МБ RAM в простое (vs 30 секунд и 500 МБ у LocalStack)</p><p>Drop-in совместимость с boto3, AWS CLI, Terraform, CDK, Pulumi</p><h2>Почему LocalStack стал платным</h2><p>LocalStack сменил лицензию с Apache 2.0 на <b>Business Source License</b> (BSL) и перенёс ключевые сервисы — S3, SQS, Lambda, IAM, Cognito, EC2 — в платный тир. Бесплатная версия <a href="https://localstack.cloud/pricing/">LocalStack Community</a> фактически перестала покрывать базовые сценарии локальной разработки.</p><p>Для коммерческих команд LocalStack Pro стоит от $35/мес на разработчика. Для опенсорс-проектов и индивидуальных разработчиков это создало вакуум — и MiniStack его заполнил.</p><h2>Что умеет MiniStack</h2><p>MiniStack — это один Python ASGI-сервер, который эмулирует AWS API на порте 4566. Ваши существующие boto3-клиенты, Terraform-провайдеры, CDK-стеки и AWS CLI работают без изменений — достаточно указать endpoint.</p><h3>33 поддерживаемых сервиса</h3><ul><li><b>Хранилище:</b> S3 (с версионированием, Object Lock, CORS, lifecycle), EBS, EFS</li><li><b>Очереди и события:</b> SQS (FIFO + DLQ), SNS (fanout в SQS и Lambda), EventBridge, Kinesis, Firehose</li><li><b>Базы данных:</b> DynamoDB (TTL, транзакции), RDS (реальный Postgres/MySQL), ElastiCache (реальный Redis), Athena (реальный SQL через DuckDB)</li><li><b>Вычисления:</b> Lambda (реальное исполнение Python, warm pool), ECS (реальные Docker-контейнеры), Step Functions, EMR</li><li><b>Сеть и безопасность:</b> EC2 (instances, VPC, subnets, security groups), ALB/ELBv2, Route53, Cognito, IAM, STS</li><li><b>Конфигурация:</b> SSM Parameter Store, Secrets Manager, CloudFormation, Glue Catalog</li></ul><h3>Реальная инфраструктура, а не заглушки</h3><p>Главное отличие MiniStack — он поднимает <b>настоящие</b> сервисы там, где это важнее всего:</p><ul><li><b>RDS</b> — вызов CreateDBInstance с engine=postgres запускает реальный PostgreSQL-контейнер и возвращает настоящий host:port</li><li><b>ElastiCache</b> — реальный Redis-контейнер, а не in-memory имитация</li><li><b>ECS</b> — реальные Docker-контейнеры</li><li><b>Athena</b> — реальный SQL через DuckDB (при наличии)</li></ul><p>Это принципиально отличается от подхода LocalStack, который во многих случаях использует стабы — ответы «всё ОК» без реального выполнения.</p><h2>Сравнение с LocalStack</h2><p>Ключевые метрики рядом:</p><ul><li><b>Запуск:</b> MiniStack ~2 сек vs LocalStack ~15–30 сек</li><li><b>RAM в простое:</b> MiniStack ~30 МБ vs LocalStack ~500 МБ</li><li><b>Docker-образ:</b> MiniStack 150 МБ vs LocalStack ~1 ГБ</li><li><b>Лицензия:</b> MiniStack MIT vs LocalStack BSL (ограниченная) / Proprietary ($35+/мес)</li><li><b>RDS, ElastiCache, ECS:</b> MiniStack — реальные контейнеры бесплатно vs LocalStack — только в Pro</li></ul><p>При этом MiniStack совместим с тем же API: endpoint-url, boto3, Terraform, CDK — всё работает без изменения кода.</p><h2>Как начать</h2><p>Три способа установки:</p><p>Проверка работы:</p><p>Для Python-проектов:</p><h2>Выводы</h2><p>MiniStack не претендует на полное покрытие всех AWS-сервисов — это задача LocalStack Pro. Но для типичной локальной разработки (S3 + SQS + DynamoDB + Lambda + IAM + RDS) он закрывает 90% потребностей при нулевой стоимости, мгновенном запуске и MIT-лицензии.</p><p>Проект активно развивается (947 звёзд на GitHub, версия 1.0.7) и уже привлёк внимание на <a href="https://news.ycombinator.com/item?id=47593285">Hacker News</a> (227+ баллов). Если вы искали замену LocalStack после перехода на платную модель — попробуйте.</p><p>Ссылки: <a href="https://github.com/Nahuel990/ministack">GitHub</a> | <a href="https://ministack.org/">Сайт</a> | <a href="https://pypi.org/project/ministack/">PyPI</a> | <a href="https://hub.docker.com/r/nahuelnucera/ministack">Docker Hub</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Интуиция за Pratt parsing: как работают приоритеты операторов</title>
      <link>https://tproger.ru/translations/intuiciya-za-pratt-parsing--kak-rabotayut-prioritety-operatorov</link>
      <comments>https://tproger.ru/translations/intuiciya-za-pratt-parsing--kak-rabotayut-prioritety-operatorov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/intuiciya-za-pratt-parsing--kak-rabotayut-prioritety-operatorov</guid>
      <description><![CDATA[<p>Понятное объяснение алгоритма Pratt parsing: как деревья разбора наклоняются по приоритету операторов, что такое binding power (LBP/RBP) и как написать корректный парсер за 10 строк на Python.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/intuiciya-za-pratt-parsing--kak-rabotayut-prioritety-operatorov">Интуиция за Pratt parsing: как работают приоритеты операторов</a>»</p>]]></description>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Языки программирования]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Компиляторы]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 30 Mar 2026 15:10:50 GMT</pubDate>
      <content:encoded><![CDATA[<p>Перевод статьи <a href="https://louis.co.nz/2026/03/26/pratt-parsing.html">Intuiting Pratt parsing</a>. Автор: Louis.</p><p>Вы знаете, что a + b * c + d вычисляется как a + (b * c) + d. Но как объяснить это машине? Как закодировать правила приоритета операторов так точно, чтобы парсер сам правильно строил дерево разбора?</p><p>Pratt parsing — элегантный алгоритм, придуманный Воном Праттом в 1973 году. Он лежит в основе парсеров многих современных языков, но его описания часто сводятся к магическим таблицам приоритетов без объяснения, почему это работает. Эта статья строит интуицию с нуля.</p><p>— AST — основная структура данных: оператор над операндами, вычисление снизу вверх.</p><p>— Приоритет операторов определяет наклон дерева: выше приоритет — глубже в дерево.</p><p>— При падении приоритета алгоритм поднимается по «хребту» дерева — это и есть суть Pratt parsing.</p><p>— Левая и правая ассоциативность задаются через LBP и RBP (left/right binding power).</p><p>— Полный парсер с поддержкой всех операторов — около 10 строк на Python.</p><h2>Абстрактное синтаксическое дерево</h2><p>Наиболее распространённое решение для разбора выражений — абстрактное синтаксическое дерево (AST, Abstract Syntax Tree). В AST каждый оператор стоит над своими операндами. Вычисление идёт снизу вверх: сначала обрабатываются дочерние узлы, затем операция над ними.</p><p>Для выражения a + b * c + d правильное AST выглядит так:</p><p>Узел * находится глубже, чем +, — это и отражает более высокий приоритет умножения. При вычислении сначала перемножаются b * c, результат складывается с a, затем прибавляется d.</p><h2>Как приоритет влияет на форму дерева</h2><p>Здесь кроется главная интуиция Pratt parsing. Приоритет операторов напрямую определяет, в какую сторону «наклоняется» дерево:</p><ul><li><b>Убывающий приоритет</b> (например, * + ==) — дерево наклонено влево: операторы с большим приоритетом оказываются глубже.</li><li><b>Возрастающий приоритет</b> (например, == + *) — дерево наклонено вправо.</li><li><b>Равный приоритет</b> — по соглашению левая ассоциативность, дерево наклоняется влево.</li></ul><p>Рассмотрим промежуточное состояние разбора. Пусть мы уже построили дерево I для выражения (a &gt; b + c * d) — это правонаклонное дерево: &gt; наверху, * в самом низу.</p><p>Теперь встречаем следующий оператор. Куда вставить новый узел? Зависит от приоритета:</p><ul><li><b>[I] * e</b> — приоритет * не меньше приоритета * в дереве, вставляем в самый глубокий узел.</li><li><b>[I] + e</b> — приоритет + ниже *, но выше &gt;, поднимаемся по хребту выше узла *.</li><li><b>[I] == e</b> — приоритет == ниже всего дерева, всё дерево I становится левым потомком.</li></ul><p><b>Ключевое наблюдение:</b> когда встречается оператор с меньшим приоритетом, мы поднимаемся по правому хребту дерева, собирая узлы с более высоким приоритетом. Это и есть Pratt parsing — алгоритм, который реализует именно этот подъём по хребту.</p><h2>От интуиции к коду</h2><p>Начнём с простого случая — правонаклонного дерева. Каждый оператор правоассоциативен, приоритет не учитывается:</p><p>Здесь leaf() читает следующий терминал (число или переменную), peek() смотрит на текущий токен без продвижения, advance() читает и сдвигает позицию.</p><p>Добавляем приоритет. Рекурсивный вызов передаёт приоритет текущего оператора как порог — это ограничивает, какие операторы следующий уровень может «захватить»:</p><p>Это уже работает для правоассоциативных операторов. Но у нас if — разобрали один оператор и вышли. Для левоассоциативных нужен цикл:</p><h3>Полный Pratt parser</h3><p>Замена if на while — это и есть процедура подъёма по хребту дерева. Цикл продолжается, пока следующий оператор имеет достаточно высокий приоритет, чтобы «захватить» левую часть. Как только приоритет падает — выходим, и текущее поддерево передаётся вверх по стеку вызовов.</p><h3>Правая ассоциативность через binding power</h3><p>До сих пор у нас одно число приоритета на оператор. Но для управления ассоциативностью нужно различать, насколько «сильно» оператор притягивает операнды слева и справа. Вводятся два понятия:</p><ul><li><b>LBP (left binding power)</b> — сила притяжения левого операнда.</li><li><b>RBP (right binding power)</b> — сила притяжения правого операнда.</li></ul><p>Правила ассоциативности через binding power:</p><ul><li><b>Левоассоциативные операторы</b> (+, *): LBP == RBP. Правый рекурсивный вызов получает тот же приоритет — следующий оператор с таким же приоритетом не захватывается, строится левое дерево.</li><li><b>Правоассоциативные операторы</b> (**, =): RBP = LBP − 1. Правый рекурсивный вызов получает чуть меньший приоритет — следующий оператор с таким же приоритетом захватывается, строится правое дерево.</li></ul><p>Это финальная версия Pratt parser. Всего 8 строк кода — и корректный разбор всех операторов с любым приоритетом и ассоциативностью.</p><h2>Итог</h2><p>Pratt parsing — не хитрый трюк, а следствие простой геометрической интуиции. Деревья разбора наклоняются влево или вправо в зависимости от приоритета операторов. Когда встречается оператор с меньшим приоритетом, алгоритм поднимается по правому хребту текущего дерева — ровно настолько, насколько нужно.</p><p>Ключевые детали реализации:</p><ul><li>while вместо if — реализует подъём по хребту для левоассоциативных операторов.</li><li>Передача приоритета в рекурсию — ограничивает, какие операторы «захватываются» на следующем уровне.</li><li>LBP / RBP — контролируют ассоциативность через разницу в единицу.</li></ul><p>Понимание этой геометрии делает Pratt parsing прозрачным: каждая строка кода отражает конкретное геометрическое действие с деревом, а не магию таблиц приоритетов.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что на самом деле копирует fork() — и почему ваш пул соединений ломается</title>
      <link>https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-</link>
      <comments>https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-</guid>
      <description><![CDATA[<p>fork() копирует память процесса, но разделяет файловые дескрипторы. Разбираем реальный инцидент с Django, Celery и psycopg: как один флаг сломал все воркеры.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-">Что на самом деле копирует fork() — и почему ваш пул соединений ломается</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:24:45 GMT</pubDate>
      <content:encoded><![CDATA[<p>Все воркеры <a href="https://docs.celeryq.dev/en/stable/">Celery</a> одновременно перестали получать соединения с базой. Ни один. Ни ошибок подключения, ни таймаутов запросов — просто тишина. Метрики пула показывали норму. Инфраструктура была в порядке. Но каждая задача падала через 20 секунд с одним и тем же сообщением:</p><p>Проблема оказалась не в сети, не в базе и не в нагрузке. Проблема была в одном булевом флаге, который изменил момент открытия пула соединений — и превратил fork() из безопасной операции в мину замедленного действия.</p><blockquote><b>Ключевые выводы:</b><br />— fork() копирует память процесса (copy-on-write), но разделяет файловые дескрипторы с ядром<br />— TCP-сокеты после fork() указывают на один и тот же объект ядра — два процесса пишут в один поток<br />— threading.Lock ломается: futex-ключ привязан к физическому адресу памяти, который меняется при copy-on-write<br />— Фоновые потоки пула просто не существуют в дочернем процессе — fork() копирует только вызывающий поток<br />— Правило: никогда не держите открытые соединения перед fork()</blockquote><h2>Инцидент — что случилось</h2><p>За несколько дней до инцидента в конфигурации изменился один флаг. Нужно было, чтобы обработчики сигналов <a href="https://www.djangoproject.com/">Django</a> регистрировались внутри воркеров Celery. Существующая настройка пропускала регистрацию, если флаг не был установлен в true.</p><p>Флаг установили. Сигналы заработали. Фича уехала в прод.</p><p>QA был быстрым. Изменение выглядело изолированным: переключить флаг, убедиться, что сигналы регистрируются в Celery, готово. Никто не искал побочных эффектов, спрятанных в AppConfig.ready().</p><p>А потом тихо начало выполняться кое-что ещё. Несколько методов AppConfig.ready(), которые теперь стали активны, делали ORM-запросы при старте: создавали расписания периодических задач, проверяли записи crontab. Рутинные вещи. Ничего опасного на вид.</p><p>Но эти запросы открыли пул соединений с базой данных. В мастер-процессе. <b>До fork()</b>.</p><p>Модель конкурентности Celery по умолчанию — prefork. Не потоки, не async-воркеры. Celery использует fork() — системный вызов POSIX — для создания пула рабочих процессов. Эту деталь легко забыть, когда смотришь на код приложения. Но она становится критически важной, когда открываешь соединения с базой при старте.</p><p>В этом и была проблема.</p><h2>Что копирует fork()</h2><p>Большинство разработчиков знают поверхностный ответ: fork() создаёт дочерний процесс, который является копией родительского. Но слово «копия» скрывает важное различие, которое ядро проводит между двумя типами ресурсов.</p><h3>Память — copy-on-write</h3><p>Когда fork() выполняется, ядро не дублирует RAM немедленно. Вместо этого оно помечает таблицы страниц обоих процессов как read-only, указывая на одни и те же физические страницы. Фактическое копирование происходит только когда один из процессов пишет в страницу: ядро перехватывает page fault, выделяет новую физическую страницу, копирует содержимое и обновляет таблицу страниц. До этого момента оба процесса разделяют физическую память, не зная об этом.</p><p>Это эффективно. И именно поэтому Python-объекты, включая внутреннее состояние пула соединений, выглядят целыми в дочернем процессе. Дочерний процесс получает свою копию pool._pool, pool._lock, pool._sched. Байты на месте. Структура на месте.</p><p>Но некоторые из этих байтов указывают на ресурсы ядра. А ресурсы ядра не копируются.</p><h3>Файловые дескрипторы — общие</h3><p>TCP-сокет — это не Python-объект. Это объект ядра: struct file со счётчиком ссылок, за которым стоит struct sock с буферами отправки и получения, состоянием TCP, sequence numbers. Когда fork() выполняется, ядро вызывает dup_fd() для таблицы файловых дескрипторов родителя: каждый открытый fd дублируется в дочерний процесс, а счётчик ссылок на struct file увеличивается.</p><p>Оба процесса теперь держат fd=12. Оба указывают на один и тот же объект сокета в ядре. Один TCP-поток, два читателя и два писателя — без координации между ними.</p><p>Проводной протокол <a href="https://www.postgresql.org/">PostgreSQL</a> — stateful. Он ожидает последовательные пары запрос-ответ в одном потоке. Два процесса, чередующие байты в одном соединении, не создают два независимых разговора. Они создают мусор.</p><h3>Блокировки — сломанный futex</h3><p>threading.Lock построен на pthread_mutex_t, который внутри опирается на futex — механизм ядра Linux, использующий <i>физический адрес памяти</i> целого числа как ключ для очереди ожидания. После fork() copy-on-write может переместить страницу дочернего процесса на новый физический адрес при записи. Futex-ключ дочернего процесса расходится с родительским. futex_wake из одного процесса не будит никого в другом.</p><p>POSIX явно говорит об этом: поведение мьютекса после fork() не определено, если он не был создан с атрибутом process-shared. Python-овский Lock этот атрибут не использует.</p><h3>Фоновые потоки — просто не существуют</h3><p>fork() дублирует только вызывающий поток. Внутренний планировщик пула — отвечающий за поддержание минимального числа соединений, проверки здоровья, уведомление ожидающих — копируется как Python-объект, но не имеет соответствующего потока ОС. Его TID не существует. Любой путь кода, который зависит от его ожидания или сигнализации, заблокируется навсегда.</p><p>Дочерние процессы унаследовали пул, который выглядел целым, но был мёртв. Они вызывали pool.getconn(), пытались захватить сломанный лок, ждали 20 секунд notify, который никогда не придёт, и получали таймаут.</p><h2>Почему пул соединений ломается после fork()</h2><p>Пул соединений <a href="https://www.psycopg.org/psycopg3/docs/">psycopg</a> (ConnectionPool) хранит три вида ресурсов. Каждый ломается по-своему после fork().</p><p><b>TCP-сокеты</b> разделяются на уровне ядра. Любая попытка использовать их из двух процессов одновременно разрушает поток протокола. На практике дочерние процессы до этого даже не доходили.</p><p><b>threading.Lock</b> опирается на futex, привязанный к физическому адресу. После copy-on-write ключ дрейфует. futex_wake будит очередь по старому адресу — никого в дочернем процессе там нет.</p><p><b>Фоновые потоки</b> не существуют в дочернем процессе. Планировщик пула — Python-объект без ОС-потока. Код, зависящий от его notify, блокируется навечно.</p><p>Вот почему раньше всё работало:</p><p>До изменения флага AppConfig.ready() пропускал регистрацию периодических задач. ORM не вызывался при старте. Нет запросов — нет пула. Нет пула — нечего наследовать. Каждый дочерний процесс начинал с чистого состояния и лениво создавал собственный пул при первом обращении к базе: собственные сокеты, собственный лок, собственный фоновый поток.</p><h2>Решение — чистый fork без наследства</h2><p>Два хука сигналов Celery.</p><p>worker_before_create_process срабатывает в родительском процессе после ready(), перед каждым fork(). Он закрывает все соединения с базой по всем алиасам и уничтожает пулы соединений. К моменту выполнения fork() нет открытых TCP-сокетов, нет локов, нет фоновых потоков для наследования.</p><p>worker_process_init срабатывает в каждом дочернем процессе после fork() как второй уровень защиты — defense-in-depth, на случай если что-то было пропущено.</p><p>После обоих хуков каждый дочерний процесс пуст. Первый ORM-запрос создаёт свежий пул, который целиком принадлежит этому процессу.</p><p>Принцип простой: никогда не держите открытые соединения перед fork() — или явно закройте и уничтожьте их до форка. Это правило применимо к любому пулу соединений на основе TCP: <a href="https://www.psycopg.org/psycopg3/docs/">psycopg</a>, <a href="https://www.sqlalchemy.org/">SQLAlchemy</a>, redis-py. Технология не важна. Важно поведение ядра.</p><h2>Диаграммы — до и после</h2><h3>До изменения флага: чистый fork, каждый воркер создаёт свой пул</h3><h3>После изменения флага: пул открыт в мастере, fork() распространяет повреждение</h3><h3>После исправления: пул уничтожен перед fork(), каждый воркер стартует чисто</h3><h2>Выводы</h2><p>Настоящий урок — не в конкретном фиксе. Он в понимании того, почему пул сломался. Что fork() на самом деле копирует. Что он разделяет. Почему лок, который выглядит целым, может заблокировать дочерний процесс. Почему поток, существующий как Python-объект, может полностью отсутствовать в ОС.</p><p>Правила, которые стоит запомнить:</p><ul><li>Никогда не открывайте пул соединений до fork() — или явно уничтожьте его перед форком</li><li>fork() копирует память (лениво), но разделяет файловые дескрипторы на уровне ядра</li><li>threading.Lock после fork() — undefined behavior: futex привязан к физическому адресу, который дрейфует при copy-on-write</li><li>Фоновые потоки не дублируются — fork() копирует только вызывающий поток</li><li>Это правило касается любого пула TCP-соединений: psycopg, SQLAlchemy, redis-py — поведение ядра одинаково</li></ul><p>Невидимая часть — это взаимодействие между последовательностью запуска Django и моделью процессов Celery. Две системы, каждая из которых хорошо понятна по отдельности, делают неожиданное на стыке.</p><p><i>Адаптированный перевод статьи <a href="https://tech.daniellbastos.com.br/posts/what-fork-actually-copies/">What fork() Actually Copies</a> Даниэля Бастоса (Daniel Bastos).</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Пропуск 90% работы по деквантизации ускорил декодирование LLM на 22,8%</title>
      <link>https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na</link>
      <comments>https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na</guid>
      <description><![CDATA[<p>Разработчик ускорил декодирование LLM на 22,8% тремя строками кода: sparse V dequantization пропускает деквантизацию KV-кеша для позиций с малым весом внимания.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/propusk-90--raboty-po-dekvantizacii-uskoril-dekodirovanie-llm-na">Пропуск 90% работы по деквантизации ускорил декодирование LLM на 22,8%</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:53:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Три строчки кода в ядре flash attention — и декодирование большой языковой модели ускоряется на 22,8%. Без потери качества, без переобучения, без изменений в архитектуре модели.</p><p>Независимый разработчик <a href="https://github.com/TheTom">Tom Turney</a> опубликовал метод sparse V dequantization — оптимизацию для инференса LLM с квантизированным KV-кешем. Вместо того чтобы ускорять деквантизацию каждого значения, он предложил пропускать деквантизацию целиком для позиций, где вес внимания пренебрежимо мал. На Apple M5 Max с моделью Qwen 3.5 35B (MoE) метод дал прирост +22,8% к скорости декодирования на контексте 32K токенов. Перплексия осталась неизменной, а точность поиска needle-in-a-haystack выросла с 7/9 до 9/9.</p><blockquote><b>Ключевые выводы</b><br /><br />— Узкое место квантизированного KV-кеша — не то, <i>как</i> вы деквантизуете, а то, <i>сколько</i> значений вы деквантизуете.<br />— При длинном контексте (32K+) более 90% весов внимания пренебрежимо малы — их деквантизацию можно пропустить.<br />— Метод работает с любой схемой квантизации KV-кеша: TurboQuant, q8_0, q4_0, NVFP4.<br />— Реализация — 3 строки в ядре flash attention. Не нужно ни переобучение, ни калибровочные данные.<br />— NIAH-тест улучшился: 9/9 вместо 7/9. Пропуск деквантизации убирает квантизационный шум от позиций, которые ничего не вносят в результат.</blockquote><h2>Проблема — узкое место деквантизации</h2><p>Чтобы понять суть открытия, нужно разобраться в контексте. Современные LLM при генерации текста хранят промежуточные вычисления в так называемом <b>KV-кеше</b> (key-value cache). Это массив ключей (K) и значений (V), которые модель использует в механизме внимания — чтобы при генерации каждого нового токена не пересчитывать всю историю диалога.</p><p>Проблема: KV-кеш растёт линейно с длиной контекста и при 32K токенов занимает гигабайты памяти. Решение — <b>квантизация</b>: сжатие значений из 16-битного формата в 3-4 бита. <a href="https://research.google/blog/turboquant-redefining-ai-efficiency-with-extreme-compression/">TurboQuant</a> от Google (ICLR 2026) сжимает KV-кеш в 4,6 раза с потерей перплексии всего ~1%. Но при декодировании каждый сжатый блок нужно <b>деквантизовать</b> — преобразовать обратно в числа с плавающей точкой. И на длинных контекстах это становится узким местом.</p><p>На Apple M5 Max деквантизация TurboQuant занимает 14-34% времени декодирования. На контексте 32K токенов скорость декодирования падает с 78,3 tok/s (потолок без деквантизации) до 47,0 tok/s — штраф в 40%.</p><h3>14 попыток, которые провалились</h3><p>Прежде чем найти решение, Turney перебрал 14 альтернативных реализаций деквантизации на Apple Silicon: регистровые массивы, битовую арифметику, SIMD-шаффлы, FMA без ветвлений, слитые блочные операции. Ни одна не обогнала базовую таблицу подстановки (LUT) в константной памяти GPU.</p><p>Вывод оказался фундаментальным: на Apple Silicon 4 дивергентных чтения из константной памяти быстрее любой арифметики, которая даёт тот же результат. Это аппаратный пол — ниже него не опуститься. Единственный оставшийся рычаг — уменьшить количество позиций, которые нужно деквантизовать.</p><h2>Решение — не ускорять, а пропускать</h2><p>Ключевое наблюдение: в flash attention ядре веса внимания (softmax) вычисляются из ключей (K) <i>до того</i>, как начинается работа со значениями (V). То есть на момент деквантизации V мы уже знаем, какие позиции контекста получили значимый вес внимания, а какие — нет.</p><p><b>Что такое разреженность внимания?</b> Когда модель генерирует следующий токен, механизм внимания (softmax) распределяет вероятность по всем предыдущим позициям контекста. При коротком контексте внимание распределено относительно равномерно. Но чем длиннее контекст, тем более концентрированным оно становится: модель «смотрит» на несколько десятков ключевых позиций, а 90%+ получают веса ниже 10-6 — они практически ничего не вносят в итоговый результат.</p><p>Sparse V dequantization использует этот факт: если вес внимания для позиции ниже порога (10-6), её деквантизация и аккумуляция пропускаются целиком.</p><p>Вся реализация — три строки в Metal-ядре:</p><p>Это экономит чтения из константной памяти (обращения к LUT), ALU-операции (извлечение индексов, применение знака, умножение на норму) и чтения из глобальной памяти устройства.</p><p>Принципиальное отличие от 14 провалившихся подходов: они пытались сделать каждую из N операций быстрее (ограничено аппаратным полом). Sparse V убирает (1-p) x N операций целиком — и выигрыш растёт с длиной контекста, потому что разреженность внимания растёт.</p><h2>Результаты</h2><h3>Скорость декодирования (M5 Max, Qwen 3.5 35B MoE)</h3><p>Эффект sparse V масштабируется с длиной контекста — чем длиннее контекст, тем больше позиций пропускается:</p><ul><li>Короткий контекст: +1,4% (внимание плотное, мало что пропускать)</li><li>4K: +4,0%</li><li>8K: +7,2%</li><li>16K: +12,9%</li><li>32K: <b>+22,8%</b> (с 47,0 до 57,7 tok/s)</li></ul><p>При 32K токенов отношение к несжатому q8_0 улучшилось с 0,76x до 0,93x — почти паритет.</p><h3>Качество: перплексия и retrieval</h3><p>Перплексия при включении sparse V остаётся <b>численно идентичной</b> варианту без него. На корпусе wikitext-103 (50 чанков, контекст 32K, CI +/-0.021) дельта составила ровно 0,0000. Это подтверждено на всех протестированных длинах контекста: 8K, 16K, 32K.</p><p>Ещё более неожиданный результат — в тесте needle-in-a-haystack (NIAH). Этот тест проверяет, может ли модель найти конкретный факт, спрятанный в длинном контексте:</p><ul><li>Без sparse V: 7/9 (q8_0 тоже 7/9)</li><li>С sparse V: <b>9/9 (100%)</b></li></ul><p>Гипотеза автора: позиции с пренебрежимо малым весом внимания вносят практически нулевой полезный сигнал, но при деквантизации добавляют квантизационный шум. Sparse V убирает этот шум — и соотношение сигнал/шум улучшается.</p><h2>Почему это работает для любой квантизации</h2><p>Sparse V работает не на уровне конкретного формата квантизации, а на уровне <b>механизма внимания</b>. Метод использует веса softmax как сигнал для пропуска вычислений — а softmax одинаков вне зависимости от того, как именно сжаты K и V.</p><p>Автор валидировал метод на трёх форматах KV-кеша с разной стоимостью деквантизации:</p><ul><li><b>turbo3</b> (3,5 бита, TurboQuant) — +22,8% на 32K, перплексия без изменений</li><li><b>q8_0</b> (8 бит) — +5% к декодированию, перплексия идентична</li><li><b>q4_0</b> (4 бита) — перплексия идентична, скорость в пределах шума</li></ul><p>Закономерность: чем дороже деквантизация, тем больше выигрыш. turbo3 использует Walsh-Hadamard-преобразование и полярную декомпозицию — тяжёлые операции, поэтому выигрыш максимален. q4_0 использует простое масштабирование — пропуск дешёвой операции даёт мало. Но в обоих случаях метод <b>не ухудшает</b> ни перплексию, ни retrieval.</p><p>Это означает, что sparse V можно включить по умолчанию для любого формата квантизации KV-кеша в <a href="https://github.com/ggerganov/llama.cpp">llama.cpp</a> — как безусловную оптимизацию, которая ничего не стоит на коротком контексте и даёт ощутимый выигрыш на длинном.</p><h2>FAQ</h2><h3>Не сломает ли пропуск позиций качество генерации?</h3><p>Нет. Порог 10-6 выбран консервативно. При контексте 32K с 32 головами внимания вклад пропущенной позиции в итоговый вектор — менее одной миллионной от максимума. Перплексия во всех экспериментах осталась численно идентичной — дельта 0,0000 на 50 чанках wikitext-103.</p><h3>Почему нельзя пропускать и K-деквантизацию?</h3><p>Потому что K нужен для вычисления весов внимания. Чтобы узнать, какие позиции имеют пренебрежимо малый вес, нужно сначала деквантизовать все ключи и посчитать softmax. Автор отмечает это как направление будущей работы: двухпроходный механизм, где первый проход вычисляет приблизительные веса для фильтрации K-позиций.</p><h3>Работает ли это на NVIDIA GPU?</h3><p>Эксперименты проводились на Apple Silicon (M5 Max, M2 Pro). Реализация — Metal-ядро для llama.cpp. На CUDA профиль узкого места другой (NVIDIA использует тензорные ядра и HBM вместо unified memory), но сам принцип — пропуск работы для позиций с малым весом внимания — универсален. Портирование на CUDA и ROCm автор указывает как следующий шаг.</p><h2>Выводы</h2><p>Sparse V dequantization — элегантное решение, которое меняет вопрос с «как деквантизовать быстрее?» на «нужно ли деквантизовать вообще?». Метод использует информацию, которая уже есть в ядре flash attention — веса softmax — как бесплатный сигнал для пропуска вычислений.</p><p>Главная ценность подхода — не конкретный прирост в 22,8%, а более широкая идея <b>attention-aware computation</b>: использование собственных паттернов разреженности модели для оптимизации ядер инференса. Чем длиннее контекст, тем сильнее разреженность — и тем больше выигрыш. Именно при тех длинах контекста, где узкое место деквантизации наиболее болезненно.</p><p>Код и бенчмарки: <a href="https://github.com/TheTom/turboquant_plus">TheTom/turboquant_plus</a> (бенчмарки и диагностика), <a href="https://github.com/TheTom/llama-cpp-turboquant">TheTom/llama-cpp-turboquant</a> (реализация, ветка experimental_decode_speed_tests).</p><p>Обсуждение на <a href="https://www.reddit.com/r/LocalLLaMA/">r/LocalLLaMA</a>.</p><p><i>Источник: <a href="https://github.com/TheTom/turboquant_plus/blob/main/docs/papers/sparse-v-dequant.md">Sparse V Dequantization</a> — документация turboquant_plus.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</title>
      <link>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</link>
      <comments>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</guid>
      <description><![CDATA[<p>30 лучших уроков из 113 по масштабированию Django-монолита до 1 000 000 строк: архитектура, тестирование, мониторинг, безопасность, деплой и карьерный рост.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto">Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Что можно узнать из монолита на миллион строк кода? Джек Кинселла проработал 7 лет техлидом и CTO в <a href="https://www.morphmarket.com/">MorphMarket</a> — маркетплейсе на стеке <a href="https://www.djangoproject.com/">Django</a> / <a href="https://react.dev/">React</a> (TS) / React Native с командой из 20 человек. За это время кодовая база выросла до 1 000 000 строк, а Джек прошёл путь от первого инженера до технического директора.</p><p>Он опубликовал 113 уроков, которые вынес из этого опыта. Мы отобрали самые ценные, сгруппировали по темам и адаптировали для русскоязычной аудитории. Каждый урок — практический: его можно взять и применить в своём проекте уже сегодня.</p><blockquote><b>Ключевые выводы</b><br /><br />— Кэширование — последняя мера. Сначала исправьте модель данных, индексы и N+1-запросы.<br />— E2E-тесты на happy path дают в 10 раз больше отдачи, чем юнит-тесты на отдельные методы.<br />— Доведите Sentry до inbox zero — это единственный самый мощный шаг для качества продукта.<br />— Не обвиняйте конкретного разработчика за баг в проде: это всегда каскад из 3+ ошибок системы.<br />— Деплой за 2 минуты вместо 28 — достижимо, и это меняет культуру принятия решений.</blockquote><h2>Про архитектуру и масштабирование</h2><h3>Используйте сервисные объекты вместо раздувания моделей</h3><p>В фреймворках, где модели строятся вокруг существительных (Product, User, Auction), логика неизбежно скапливается в этих классах. Со временем каждый из них превращается в монстра на тысячи строк. Решение — выносить процессы в отдельные сервисные объекты: PlaceAuctionBidService, ProductBillingService. Суть — перестать мыслить существительными и начать мыслить глаголами.</p><h3>Одна и та же вещь, реализованная дважды — провал технического лидерства</h3><p>Если в кодовой базе есть два разных клиента к одному платёжному провайдеру, три файла с функциями для работы с датами или дублирующиеся UI-компоненты — это симптом отсутствия ответственного за архитектуру. Создавайте очевидные, легко находимые места для общего кода (common/datetime.py, common/geo.py) и отмечайте переиспользуемый код на ревью.</p><h3>Полиморфные связи в БД — почти всегда ошибка</h3><p>ORM-код для полиморфных отношений становится крайне запутанным, и вы теряете защиту через foreign key constraints. Проще добавить nullable FK для каждой связанной модели и написать абстракции на уровне запросов.</p><h3>Дефолтные фильтры и сортировки в ORM — всегда ошибка</h3><p>Скрытые дефолты вроде order_by('-id') или filter(is_deleted=False) становятся миной замедленного действия, когда в команде 20+ человек. Новый разработчик не знает про неявное поведение — и получает баг. А скрытый ORDER BY портит аналитические запросы, потому что ORM добавляет ключ сортировки в SELECT.</p><h3>Вся инфраструктура — в коде</h3><p>Когда Джек пришёл в MorphMarket, все 27 крон-задач были настроены вручную на продакшн-сервере. Большинство отсутствовали на staging. Перенос в код (а затем конфигурация AWS/Heroku/CloudFlare/Sentry через <a href="https://www.terraform.io/">Terraform</a>) сделал все окружения похожими друг на друга — и, что важно, сделал инфраструктуру доступной для чтения AI-агентам.</p><h2>Про тестирование</h2><h3>E2E &gt; интеграционные &gt; юнит-тесты</h3><p>Пользователям важно, чтобы система работала целиком. Один E2E-тест на happy path каждой ключевой фичи (<i>регистрация, покупка, создание листинга</i>) даёт на порядок больше уверенности, чем десятки юнит-тестов. Детали дорабатывайте интеграционными и юнит-тестами — они быстрее и проще в поддержке.</p><h3>Автоматические тесты для шаблонных фич</h3><p>В MorphMarket написали систему, которая автоматически тестирует около 300 админ-страниц: парсит имя страницы, находит фабрику, создаёт запись в БД и проверяет, что страница открывается. Тот же подход можно применить к любому CRUD-эндпоинту без выделенного теста.</p><h3>Изолируйте тесты полностью</h3><p>Хрупкость тестов убивает мотивацию команды. Основные ловушки:</p><ul><li>Всё, что связано со временем — замораживайте или мокайте</li><li>Фиксируйте seed для всех источников случайности — в каждой библиотеке</li><li>Запретите реальные HTTP-запросы в не-E2E тестах (используйте VCR-подобные библиотеки)</li><li>Отдельный Redis для тестов с автоочисткой перед каждым тестом</li><li>Тестовая среда не должна читать ни одной переменной из вашего .env</li></ul><h3>Мокайте только внешние объекты</h3><p>Мок — полезный инструмент, но только для внешних зависимостей. Мокать внутренние методы тестируемого класса — антипаттерн. Если можно переписать тест без мока без потери скорости — так и сделайте.</p><h3>CI должен проверять не только ошибки, но и раздражающие мелочи</h3><p>В MorphMarket CI также ловит новые warning-и в тестах, рассинхронизацию SDK с бэкендом и забытые миграции в Django. Чем раньше замечена мелочь, тем дешевле её исправить.</p><h2>Про отладку и мониторинг</h2><h3>Sentry до inbox zero — самый мощный шаг к качеству</h3><p>Когда Джек пришёл в компанию, Sentry показывал 1 000 ошибок в час. После агрессивной фильтрации (игнор старых браузеров, расширений, пометка JavaScript-ошибок уникальным идентификатором) и починки реальных багов — меньше 1 ошибки в час. Результат: каждая новая ошибка стала <i>событием</i>, на которое команда реагирует немедленно.</p><h3>Dead Man Switch — мониторьте то, что должно происходить</h3><p>Исключения шумят и легко ловятся. А вот отсутствие события — нет. Если крон-система перестала работать или бэкапы БД не снимаются — вы узнаете об этом только через heartbeat-мониторы. Настройте их.</p><h3>Username в каждой строке лога</h3><p>По умолчанию в логах — только IP-адрес. Но IP ротируется, создаёт лишнее перенаправление при дебаге и не показывает картину при использовании нескольких устройств. Поместите текущий запрос в thread-local и добавьте username в каждую запись.</p><h3>Трипвайр-алерты на 20 самых важных эндпоинтов</h3><p>Деградация производительности подкрадывается незаметно — инкрементальные фичи постепенно ломают индексы и кэши. Установите порог в несколько стандартных отклонений от нормы на ключевых эндпоинтах. Команда узнает о проблеме в течение часов после деплоя, а не через неделю жалоб.</p><h3>Громко падайте при ошибках</h3><p>Не глушите исключения except: pass. Паттерн от Джека: <i>крашиться локально, мягко отказывать в проде — но всегда отправлять ошибку в Sentry/логи</i>. Чем раньше заметили — тем меньше ущерб.</p><h2>Про работу в команде</h2><h3>Не обвиняйте одного человека за баг в проде</h3><p>Любой инцидент — это каскад минимум трёх провалов: автор не заметил, ревьюер пропустил, QA не покрыло, тесты не проверили, архитектура допустила класс ошибки. Обвинение одного человека мешает команде видеть системные причины.</p><h3>Все разработчики должны быть немного фулстек</h3><p>В MorphMarket убрали строгое деление на фронтенд/бэкенд/ops. Переобучение заняло год, но окупилось: фронтендер добавляет поле в БД сам, а не ждёт бэкендера — это сокращает время доставки фич.</p><h3>Пятницы технического долга</h3><p>Разумный компромисс между CEO, который хочет фичи, и инженерами, которым нужна чистота кода: с понедельника по четверг приоритеты задаёт продукт, а по пятницам — CTO. Бонус: инженеры отдыхают на глубоких задачах перед выходными.</p><h3>Запретите длинные PR</h3><p>В MorphMarket был период, когда PR висели по 3 месяца и накапливали тысячи изменений. Их невозможно ревьюить, они конфликтуют с master и создают огромный деплой-риск. Новое правило: мерж в master каждые 1–2 дня, максимум — раз в неделю. Побочный эффект: старшие разработчики дают фидбэк раньше, и драматические переписывания случаются реже.</p><h3>Поощряйте общение в удалённых командах</h3><p>Джек начал с ежедневных постов о прогрессе, потом стал публично задавать вопросы (даже на которые знал ответ), делиться изменениями в коде и просить мнения. Это постепенно снизило барьер и сделало общение нормой, а не исключением.</p><h2>Про производительность</h2><h3>N+1-запросы — враг номер один в ORM</h3><p>Проблема проявляется на крупных эндпоинтах, которые постепенно расширяют разные авторы: новые фильтры и параметры создают спорадические N+1, которые возникают только при определённом сочетании фильтров и объёме данных. Настройте APM (например, <a href="https://newrelic.com/">New Relic</a>) для автоматического обнаружения N+1 и обучите команду ловить их на ревью.</p><h3>Кэширование — последняя мера</h3><p>Код кэширования чудовищно сложен в поддержке. Сначала исправьте модель данных, допишите индексы, уберите N+1, оптимизируйте алгоритмы. И только после этого — кэш. Джек рассказывает о случаях, когда наивный кэш работал на малых объёмах, но убивал Redis (он однопоточный!) при росте данных на два порядка.</p><h3>Таймауты на всех уровнях — иммунная система сервиса</h3><p>Веб-запрос должен завершиться за 25 секунд — иначе убивается. Да, для длинных задач придётся использовать фоновые задачи, но без таймаутов одна тормозящая внешняя API может съесть все потоки сервера и уронить всё.</p><h3>Выгружайте работу в фоновые задачи</h3><p>Отправка email, push-уведомления, обработка вебхуков от платёжных систем — всё это должно уходить в очередь. Веб-процесс обязан оставаться быстрым.</p><h3>ORDER BY id вместо ORDER BY created</h3><p>Если поле created неизменяемо и совпадает по порядку с id — сортируйте по id. Индекс на id уже есть бесплатно в каждой таблице.</p><h3>Удаляйте ненужные данные</h3><p>В MorphMarket некоторые таблицы копили 10 лет данных — логи IP-адресов, записи push-уведомлений, сообщения девятилетней давности. Скрипт очистки делает эти таблицы компактнее и быстрее.</p><h2>Про безопасность и надёжность</h2><h3>Начните с модели угроз: что может получить атакующий?</h3><p>Для MorphMarket основные вектора: захват аккаунтов (фикс — обязательный 2FA), спам-фишинг (капча на регистрации + фильтры сообщений), DDoS (<a href="https://www.cloudflare.com/">Cloudflare</a> + rate limit на nginx + кэш ключевых эндпоинтов). Универсального подхода нет — каждый сервис должен понимать свою специфику.</p><h3>CHECK-ограничения в БД — лучше предотвратить, чем чинить</h3><p>Раньше в MorphMarket крон-задача раз в сутки проверяла целостность данных и слала алерты. Всё заменили на CHECK constraints на уровне БД — некоторые проверяют до 16 операций над разными колонками. Невалидные данные просто не могут быть записаны.</p><h3>Вебхуки — рассадник race conditions</h3><p>Действие в коде может вызвать вебхук, который прилетит быстрее, чем данные запишутся в БД. Получатель вебхука прочитает устаревшее состояние и может затереть свежие данные. Решение: откладывайте действия, вызывающие вебхуки, до on_commit в БД.</p><h3>Логируйте намерение перед побочным эффектом — особенно для платежей</h3><p>Перед списанием с карты или возвратом пишите в лог: <i>"bill user XYZ $100 for Y"</i>. Простой Write-Ahead Log спасал MorphMarket в нескольких инцидентах.</p><h2>Про деплой</h2><h3>Деплой должен быть быстрым — действительно быстрым</h3><p>MorphMarket сократил деплой с 28 минут до 2. Как: разделили фронтенд и бэкенд, распараллелили, перешли на Rust-based компиляцию JS, вынесли сборку на мощные машины (MacBook M-series вместо commodity Heroku), убрали из слага всё ненужное, почистили старые ассеты из S3.</p><h3>Smoke-тест после каждого деплоя в прод</h3><p>Автоматическая проверка ключевых страниц через <a href="https://playwright.dev/">Playwright</a> после каждого деплоя. Если последние 150 деплоев были скучными — соблазн перестать проверять вручную огромен. Автоматический smoke-тест не устаёт и не теряет бдительность.</p><h3>Не деплойте ничего рискованного в пятницу</h3><p>Баг может проявиться вечером или в субботу, когда никого нет. Мелкие хотфиксы и изменения в админке — можно. Всё остальное — до четверга.</p><h3>Плейбук для инцидентов при деплое</h3><p>Короткие инструкции: как убить залипшие соединения к БД, точечно почистить кэш (без flush, который разлогинит всех), форсировать обновление ассетов. Обученная команда сокращает даунтайм в разы.</p><h2>Про карьерный рост — от тимлида до CTO</h2><h3>Научитесь говорить о производительности сотрудников</h3><p>Джек признаётся, что панически боялся этих разговоров, потому что не любит конфликты. Но реальность проста: лучше открыто сказать человеку, где он не дотягивает, чем копить раздражение и уволить неожиданно. Большинство людей способны расти под менторингом — Джек это недооценивал.</p><h3>Документ ожиданий от сотрудника</h3><p>Если написать <i>"мы ценим, когда вы решаете проблемы в #bug-discussion до того, как они дойдут до CTO"</i>, люди действительно начнут так делать. Мотивированный сотрудник хочет ясности. Он не может прочитать ваши мысли.</p><h3>Одно крупное изменение за раз</h3><p>Внедряете E2E-тесты? Доведите до стабильности, прежде чем браться за линтеры. Если система сделана плохо — команда не будет ей доверять и не будет использовать. Плюс есть предел того, сколько изменений люди могут усвоить одновременно.</p><h3>CLI-инструменты + AI = максимальный рычаг</h3><p>AI отлично работает с текстом. CLI-инструменты тоже. Дайте AI доступ к продакшн-логам, отчётам об ошибках, read-only реплике БД, CI-результатам и REPL — и получите огромный множитель продуктивности. Джек называет это <i>"наблюдаемость через CLI — ваш главный усилитель для AI"</i>.</p><h2>FAQ</h2><h3>Почему монолит, а не микросервисы?</h3><p>Монолит на миллион строк с 20 разработчиками — рабочая модель, если соблюдать дисциплину. Микросервисы добавляют сетевую сложность, и для команды такого размера overhead координации перевешивает выгоды. Джек не утверждает, что микросервисы плохи — он утверждает, что переход оправдан только при конкретной боли, а не как дефолтный выбор.</p><h3>С чего начать, если в проекте уже бардак?</h3><p>С Sentry до inbox zero и таймаутов на веб-запросы. Первое даёт видимость реальных проблем, второе предотвращает каскадные отказы. Дальше — по статье: N+1, изоляция тестов, сервисные объекты.</p><h3>Насколько эти уроки применимы за пределами Django?</h3><p>Джек 17 лет работал с Rails, Laravel, Express и другими фреймворками — и подчёркивает, что большинство уроков переносимы. N+1, дефолтные сортировки, race conditions в вебхуках, инфраструктура как код — всё это не привязано к конкретному стеку.</p><h3>Правда ли, что E2E-тесты лучше юнит-тестов?</h3><p>Не <i>лучше</i>, а дают больше отдачи на первом этапе. Один E2E-тест на happy path покрывает весь стек. Юнит-тесты нужны для деталей и граничных случаев, но начинать покрытие лучше сверху вниз.</p><h2>Выводы</h2><p>113 уроков Джека Кинселлы — это не теория из книг, а выжимка из 7 лет ежедневной работы над монолитом, который обслуживает реальных пользователей. Главные принципы:</p><ul><li>Предотвращайте, а не чините — CHECK constraints, таймауты, линтеры</li><li>Делайте невидимое видимым — логи, мониторинг, Sentry до inbox zero</li><li>Инвестируйте в скорость обратной связи — быстрый деплой, быстрый CI, короткие PR</li><li>Доверяйте людям и системе, а не контролю — fullstack-навыки, плейбуки, документ ожиданий</li></ul><p>Оригинальная статья: <a href="https://www.semicolonandsons.com/articles/scaling-a-monolith-to-1m-loc-113-pragmatic-lessons-from-tech-lead-to-cto">Scaling a Monolith to 1M LOC: 113 Pragmatic Lessons from Tech Lead to CTO</a> (Semicolon &amp; Sons, Jack Kinsella).</p><p>Если знаете коллегу, который борется с растущим монолитом — отправьте ему эту статью. А в комментариях расскажите, какой урок оказался самым неожиданным для вас.</p>]]></content:encoded>
    </item>
    <item>
      <title>Волна supply chain атак на PyPI: разбираем LiteLLM, telnyx и Trivy</title>
      <link>https://tproger.ru/news/volna-supply-chain-atak-na-pypi--razbiraem-litellm--telnyx-i-tri</link>
      <comments>https://tproger.ru/news/volna-supply-chain-atak-na-pypi--razbiraem-litellm--telnyx-i-tri?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/volna-supply-chain-atak-na-pypi--razbiraem-litellm--telnyx-i-tri</guid>
      <description><![CDATA[<p>Разбор волны атак на PyPI в марте 2026: как группировка TeamPCP скомпрометировала LiteLLM, telnyx и Trivy через украденные токены. Анализ, IoC и защита.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/volna-supply-chain-atak-na-pypi--razbiraem-litellm--telnyx-i-tri">Волна supply chain атак на PyPI: разбираем LiteLLM, telnyx и Trivy</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Кибербезопасность]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:06 GMT</pubDate>
      <content:encoded><![CDATA[<p>Представьте: вы запускаете pip install для библиотеки, которую используете каждый день. Она прошла миллионы загрузок, стоит в зависимостях десятков ваших проектов. Но вот именно эта версия — уже не та библиотека. Кто-то подменил пакет, и при следующем import ваши SSH-ключи, токены AWS и пароли от баз данных утекают на сервер злоумышленников.</p><p>Именно это произошло на неделе 24–28 марта 2026 года. Три популярных пакета на <a href="https://pypi.org/">PyPI</a> — <a href="https://github.com/BerriAI/litellm">LiteLLM</a>, <a href="https://github.com/team-telnyx/telnyx-python">telnyx</a> и <a href="https://github.com/aquasecurity/trivy">Trivy</a> — оказались скомпрометированы одной и той же группировкой. Разбираемся, как это работает, чем грозит и как защититься.</p><p><b>Supply chain атака</b> (атака на цепочку поставок) — это тип кибератаки, при котором злоумышленник внедряет вредоносный код не напрямую в вашу систему, а через доверенную зависимость: библиотеку, инструмент или сервис, который вы уже используете. Вместо того чтобы взламывать ваш сервер, атакующий компрометирует то, что вы сами устанавливаете. Это особенно опасно, потому что такие пакеты проходят через все ваши проверки — они же «легитимные».</p><blockquote><b>Ключевые выводы</b><br /><br />— За одну неделю скомпрометированы три крупных пакета: <a href="https://pypi.org/project/litellm/">litellm</a> (версии 1.82.7 и 1.82.8), <a href="https://pypi.org/project/telnyx/">telnyx</a> (версии 4.87.1 и 4.87.2) и <a href="https://github.com/aquasecurity/trivy">Trivy</a> (сканер уязвимостей от Aqua Security).<br />— Все атаки связаны с группировкой <b>TeamPCP</b>: совпадают RSA-ключ шифрования, схема эксфильтрации и имя архива tpcp.tar.gz.<br />— Вредоносный код крал SSH-ключи, токены облачных провайдеров (AWS, GCP, Azure), секреты Kubernetes, файлы криптокошельков и переменные окружения.<br />— LiteLLM — самый агрессивный вариант: латеральное перемещение по кластерам K8s и установка персистентного бэкдора через systemd.<br />— telnyx использовал стеганографию: вредоносный бинарник был спрятан внутри WAV-файла.<br />— Вектор первоначальной атаки — компрометация Trivy, через который были украдены PyPI-токены из CI/CD пайплайнов.</blockquote><h2>Хронология атак</h2><p>Атаки развивались каскадно — каждая следующая была следствием предыдущей.</p><ol><li><b>Март 2026, начало месяца — Trivy.</b> Группировка TeamPCP скомпрометировала apt-репозиторий сканера уязвимостей <a href="https://github.com/aquasecurity/trivy">Trivy</a> от Aqua Security. Отравленный бинарник Trivy, установленный в CI/CD пайплайнах, начал красть секреты раннеров — в том числе токены для публикации на PyPI.</li><li><b>24 марта 2026, 10:52 UTC — LiteLLM.</b> На PyPI появляется версия <a href="https://pypi.org/project/litellm/">litellm</a> 1.82.8 с вредоносным .pth-файлом. Пакет крадёт учётные данные, перемещается по кластерам Kubernetes и устанавливает бэкдор. Чуть ранее вышла версия 1.82.7 с менее агрессивным вариантом — без .pth-файла, но с таким же пейлоадом в коде прокси-сервера.</li><li><b>27 марта 2026 — telnyx.</b> Публикуются версии <a href="https://pypi.org/project/telnyx/">telnyx</a> 4.87.1 и 4.87.2. Вредоносный код внедрён в _client.py. Используется новая техника доставки — стеганография в WAV-файлах. Версия 4.87.1 содержит баг (опечатка в регистре Setup() вместо setup()), версия 4.87.2 исправляет его.</li></ol><p>Ни одна из вредоносных версий не имела соответствующего тега или релиза в GitHub-репозитории. Пакеты были загружены напрямую на PyPI с помощью украденных токенов, минуя стандартный CI/CD-процесс.</p><h2>LiteLLM — подробный разбор</h2><p><a href="https://github.com/BerriAI/litellm">LiteLLM</a> — популярная Python-библиотека от BerriAI, которая предоставляет единый интерфейс к более чем 100 провайдерам LLM: OpenAI, Anthropic, Google, Azure и другим. Она широко используется в AI-стартапах, MCP-плагинах и продакшн-системах. Именно поэтому компрометация этого пакета особенно опасна — он часто оказывается в окружениях с доступом к критическим секретам.</p><h3>Как работал вредоносный код</h3><p>Атака использовала малоизвестную особенность Python — файлы .pth (path configuration files). Модуль site обрабатывает их при каждом запуске интерпретатора: строки, начинающиеся с import, передаются в exec(). Это значит, что вредоносный код выполнялся при <b>любом</b> запуске Python в окружении, где был установлен litellm — не только при импорте библиотеки.</p><p>Файл litellm_init.pth (34 628 байт) содержал одну строку:</p><p>Эта строка запускала фоновый процесс через Popen, который декодировал и выполнял трёхуровневый вредоносный пейлоад.</p><h3>Три стадии атаки</h3><p><b>Стадия 1 — оркестратор</b> (25 844 байта). Содержал захардкоженный RSA-4096 публичный ключ и ещё один base64-закодированный блоб с логикой сбора данных. Управлял шифрованием и отправкой украденных данных.</p><p><b>Стадия 2 — коллектор</b> (17 281 байт). Собирал:</p><ul><li>SSH-ключи всех типов (RSA, Ed25519, ECDSA, DSA) со всех пользователей системы</li><li>Переменные окружения и .env-файлы</li><li>Учётные данные AWS, GCP, Azure — включая активную эксплуатацию через AWS SigV4 для дампа Secrets Manager и SSM Parameter Store</li><li>Конфигурации Kubernetes и секреты из всех namespace</li><li>Файлы криптокошельков: Bitcoin, Ethereum, Solana, Litecoin, Dogecoin, Zcash и другие</li><li>Историю shell, .gitconfig, пароли баз данных</li></ul><p><b>Стадия 3 — персистентность</b> (1 125 байт). Устанавливал бэкдор, замаскированный под «System Telemetry Service»:</p><p>Домен checkmarx.zone имитировал известную компанию <a href="https://checkmarx.com/">Checkmarx</a>, работающую в сфере безопасности приложений.</p><h3>Латеральное перемещение по Kubernetes</h3><p>Самая агрессивная часть атаки — распространение по кластерам Kubernetes. Если на скомпрометированной машине был доступен токен сервисного аккаунта K8s, вредонос создавал привилегированный pod на <b>каждом</b> узле кластера:</p><p>Поды именовались node-setup-* в пространстве имён kube-system, чтобы выглядеть легитимно. Каждый под монтировал корневую файловую систему хоста, получал доступ к PID и сети хоста и через chroot устанавливал персистентный бэкдор непосредственно на узел.</p><h3>Шифрование и эксфильтрация</h3><p>Украденные данные шифровались по гибридной схеме: случайный 32-байтный ключ AES-256-CBC для данных, затем сам ключ шифровался RSA-4096 (OAEP). Архив отправлялся POST-запросом на домен models.litellm.cloud — он был зарегистрирован 23 марта 2026 года, за день до публикации вредоносных пакетов, и не является частью легитимной инфраструктуры LiteLLM.</p><p>Побочный эффект — из-за того, что .pth-файл срабатывал при каждом запуске интерпретатора, дочерний процесс снова запускал тот же .pth, создавая экспоненциальную fork-бомбу. Именно так компрометацию и обнаружили: один из инженеров <a href="https://futuresearch.ai/">FutureSearch</a> заметил, что машина упала после установки litellm как транзитивной зависимости MCP-плагина в <a href="https://www.cursor.com/">Cursor</a>.</p><h2>telnyx — стеганография в WAV-файлах</h2><p><a href="https://github.com/team-telnyx/telnyx-python">telnyx</a> — официальный Python SDK для телекоммуникационной платформы <a href="https://telnyx.com/">Telnyx</a>, используемый для программной телефонии, SMS и работы с сетью. Пакет загружается более миллиона раз в месяц (~30 000 в день), что делает его компрометацию особенно масштабной.</p><h3>Вектор атаки</h3><p>Как и в случае с LiteLLM, GitHub-репозиторий telnyx не был скомпрометирован. Все недавние коммиты принадлежат stainless-app[bot] — боту платформы <a href="https://www.stainlessapi.com/">Stainless</a>, которая генерирует SDK. Ни версия 4.87.1, ни 4.87.2 не имеют соответствующих тегов или релизов в GitHub.</p><p>Ключевое доказательство — отпечаток инструмента загрузки. Легитимный CI-пайплайн использует rye publish, а метаданные PyPI для вредоносных версий показывают twine/6.2.0 CPython/3.14.3. Злоумышленник загрузил пакеты вручную, используя украденный API-токен.</p><p>Репозиторий не использовал <a href="https://docs.pypi.org/trusted-publishers/">PyPI Trusted Publishers</a> (OIDC), которые привязывают загрузку к конкретному репозиторию и workflow. Без этой защиты украденный токен позволяет загрузить любую версию с любой машины.</p><h3>Стеганография в WAV-файлах</h3><p>Главное нововведение этой атаки — доставка пейлоада через аудиофайлы. При импорте модуля telnyx вредоносный код скачивал WAV-файл с C2-сервера и извлекал из аудиоданных скрытый исполняемый файл:</p><p>Этот подход позволяет обойти сетевые инструменты инспекции трафика: WAV-файл выглядит как обычный аудиоконтент, а сам пейлоад закодирован в base64 и XOR-шифрован внутри аудиоданных.</p><h3>Два вектора атаки</h3><p>Вредоносный код содержал два независимых пути — для Windows и для Linux/macOS.</p><p><b>Windows</b> — функция setup() скачивала WAV-файл hangup.wav, извлекала из него бинарник и сохраняла как msbuild.exe в папку автозагрузки Windows (директория Startup в %APPDATA%). Имя файла имитировало легитимный <a href="https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild">Microsoft Build Engine</a>. Для предотвращения повторных загрузок использовался скрытый lock-файл с 12-часовым таймаутом.</p><p><b>Linux/macOS</b> — функция FetchAudio() скачивала ringtone.wav, декодировала из base64-переменной _p скрипт сбора учётных данных и запускала его в фоновом процессе. Скрипт собирал те же категории данных, что и в атаке на LiteLLM, шифровал их AES-256-CBC + RSA-4096 и отправлял через HTTP POST.</p><p>Любопытная деталь: версия 4.87.1 содержала баг — вызов Setup() с заглавной буквой при определении def setup() со строчной. NameError прерывал выполнение модуля, и ни один из векторов атаки не работал. Версия 4.87.2 исправляла опечатку.</p><h2>Trivy — когда сканер безопасности сам становится угрозой</h2><p><a href="https://github.com/aquasecurity/trivy">Trivy</a> — популярный open source сканер уязвимостей от <a href="https://www.aquasec.com/">Aqua Security</a>, который проверяет контейнеры, файловые системы и Git-репозитории. Он широко используется в CI/CD пайплайнах для автоматического поиска уязвимостей перед деплоем.</p><p>Именно Trivy стал точкой входа для всей цепочки атак. TeamPCP скомпрометировала apt-репозиторий Trivy и подменила бинарник. Когда CI-раннеры устанавливали Trivy без фиксации версии, они получали отравленную сборку:</p><p>Отравленный Trivy с полными привилегиями CI-раннера собирал секреты окружения, включая PYPI_PUBLISH_PASSWORD. Эти украденные токены затем использовались для публикации вредоносных версий LiteLLM и telnyx напрямую на PyPI.</p><p>Мейнтейнер LiteLLM <a href="https://news.ycombinator.com/item?id=43466171">подтвердил связь с Trivy на Hacker News</a>. Ирония ситуации в том, что инструмент, призванный защищать от уязвимостей, сам стал вектором атаки.</p><h2>Кто за этим стоит — группа TeamPCP</h2><p>Все три атаки атрибутированы одной группировке — <b>TeamPCP</b>. Атрибуция основана на трёх совпадающих индикаторах:</p><ol><li><b>Идентичный RSA-4096 публичный ключ.</b> Ключ шифрования в пейлоаде telnyx побайтово совпадает с ключом из litellm 1.82.8. Расшифровать данные может только владелец соответствующего приватного ключа.</li><li><b>Имя архива tpcp.tar.gz и HTTP-заголовок X-Filename: tpcp.tar.gz.</b> Используются в обеих атаках при эксфильтрации. «TPCP» — сокращение от TeamPCP.</li><li><b>Идентичная схема шифрования.</b> Одинаковая последовательность команд openssl: генерация случайного ключа, шифрование AES-256-CBC, шифрование ключа через RSA OAEP — одни и те же вызовы в обоих пейлоадах.</li></ol><p>При этом атакующие активно развивают инструментарий: LiteLLM использовал скомпрометированный домен (models.litellm.cloud) для C2, telnyx — голый IP-адрес (83.142.209.203). Стеганография в WAV-файлах — новая техника, не встречавшаяся в атаке на LiteLLM.</p><h2>Как защититься</h2><p>Если вы используете любой из скомпрометированных пакетов — действуйте немедленно. Вот практический чеклист:</p><ol><li><b>Проверьте версии зависимостей.</b> Запустите pip show litellm и pip show telnyx во всех ваших окружениях, включая CI/CD раннеры и Docker-образы.</li><li><b>Удалите скомпрометированные версии и очистите кеш.</b> Удалите пакеты и выполните pip cache purge или rm -rf ~/.cache/uv, чтобы закэшированные wheel-файлы не установились повторно.</li><li><b>Проверьте признаки персистентности.</b> Ищите ~/.config/sysmon/sysmon.py и ~/.config/systemd/user/sysmon.service. В Kubernetes — поды node-setup-* в kube-system. На Windows — файл msbuild.exe в папке Startup.</li><li><b>Ротируйте все секреты.</b> SSH-ключи, токены облачных провайдеров, ключи API из .env, пароли БД, токены PyPI — всё, что присутствовало в скомпрометированном окружении.</li><li><b>Фиксируйте версии зависимостей.</b> Используйте lock-файлы: pip freeze, uv lock, poetry.lock.</li><li><b>Включите PyPI Trusted Publishers.</b> Привяжите публикацию пакетов к конкретному GitHub-репозиторию и workflow через OIDC. Украденные токены станут бесполезными.</li><li><b>Проверяйте хеши пакетов.</b> Используйте флаг --require-hashes при установке через pip или аналогичные механизмы в uv и poetry.</li><li><b>Настройте мониторинг зависимостей.</b> Инструменты вроде <a href="https://github.com/safedep/vet">SafeDep vet</a>, <a href="https://github.com/pypa/pip-audit">pip-audit</a> или <a href="https://socket.dev/">Socket</a> помогут обнаружить вредоносные пакеты до попадания в продакшн.</li></ol><p>Пример фиксации хешей с pip:</p><p>Пример фиксации с uv:</p><h2>FAQ</h2><h3>Как понять, что мой проект затронут?</h3><p>Проверьте установленные версии: pip show litellm и pip show telnyx. Если у вас litellm 1.82.7 или 1.82.8, либо telnyx 4.87.1 или 4.87.2 — вы затронуты. Также проверьте кеш пакетного менеджера: для uv поищите файл litellm_init.pth в ~/.cache/uv. Наличие этого файла — признак компрометации.</p><h3>Достаточно ли просто обновить пакет?</h3><p>Нет. Если вредоносная версия была установлена, одного обновления недостаточно. Вредонос мог уже установить персистентный бэкдор (~/.config/sysmon/sysmon.py), украсть секреты или создать привилегированные поды в Kubernetes. Необходимо провести полный аудит: проверить систему на признаки персистентности, ротировать все секреты и очистить кеш пакетного менеджера.</p><h3>Работает ли вредоносный код в Docker-контейнерах?</h3><p>Да. .pth-файл из litellm срабатывает при любом запуске Python в контейнере, где установлен пакет. Более того — если в контейнере доступен Kubernetes service account token (что является стандартной конфигурацией), вредонос может выйти за пределы контейнера и скомпрометировать весь кластер.</p><h3>Как TeamPCP получила токены PyPI?</h3><p>Через скомпрометированный Trivy. CI/CD пайплайны LiteLLM и, вероятно, telnyx устанавливали Trivy из apt-репозитория без фиксации версии. Когда TeamPCP подменила бинарник в репозитории, отравленный Trivy с привилегиями CI-раннера извлёк секреты окружения — в том числе токены для публикации на PyPI. Репозиторий telnyx не использовал Trivy, поэтому точный вектор получения его токена пока не установлен, но паттерн атаки идентичен.</p><h3>Можно ли было обнаружить атаку до установки?</h3><p>Да, при наличии правильных инструментов. Отсутствие соответствующего GitHub-тега для опубликованной версии, изменение инструмента загрузки (twine вместо rye publish), появление .pth-файла в wheel — всё это детектируемые аномалии. Инструменты типа <a href="https://github.com/safedep/vet">SafeDep vet</a> умеют проверять пакеты на подобные сигналы. Также помогает проверка хешей через --require-hashes.</p><h2>Выводы</h2><p>Волна атак TeamPCP демонстрирует новый уровень supply chain атак: это не тайпсквоттинг и не подмена малоизвестных пакетов. Атакующие компрометируют инфраструктуру публикации настоящих, широко используемых библиотек — и делают это через цепочку доверия. Trivy ломает CI/CD, CI/CD отдаёт PyPI-токены, PyPI-токены используются для публикации троянизированных пакетов.</p><blockquote>«Безопасность цепочки поставок — это не отдельная задача, а свойство всего процесса разработки. Если ваш сканер уязвимостей сам становится вектором атаки, проблема не в конкретном инструменте — проблема в том, что мы устанавливаем инструменты безопасности с тем же уровнем доверия, что и обычные зависимости.»</blockquote><p>Три ключевых урока из этой истории:</p><ol><li><b>Фиксируйте версии всего</b> — не только библиотек, но и инструментов в CI/CD. apt-get install trivy без версии — это приглашение для атакующего.</li><li><b>Используйте Trusted Publishers</b> — PyPI OIDC привязывает публикацию к конкретному репозиторию. Украденный токен без workflow бесполезен.</li><li><b>Мониторьте аномалии</b> — отсутствие GitHub-тега для PyPI-версии, смена upload tool, появление .pth-файлов — всё это сигналы компрометации.</li></ol><p><b>Источники:</b> <a href="https://safedep.io/blog/malicious-litellm-1.82.8/">SafeDep — анализ litellm</a>, <a href="https://safedep.io/blog/compromised-telnyx-pypi-wav-steganography/">SafeDep — анализ telnyx</a>, <a href="https://blog.futuresearch.ai/p/supply-chain-attack-in-litellm-on-pypi">FutureSearch — обнаружение litellm</a>, <a href="https://github.com/BerriAI/litellm/issues/24512">GitHub Advisory litellm #24512</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Исследователь предложил метод промптинга DEO — он делает LLM на 40% креативнее</title>
      <link>https://tproger.ru/news/issledovatel-predlozhil-metod-promptinga-deo---on-delaet-llm-na-</link>
      <comments>https://tproger.ru/news/issledovatel-predlozhil-metod-promptinga-deo---on-delaet-llm-na-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/issledovatel-predlozhil-metod-promptinga-deo---on-delaet-llm-na-</guid>
      <description><![CDATA[<p>DEO — метод промптинга из когнитивной психологии. На 3 open-source LLM показал прирост креативности до 55%. Разбираем с готовыми промптами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/issledovatel-predlozhil-metod-promptinga-deo---on-delaet-llm-na-">Исследователь предложил метод промптинга DEO — он делает LLM на 40% креативнее</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Наука]]></category>
      <category><![CDATA[Промпты]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 27 Mar 2026 17:02:26 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вы просите ChatGPT или Llama решить нестандартную задачу, а модель выдаёт банальный ответ? Возможно, проблема не в модели, а в том, <b>как</b> вы формулируете промпт. Исследователь из Турции предложил метод, который заставляет LLM переключаться между аналитическим и эмоциональным мышлением — и результаты впечатляют.</p><p>Distance-Engagement Oscillation (DEO) — это метод промптинга, основанный на теории перцептивного рефрейминга (Perceptual Reframing Theory, PRT). Рефрейминг — это способность посмотреть на проблему под другим углом, сменив «рамку» восприятия. DEO заставляет языковую модель чередовать два когнитивных режима: аналитическую дистанцию (анализ проблемы со стороны) и эмоциональное вовлечение (проживание проблемы изнутри). Автор метода — <a href="https://github.com/gokmengokhan">Гёкхан Гёкмен</a> из Технического университета Бурсы.</p><p>Результаты эксперимента на трёх open-source LLM показали: DEO-промптинг повышает качество креативного мышления модели на 40–55% по сравнению с обычными промптами. Эффект подтверждён тремя независимыми оценщиками с размером эффекта Cohen's d от 1.29 до 1.63 («очень большой»).</p><p><b>Главное:</b><br />— DEO-промптинг улучшает креативное мышление LLM на 40–55% (Cohen's d = 1.29–1.63)<br />— Предсказанный порядок DEO &gt; Distance &gt; Engagement ≥ Vanilla подтвердился во всех 9 комбинациях модель×оценщик (p &lt; .001)<br />— Метод работает без дообучения — только через структуру промпта<br />— Тестировалось на Llama 3.3 70B, Qwen3 32B и Llama 4 Scout<br />— Код и данные открыты на GitHub под лицензией CC BY 4.0</p><h2>Как работает DEO-промптинг</h2><p>В основе метода лежит Perceptual Reframing Theory (PRT) — теория, объединяющая исследования из области инсайтов, самодистанцирования, теории уровней конструирования и психотерапии. PRT описывает девять когнитивных путей, которыми люди переключают восприятие при решении творческих задач.</p><p>DEO использует четыре шага, чередуя аналитическую дистанцию и эмоциональное вовлечение:</p><ol><li><b>ANALYSE</b> (дистанция) — модель анализирует проблему со стороны, выявляя скрытые допущения и рамки мышления</li><li><b>FEEL</b> (вовлечение) — модель «проживает» проблему, подключая эмпатию и эмоциональный контекст</li><li><b>REFRAME</b> (дистанция) — модель формулирует альтернативные интерпретации на основе обоих режимов</li><li><b>ENVISION</b> (вовлечение) — модель представляет конкретную реализацию нового решения</li></ol><p>Ключевая идея: ни дистанция, ни вовлечение по отдельности не дают максимального эффекта. Именно <b>чередование</b> между ними создаёт качественный сдвиг в мышлении модели.</p><h2>Дизайн эксперимента</h2><p>Исследование включало два анализа с общим объёмом в <b>13 500 вызовов генерации</b>:</p><h3>Анализ 1: промпты с рефреймингом vs обычные промпты</h3><ul><li><b>50 оригинальных задач</b> из 8 категорий когнитивных ловушек: якорение, ложная бинарность, функциональная фиксированность, фрейминговая ловушка, нарративный замок, нулевая сумма, эффект Эйнштеллунга, многоходовые задачи</li><li><b>3 open-source LLM</b>: Llama 3.3 70B, Qwen3 32B, Llama 4 Scout 17B-16E</li><li><b>5 запусков</b> на каждое условие (temperature 0.7) с усреднением</li><li><b>3 независимых оценщика</b>: самооценка модели + Claude Sonnet 4 + GPT-4.1 (все слепые к условию)</li><li><b>5 критериев оценки</b>: разнообразие фреймов, выявление допущений, новизна решения, ревизия предпосылок, корректность</li></ul><h3>Анализ 2: четыре условия DEO</h3><p>Для 20 задач с чистыми путями дистанции сравнивались четыре условия:</p><ul><li><b>Vanilla</b> — обычный промпт без дополнительной структуры</li><li><b>Distance-only</b> — только аналитическая дистанция</li><li><b>Engagement-only</b> — только эмоциональное вовлечение</li><li><b>DEO</b> — полная осцилляция между дистанцией и вовлечением</li></ul><h2>Результаты</h2><p>Предсказанный теорией порядок <b>DEO &gt; Distance &gt; Engagement ≥ Vanilla</b> подтвердился во всех 9 комбинациях модель × оценщик (все Friedman p &lt; .001).</p><h3>Анализ 1: рефрейминг значимо лучше обычных промптов</h3><p>Средние баллы по 5-балльной шкале рефрейминга:</p><ul><li><b>Llama 3.3 70B</b> — vanilla: 2.84, рефрейминг: 3.99 (Δ = +1.15, d = 1.29)</li><li><b>Qwen3 32B</b> — vanilla: 2.86, рефрейминг: 4.00 (Δ = +1.14, d = 1.56)</li><li><b>Llama 4 Scout</b> — vanilla: 2.82, рефрейминг: 3.98 (Δ = +1.16, d = 1.42)</li></ul><p>Все 9 пар модель × оценщик показали статистическую значимость на уровне p &lt; .001. При этом корректность ответов не снизилась — напротив, также значимо выросла.</p><h3>Анализ 2: осцилляция эффективнее любого режима по отдельности</h3><p>Средние баллы по четырём условиям (оценщик OpenAI, Llama 3.3 70B):</p><ul><li><b>Vanilla</b>: 2.37</li><li><b>Engagement-only</b>: 2.92</li><li><b>Distance-only</b>: 3.61</li><li><b>DEO</b>: 4.06</li></ul><p>Дистанция значительно превосходит вовлечение во всех 9 случаях — это подтверждает гипотезу PRT о том, что <b>увидеть фрейм</b> мышления сложнее, чем прочувствовать проблему. DEO добавляет значимое улучшение поверх дистанции в 4 из 9 случаев (d = 0.40–1.06).</p><h3>Какие задачи выигрывают больше всего</h3><p>Максимальный эффект DEO наблюдается на задачах с <b>нарративным замком</b> (narrative lock-in) — когда модель застревает в одной истории или интерпретации. Здесь прирост достигает Δ = +2.14, d = 3.23. На втором месте — задачи с <b>фрейминговой ловушкой</b> и <b>эффектом Эйнштеллунга</b> (фиксация на привычном методе решения).</p><h2>Девять путей рефрейминга PRT</h2><p>Теория PRT выделяет девять когнитивных путей, которые используются как промпт-стратегии:</p><ol><li><b>Name the Frame</b> — назвать скрытую рамку мышления (Δ до +1.60)</li><li><b>Decompose to Generic</b> — разложить задачу на абстрактные компоненты</li><li><b>Distant Analogy</b> — применить аналогию из другой области (Δ до +1.64)</li><li><b>Incubate &amp; Reset</b> — сделать паузу и вернуться к задаче заново</li><li><b>Invert</b> — перевернуть задачу наоборот (Δ до +1.38)</li><li><b>Premise Reflection</b> — поставить под сомнение исходные предпосылки (Δ до +1.56)</li><li><b>Surprise as Signal</b> — использовать удивление как сигнал для рефрейминга (Δ до +1.87)</li><li><b>Confidence Calibration</b> — калибровать уверенность в ответе</li><li><b>Step Outside</b> — посмотреть на задачу глазами другого человека (Δ до +1.34)</li></ol><p>Самые эффективные пути: <b>Name the Frame</b>, <b>Distant Analogy</b> и <b>Surprise as Signal</b>. Все три связаны с дистанцией — способностью модели "увидеть" рамку, в которой она мыслит.</p><h2>Как попробовать DEO самому</h2><p><a href="https://github.com/gokmengokhan/deo-llm-reframing">Репозиторий</a> включает slash-команду <b>/reframe</b> для <a href="https://docs.anthropic.com/en/docs/claude-code">Claude Code</a>. Она применяет четырёхшаговую осцилляцию DEO к любой задаче:</p><p>Claude пройдёт четыре этапа — ANALYSE, FEEL, REFRAME, ENVISION — и выдаст рефреймированный ответ с альтернативными интерпретациями проблемы.</p><p>Для воспроизведения полного эксперимента:</p><p>Полный эксперимент (3 модели, 4 условия, 5 запусков, 3 оценщика) обходится примерно в $15–20.</p><h2>FAQ</h2><h3>Что такое DEO-промптинг?</h3><p>DEO (Distance-Engagement Oscillation) — это метод промптинга, в котором языковая модель чередует аналитический режим (дистанция) и эмоциональный режим (вовлечение). Такое чередование помогает модели выйти за рамки привычного мышления и находить более креативные решения. Метод основан на теории перцептивного рефрейминга (PRT) из когнитивной психологии.</p><h3>На каких моделях работает DEO?</h3><p>В исследовании метод тестировался на трёх open-source моделях: Llama 3.3 70B, Qwen3 32B и Llama 4 Scout 17B-16E. Эффект был стабильно значимым на всех трёх. Поскольку DEO работает на уровне промпта (zero-shot, без дообучения), он применим к любой LLM, способной следовать структурированным инструкциям.</p><h3>Чем DEO отличается от Chain-of-Thought?</h3><p>Chain-of-Thought заставляет модель рассуждать пошагово в одном когнитивном режиме. DEO добавляет <b>переключение между режимами</b> — от анализа к эмпатии и обратно. Это помогает модели не просто "думать дольше", а "думать иначе", выявляя скрытые предпосылки и рамки мышления, которые блокируют креативные решения.</p><h3>Снижается ли корректность ответов при использовании DEO?</h3><p>Нет. В эксперименте корректность ответов значимо выросла во всех 9 комбинациях модель × оценщик (p &lt; .001). DEO не жертвует точностью ради креативности — оба показателя улучшаются одновременно.</p><h3>Можно ли использовать DEO в продакшене?</h3><p>Код и данные открыты под лицензией CC BY 4.0. Метод не требует дообучения или специальной инфраструктуры — достаточно модифицировать системный промпт. Четырёхшаговая структура ANALYSE–FEEL–REFRAME–ENVISION легко интегрируется в любой пайплайн.</p><h2>Выводы</h2><blockquote>Аналитическая дистанция показывает модели, что рамка мышления существует. Эмоциональное вовлечение делает сдвиг восприятия устойчивым. Именно осцилляция между ними даёт максимальный эффект.</blockquote><p>Исследование Гёкмена — первый эмпирический тест механизма DEO и одна из немногих работ, применяющих когнитивную психологию рефрейминга к промпт-инжинирингу. Результаты показывают, что структура промпта важнее, чем размер модели: правильно организованное чередование дистанции и вовлечения даёт стабильный и воспроизводимый прирост креативного мышления LLM.</p><p>Полный код эксперимента, 307 JSON-файлов с ответами моделей и статистический анализ доступны в <a href="https://github.com/gokmengokhan/deo-llm-reframing">репозитории на GitHub</a>. Препринт статьи опубликован на <a href="https://zenodo.org/records/19252225">Zenodo</a> (DOI: 10.5281/zenodo.19252225).</p><p>Попробуйте DEO-промптинг на своих задачах — возможно, ваша LLM способна на большее, чем вы думаете.</p>]]></content:encoded>
    </item>
    <item>
      <title>Взломан PyPI-пакет telnyx: вредонос прячется в WAV-файлах и крадёт учётные данные</title>
      <link>https://tproger.ru/news/vzloman-pypi-paket-telnyx--vredonos-pryachetsya-v-wav-fajlah-i-krad</link>
      <comments>https://tproger.ru/news/vzloman-pypi-paket-telnyx--vredonos-pryachetsya-v-wav-fajlah-i-krad?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/vzloman-pypi-paket-telnyx--vredonos-pryachetsya-v-wav-fajlah-i-krad</guid>
      <description><![CDATA[<p>Версии telnyx 4.87.1 и 4.87.2 на PyPI содержат вредоносный код (CVE-2026-33634). Вредонос использует стеганографию в WAV-файлах для кражи учётных данных. Проверьте свои проекты.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/vzloman-pypi-paket-telnyx--vredonos-pryachetsya-v-wav-fajlah-i-krad">Взломан PyPI-пакет telnyx: вредонос прячется в WAV-файлах и крадёт учётные данные</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Информационная безопасность]]></category>
      <category><![CDATA[Кибербезопасность]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 27 Mar 2026 16:28:05 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы используете Python-пакет <b>telnyx</b> для работы с телефонией и мессенджингом — проверьте версию прямо сейчас. Две версии официального SDK оказались заражены вредоносным кодом, который ворует учётные данные и устанавливает бэкдор.</p><p><a href="https://telnyx.com/">Telnyx</a> — платформа для программируемой телефонии, SMS и сетевых сервисов. Её Python SDK (<a href="https://pypi.org/project/telnyx/">telnyx на PyPI</a>) скачивают более <b>1 миллиона раз в месяц</b> (~30 000 загрузок в день). 27 марта 2026 года исследователи из <a href="https://safedep.io/">SafeDep</a> обнаружили, что версии <b>4.87.1</b> и <b>4.87.2</b> содержат вредоносный код, которого нет в исходном репозитории на GitHub. Инциденту присвоен <b><a href="https://nvd.nist.gov/vuln/detail/CVE-2026-33634">CVE-2026-33634</a></b> (CVSS 9.4). Обе заражённые версии уже карантинированы PyPI.</p><p><b>Главное:</b> Версии telnyx 4.87.1 и 4.87.2 на PyPI содержат вредоносный код — 74 строки, внедрённые в файл _client.py. Пакет скачивают более 1 млн раз в месяц. Вредонос использует стеганографию в WAV-файлах для доставки полезной нагрузки. На Windows устанавливается бэкдор, на Linux/macOS — крадутся учётные данные. Безопасная версия — 4.87.0. Атака связана с группировкой TeamPCP — это уже третье звено в цепочке после компрометации Trivy, Checkmarx и litellm.</p><p>Supply chain attack (атака на цепочку поставок) — тип атаки, при которой злоумышленник компрометирует не конечную цель, а один из компонентов, от которого она зависит: библиотеку, SDK или инструмент сборки. Разберём, как именно сработала атака и что делать, если вы используете telnyx.</p><h2>Как произошла компрометация</h2><p>Последняя чистая версия пакета — <b>4.87.0</b> — была опубликована 26 марта через штатный CI/CD-пайплайн (GitHub Actions + Rye). У неё есть соответствующий тег v4.87.0 в репозитории.</p><p>Версии 4.87.1 и 4.87.2 не имеют ни тегов, ни релизов на GitHub. Workflow публикации не запускался после v4.87.0. Метаданные PyPI показывают, что заражённые версии были загружены через twine/6.2.0, тогда как легитимный пайплайн использует rye publish.</p><p>Вывод: атакующий получил украденный PyPI API-токен и загрузил троянизированные версии напрямую, минуя CI/CD. У проекта не был настроен <a href="https://docs.pypi.org/trusted-publishers/">PyPI Trusted Publisher (OIDC)</a>, который привязывает загрузку к конкретному репозиторию и воркфлоу, делая украденные токены бесполезными.</p><h2>Как работает вредоносный код</h2><p>В файл telnyx/_client.py было внедрено ровно <b>74 строки</b> кода в трёх местах: импорты в начале файла, base64-закодированная переменная с полезной нагрузкой и функции атаки после легитимных классов. Код выполняется автоматически при import telnyx — никакого взаимодействия с пользователем не требуется.</p><h3>WAV-стеганография</h3><p>Ключевая особенность атаки — использование стеганографии. Вредонос скачивает с C2-сервера файлы, замаскированные под WAV-аудио (ringtone.wav для Linux/macOS, hangup.wav для Windows). Исполняемый код спрятан в аудиофреймах и извлекается через XOR-деобфускацию. Импорт модуля wave в SDK для телефонии — единственный «красный флаг», который мог выдать атаку при code review.</p><h3>Windows: бэкдор через автозагрузку</h3><p>Функция setup() проверяет os.name == 'nt', затем скачивает бинарник из WAV-файла и сохраняет его как msbuild.exe в папку автозагрузки Windows. Имя файла имитирует легитимный инструмент Microsoft Build Engine. Бэкдор запускается при каждом входе в систему с кулдауном повторной установки 12 часов.</p><h3>Linux/macOS: кража учётных данных</h3><p>Функция FetchAudio() запускает второй этап из base64-закодированной переменной (4 436 символов). Скрипт собирает учётные данные, API-ключи, SSH-ключи и секреты, шифрует их связкой <b>AES-256-CBC + RSA-4096</b> с хардкодированным публичным ключом и отправляет на C2-сервер через HTTP POST.</p><h3>Баг атакующего</h3><p>Любопытная деталь: в версии 4.87.1 атака на Windows <b>не работала</b>. Атакующий вызвал Setup() с заглавной буквы, тогда как функция определена как setup() со строчной. Это вызывало NameError, прерывавший выполнение модуля до запуска FetchAudio(). Версия 4.87.2 — по сути патч-релиз атакующего, исправляющий эту опечатку.</p><h2>Связь с TeamPCP: цепочка атак</h2><p>RSA-4096 публичный ключ, зашитый в вредоносный код, <b>побайтово совпадает</b> с ключом из <a href="https://safedep.io/blog/litellm-supply-chain-compromise/">компрометации пакета litellm</a>. Это позволяет с высокой уверенностью атрибутировать атаку группировке <b>TeamPCP</b>. Группировка использует инфраструктуру <b>CanisterWorm</b>, размещённую на блокчейне Internet Computer Protocol (ICP) — без единой точки отказа.</p><p>Компрометация telnyx — <b>третье крупное звено</b> в каскадной атаке TeamPCP:</p><ul><li><b>27 февраля</b> — атака на репозиторий Trivy (сканер уязвимостей от Aqua Security) через вредоносный PR</li><li><b>19–20 марта</b> — заражённый Trivy v0.69.4 опубликован, CanisterWorm распространился на 46+ npm-пакетов</li><li><b>23 марта</b> — скомпрометированы Checkmarx GitHub Actions</li><li><b>24 марта</b> — заражены версии litellm 1.82.7 и 1.82.8 на PyPI (~97 млн загрузок/мес)</li><li><b>27 марта</b> — заражены версии telnyx 4.87.1 и 4.87.2 на PyPI</li></ul><h2>Что делать</h2><ol><li>Проверьте версию telnyx в ваших проектах</li><li>Если установлена 4.87.1 или 4.87.2 — <b>немедленно откатитесь</b> на 4.87.0</li><li>Проверьте наличие файла msbuild.exe в папке автозагрузки Windows</li><li>Проверьте сетевые соединения с IP 83.142.209.203</li><li>Если обнаружены IoC — считайте <b>все</b> учётные данные, API-ключи и SSH-ключи скомпрометированными</li><li>Проверьте CI/CD на использование других скомпрометированных пакетов TeamPCP: Trivy, Checkmarx Actions, litellm</li><li>Включите <a href="https://docs.pypi.org/trusted-publishers/">Trusted Publishers</a> для всех ваших PyPI-пакетов</li></ol><h2>Индикаторы компрометации (IoC)</h2><p><b><a href="https://nvd.nist.gov/vuln/detail/CVE-2026-33634">CVE-2026-33634</a></b> (CVSS 9.4) — <a href="https://github.com/team-telnyx/telnyx-python/issues/235">GitHub issue #235</a></p><h2>FAQ</h2><h3>Какие версии telnyx заражены?</h3><p>Вредоносный код содержится в версиях 4.87.1 и 4.87.2. Обе уже карантинированы PyPI. Последняя безопасная версия — 4.87.0. При этом в 4.87.1 из-за опечатки атакующего ни один из векторов атаки фактически не срабатывал.</p><h3>Как атакующие получили доступ к PyPI?</h3><p>Через украденный PyPI API-токен. GitHub-репозиторий telnyx не был скомпрометирован — атакующие загрузили пакеты напрямую на PyPI через twine, минуя CI/CD. У проекта не был настроен Trusted Publisher (OIDC), который предотвратил бы такую атаку.</p><h3>Кто стоит за атакой?</h3><p>Атака атрибутирована группировке TeamPCP. Это та же группа, которая ранее скомпрометировала сканер уязвимостей Trivy, GitHub Actions Checkmarx и PyPI-пакет litellm. RSA-ключ в вредоносном коде побайтово совпадает с ключом из предыдущих атак. Инфраструктура CanisterWorm размещена на блокчейне ICP, что делает её устойчивой к блокировке.</p><h3>Что делать, если я уже установил заражённую версию?</h3><p>Немедленно откатитесь на версию 4.87.0. Проверьте систему на наличие IoC. Если обнаружены признаки компрометации — считайте все учётные данные, API-ключи и SSH-ключи на этой машине скомпрометированными и выполните их ротацию. Также проверьте CI/CD на использование Trivy, Checkmarx Actions и litellm.</p><h2>Выводы</h2><blockquote>Атака на telnyx — не изолированный инцидент. Это часть каскадной кампании TeamPCP, которая за месяц скомпрометировала Trivy, Checkmarx, litellm и теперь telnyx. Единственная надёжная защита — PyPI Trusted Publishers (OIDC), привязывающий загрузку к конкретному GitHub-репозиторию.</blockquote><p>Этот инцидент в очередной раз показывает: доверять пакетам из PyPI «на слово» нельзя, даже если это официальный SDK крупной платформы. Настройте <a href="https://docs.pypi.org/trusted-publishers/">Trusted Publishers</a>, пиньте версии зависимостей, проверяйте хеши при установке. Проверьте свои проекты прямо сейчас.</p><p>Источники: <a href="https://safedep.io/malicious-telnyx-pypi-compromise/">SafeDep</a>, <a href="https://www.aikido.dev/blog/telnyx-pypi-compromised-teampcp-canisterworm">Aikido</a>, <a href="https://thehackernews.com/2026/03/teampcp-pushes-malicious-telnyx.html">The Hacker News</a>, <a href="https://research.jfrog.com/post/team-pcp-strikes-again-telnyx-popular-library-hit/">JFrog Security Research</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Как пакет для работы с нейросетями стал стилером: полный разбор атаки на LiteLLM</title>
      <link>https://tproger.ru/news/kak-paket-dlya-raboty-s-nejrosetyami-stal-stilerom--polnyj-razbor-</link>
      <comments>https://tproger.ru/news/kak-paket-dlya-raboty-s-nejrosetyami-stal-stilerom--polnyj-razbor-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Картофельный Повелитель]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/kak-paket-dlya-raboty-s-nejrosetyami-stal-stilerom--polnyj-razbor-</guid>
      <description><![CDATA[<p>Версии LiteLLM 1.82.7 и 1.82.8 содержали стилер. Разбор атаки TeamPCP: хронология, технический анализ, IoC и чек-лист действий. Проверьте свои системы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/kak-paket-dlya-raboty-s-nejrosetyami-stal-stilerom--polnyj-razbor-">Как пакет для работы с нейросетями стал стилером: полный разбор атаки на LiteLLM</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Информационная безопасность]]></category>
      <category><![CDATA[Уязвимость]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Кибербезопасность]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 27 Mar 2026 04:44:10 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если между 24 и 25 марта 2026 года вы обновляли LiteLLM — проверьте версию прямо сейчас. Ваши API-ключи от OpenAI, Anthropic и облачных провайдеров могли утечь.</p><p><a href="https://github.com/BerriAI/litellm">LiteLLM</a> — это open-source прокси для работы с API различных LLM-провайдеров: OpenAI, Anthropic, Azure, Bedrock и ещё сотней других. Библиотека позволяет переключаться между моделями через единый интерфейс с автоматическими фоллбэками, ретраями и трекингом расходов. При <b>97 миллионах загрузок в месяц</b> (около 3,4 млн в день) это один из самых популярных инструментов в AI-инфраструктуре.</p><p>В скомпрометированных версиях 1.82.7 и 1.82.8, опубликованных на PyPI, обнаружили встроенный стилер учётных данных. Он крал SSH-ключи, токены облачных сервисов, API-ключи и пароли, а затем расползался по Kubernetes-кластерам.</p><p><b>Ключевое:</b> Версии LiteLLM 1.82.7 и 1.82.8 на PyPI содержали стилер, крадущий SSH-ключи, облачные токены и API-ключи. Версия 1.82.8 запускала вредоносный код при каждом старте Python — даже без импорта библиотеки. Если вы устанавливали LiteLLM 24 марта 2026 года — <a href="https://tproger.ru/#remediation">проверьте свои системы</a>. Последняя чистая версия — 1.82.6.</p><p>Но эта атака — не изолированный инцидент. Это финал пятидневной <b>supply chain атаки</b> (атаки через цепочку поставок — внедрение вредоносного кода в легитимный пакет через компрометацию его инфраструктуры). За ней стоит группировка <b>TeamPCP</b> — ранее неизвестная группа, которая за последнюю неделю марта целенаправленно атаковала инструменты безопасности и разработки. Кампания началась с компрометации сканера уязвимостей Trivy и через цепочку украденных CI/CD-креденшалов дотянулась до LiteLLM.</p><p>Разбираем всю цепочку атаки от начала до конца — подробнее, чем где-либо ещё.</p><h2>Хронология: пять дней, три вендора, пять экосистем</h2><p>Чтобы понять, как LiteLLM оказался скомпрометирован, нужно отмотать на пять дней назад. Атакующие не ломали LiteLLM напрямую — они добрались до него через цепочку компрометаций, каждая из которых давала доступ к следующей цели.</p><h3>19 марта: Trivy — точка входа</h3><p>Всё началось с <a href="https://github.com/aquasecurity/trivy">Trivy</a> — open-source сканера уязвимостей от Aqua Security, которым пользуются тысячи компаний для проверки контейнеров и кода.</p><p>Атакующие использовали скомпрометированные учётные данные мейнтейнера, чтобы:</p><ul><li>Опубликовать вредоносный релиз <b>Trivy v0.69.4</b>, который прошёл через стандартную release-машинерию и попал в GHCR, ECR Public, Docker Hub, deb/rpm-пакеты</li><li>Подменить <b>76 из 77 тегов</b> aquasecurity/trivy-action на вредоносные коммиты</li><li>Заменить все 7 тегов aquasecurity/setup-trivy</li></ul><p>Вредоносный код в GitHub Actions сканировал память процесса Runner.Worker, собирал креденшалы, шифровал данные AES+RSA и отправлял на подставной домен scan.aquasecurtiy[.]org — обратите внимание на опечатку в слове «security». Если прямая эксфильтрация не удавалась, малварь создавала публичный репозиторий tpcp-docs через GitHub-токен жертвы и сливала данные туда.</p><h3>20–22 марта: npm-червь и дефейс</h3><p>Уже на следующий день украденные токены пошли в дело. Атакующие запустили <b>самораспространяющегося npm-червя</b>: 28 пакетов в @EmilGroup, 16 в @opengov, плюс отдельные пакеты в других скоупах. Червь крал npm-токены из скомпрометированных окружений, проверял, к каким пакетам они дают доступ, поднимал patch-версию, подставлял оригинальный README для маскировки и переиздавал пакет с вредоносной начинкой.</p><p>К 22 марта та же инфраструктура начала обслуживать Kubernetes-скрипт с <b>разделением жертв по геолокации</b>. На иранских системах деплоился DaemonSet с контейнером kamikaze, который удалял файловую систему хоста и перезагружал ноду. На остальных — устанавливал персистентный бэкдор.</p><p>В тот же день атакующие дефейснули <b>44 репозитория внутренней GitHub-организации Aqua Security</b> (aquasec-com), переименовав их с префиксом tpcp-docs- и описанием «TeamPCP Owns Aqua Security».</p><h3>23 марта: Checkmarx</h3><p>Кампания добралась до <b>Checkmarx</b> — ещё одного крупного вендора в сфере безопасности приложений. Были скомпрометированы:</p><ul><li>Checkmarx/kics-github-action — сканер инфраструктурного кода</li><li>Checkmarx/ast-github-action — GitHub Action для платформы Checkmarx</li><li>Расширения VS Code в реестре Open VSX: ast-results v2.53.0 (около 36 000 загрузок) и cx-dev-assist v1.7.0 (около 500 загрузок)</li></ul><p>Паттерн тот же: стилер креденшалов, привязанный к домену checkmarx[.]zone, с фоллбэком на публичный репозиторий docs-tpcp для эксфильтрации.</p><h3>24 марта: LiteLLM</h3><p>В <b>10:52 UTC</b> на PyPI появилась версия LiteLLM 1.82.8. Соответствующий тег или релиз на GitHub отсутствовал — пакет был загружен напрямую, в обход стандартного процесса. По <a href="https://www.reversinglabs.com/blog/teampcp-supply-chain-attack-spreads">данным ReversingLabs</a>, был скомпрометирован GitHub-аккаунт сооснователя и CEO LiteLLM Криша Дхолакии — предположительно, через CI/CD-пайплайн, где Trivy использовался <b>без пиннинга версии</b>.</p><p>Через три часа команда безопасности PyPI поставила проект на карантин. Скомпрометированные версии были удалены. Последняя чистая версия — <b>1.82.6</b>. Но при 3,4 миллионах загрузок в день даже три часа — это огромное окно.</p><p>Мейнтейнеры LiteLLM <a href="https://docs.litellm.ai/blog/security-update-march-2026">опубликовали security-апдейт</a>, подтвердив компрометацию и рекомендовав всем пользователям обновиться до версии 1.82.6 или выше (после снятия карантина). Issue #24512 на GitHub, описывающий уязвимость, был закрыт — предположительно, самим атакующим через скомпрометированный аккаунт.</p><h2>Как работает вредоносный код</h2><p>Теперь разберём, что именно попадало на машины жертв. LiteLLM оказался скомпрометирован в двух версиях, и они существенно различаются по механизму запуска.</p><h3>Версия 1.82.7: инъекция в proxy_server.py</h3><p>В версии 1.82.7 вредоносный код был внедрён в файл litellm/proxy/proxy_server.py. Малварь запускалась только при реальном использовании LiteLLM Proxy в приложении. Если пакет был установлен, но прокси-сервер не запускался, код мог не сработать.</p><h3>Версия 1.82.8: .pth-файл — запуск без импорта</h3><p>Версия 1.82.8 принципиально опаснее. В wheel-пакет был добавлен файл litellm_init.pth размером 34 628 байт, содержащий <b>дважды закодированный</b> в base64 вредоносный код.</p><p>.pth-файлы — малоизвестная особенность Python. Согласно <a href="https://docs.python.org/3/library/site.html">документации модуля site</a>, исполняемые строки в .pth-файлах выполняются автоматически при каждом запуске интерпретатора. Не при импорте библиотеки, а при запуске <b>любого</b> Python-процесса в окружении, где установлен пакет.</p><p>Это означает: достаточно было выполнить pip install litellm==1.82.8, и <b>каждый последующий запуск Python</b> на этой машине активировал стилер. Не нужно писать import litellm — даже python -c "print('hello')" запускал вредоносный код.</p><h3>Три стадии: сбор, шифрование, эксфильтрация</h3><p><b>Стадия 1 — сбор.</b> Скрипт прочёсывает машину и собирает:</p><ul><li>SSH-ключи и конфигурации (~/.ssh/)</li><li>.env-файлы с секретами</li><li>Креденшалы AWS (Access Key, Secret Key), GCP (ADC), Azure</li><li>Конфигурации Kubernetes (~/.kube/config)</li><li>Docker-конфиги (~/.docker/config.json)</li><li>Пароли баз данных</li><li>Историю шелла (bash_history, zsh_history)</li><li>Файлы криптокошельков</li><li>Все переменные окружения</li><li>Данные с облачных metadata-эндпоинтов (IMDS)</li></ul><p><b>Стадия 2 — шифрование.</b> Собранные данные упаковываются в tar-архив и шифруются гибридной схемой: случайный сессионный ключ <b>AES-256-CBC</b> для данных, зашифрованный захардкоженным <b>4096-битным RSA</b>-публичным ключом.</p><p><b>Стадия 3 — эксфильтрация.</b> Зашифрованный архив отправляется POST-запросом на models.litellm[.]cloud (домен не имеет отношения к настоящему litellm.ai) с заголовком X-Filename: tpcp.tar.gz.</p><h3>Горизонтальное распространение в Kubernetes</h3><p>Если на машине обнаружен токен сервисного аккаунта Kubernetes, малварь переходит в режим горизонтального распространения (lateral movement):</p><ol><li>Читает <b>все секреты кластера</b> во всех неймспейсах</li><li>Создаёт привилегированный pod на <b>каждой ноде</b> в kube-system на основе образа alpine:latest</li><li>Каждый pod монтирует <b>корневую файловую систему хоста</b></li><li>Устанавливает персистентный бэкдор в /root/.config/sysmon/sysmon.py</li><li>Регистрирует systemd user service для автозапуска</li></ol><p>Таким образом, компрометация одного pip-пакета превращается в <b>полную компрометацию всего Kubernetes-кластера</b>.</p><h3>Персистентность и бэкдор</h3><p>На локальной машине малварь создаёт:</p><ul><li>~/.config/sysmon/sysmon.py — скрипт-бэкдор</li><li>~/.config/systemd/user/sysmon.service — systemd unit для автозапуска</li></ul><p>После установки бэкдор периодически обращается к https://checkmarx[.]zone/raw, скачивает файл в /tmp/pglog и выполняет его содержимое. Это даёт атакующим возможность удалённо выполнять произвольный код на скомпрометированных машинах в любой момент.</p><h2>Как обнаружили: баг в малвари устроил fork-бомбу</h2><p>Ирония истории в том, что атаку обнаружили благодаря <b>ошибке самих хакеров</b>.</p><p>Команда <a href="https://futuresearch.ai/blog/litellm-pypi-supply-chain-attack/">FutureSearch</a> столкнулась с проблемой случайно: MCP-плагин в IDE Cursor подтянул LiteLLM как транзитивную зависимость. Вредоносный .pth-файл запускал дочерний Python-процесс через subprocess.Popen. Но поскольку .pth-файлы срабатывают при каждом запуске интерпретатора, дочерний процесс тоже запускал малварь, та порождала ещё один процесс — и так далее.</p><p>Результат — <b>экспоненциальная fork-бомба</b>, которая мгновенно съедала всю оперативную память и вешала систему. Без этого бага стилер мог бы работать незамеченным значительно дольше.</p><blockquote>Мы были взломаны… тысячи людей, вероятно, прямо сейчас под атакой</blockquote><h2>Что делать, если вы затронуты</h2><p>Если в ваших проектах, CI/CD-пайплайнах или на рабочих машинах устанавливался LiteLLM 24 марта или позже — проверьте версию:</p><p>Если обнаружена версия 1.82.7 или 1.82.8:</p><ol><li><b>Удалите пакет и очистите кэши:</b> pip cache purge, rm -rf ~/.cache/uv</li><li><b>Проверьте наличие бэкдора:</b> файлы ~/.config/sysmon/sysmon.py и ~/.config/systemd/user/sysmon.service</li><li><b>В Kubernetes:</b> аудит kube-system на наличие подов node-setup-*, проверка секретов на несанкционированный доступ</li><li><b>Ротация всех креденшалов:</b> SSH-ключи, облачные токены (AWS, GCP, Azure), API-ключи, пароли БД, .env-файлы</li><li><b>Сетевые логи:</b> проверьте обращения к models.litellm[.]cloud, checkmarx[.]zone, scan.aquasecurtiy[.]org</li><li><b>Восстановление:</b> не ограничивайтесь удалением пакета — пересобирайте системы из известных чистых образов с закреплёнными (pinned) зависимостями</li></ol><p>На момент публикации публичных подтверждений массовой эксплуатации украденных ключей не зафиксировано, однако учитывая трёхчасовое окно и объём загрузок, число затронутых окружений может исчисляться тысячами.</p><h2>Индикаторы компрометации (IoC)</h2><p><b>Вредоносные домены:</b></p><ul><li>models.litellm[.]cloud — C2 для LiteLLM</li><li>checkmarx[.]zone — C2, используемый для персистентности и Checkmarx-атаки</li><li>scan.aquasecurtiy[.]org — C2 для Trivy-атаки</li></ul><p><b>Файлы на диске:</b></p><ul><li>litellm_init.pth в site-packages/</li><li>~/.config/sysmon/sysmon.py</li><li>~/.config/systemd/user/sysmon.service</li><li>/tmp/pglog</li><li>/tmp/.pg_state</li></ul><p><b>Kubernetes-артефакты:</b></p><ul><li>Поды с именами node-setup-* в kube-system</li><li>Контейнеры с именами kamikaze или provisioner</li></ul><p>Инциденту присвоен идентификатор <b>CVE-2026-33634</b>. Полный список IoC в формате CSV доступен в <a href="https://github.com/DataDog/security-labs-pocs">репозитории Datadog Security Labs</a>.</p><h2>Частые вопросы</h2><h3>Что такое LiteLLM и зачем его используют?</h3><p>LiteLLM — это open-source Python-библиотека и прокси-сервер, который предоставляет единый интерфейс для работы с более чем 100 LLM-провайдерами (OpenAI, Anthropic, Azure, AWS Bedrock и другие). Библиотека позволяет переключаться между моделями без изменения кода, автоматически обрабатывает фоллбэки и ретраи, отслеживает расходы. По данным PyPI, пакет загружается около 3,4 миллионов раз в день.</p><h3>Какие версии LiteLLM скомпрометированы?</h3><p>Скомпрометированы версии <b>1.82.7</b> и <b>1.82.8</b>, опубликованные на PyPI 24 марта 2026 года. Обе версии удалены. Последняя безопасная версия — <b>1.82.6</b>. Версия 1.82.8 опаснее: она запускает вредоносный код при каждом старте Python через механизм .pth-файлов, тогда как 1.82.7 активируется только при использовании прокси-сервера.</p><h3>Как проверить, затронут ли я?</h3><p>Выполните pip show litellm для проверки версии и find ~/.cache/uv -name "litellm_init.pth" для поиска вредоносного файла в кэше. Также проверьте наличие бэкдора: файлы ~/.config/sysmon/sysmon.py и ~/.config/systemd/user/sysmon.service. В Kubernetes ищите поды node-setup-* в namespace kube-system.</p><h3>Кто стоит за атакой?</h3><p>Атака приписывается группировке <b>TeamPCP</b>, которая за последнюю неделю марта 2026 года провела серию supply chain атак на инструменты разработки и безопасности: сканер уязвимостей Trivy (Aqua Security), GitHub Actions и расширения VS Code от Checkmarx, npm-пакеты, и в финале — LiteLLM. Инциденту присвоен идентификатор CVE-2026-33634.</p><h3>Что такое .pth-файл и почему он опасен?</h3><p>Файлы с расширением .pth, размещённые в директории site-packages, автоматически обрабатываются модулем site Python при каждом запуске интерпретатора. Исполняемые строки в таких файлах выполняются без явного импорта библиотеки. В случае LiteLLM 1.82.8 файл litellm_init.pth содержал дважды закодированный в base64 вредоносный скрипт, который запускался при каждом вызове python в скомпрометированном окружении.</p><h2>Выводы</h2><p>Ирония инцидента — в том, что LiteLLM по определению хранит API-ключи ко всем LLM-провайдерам организации. Атакующие выбрали пакет, который гарантированно имеет доступ к самым ценным секретам.</p><blockquote>Одна зависимость. Одна цепная реакция. Пять экосистем supply chain скомпрометированы менее чем за месяц</blockquote><p>TeamPCP целенаправленно атаковали инструменты безопасности — сканер уязвимостей, анализатор инфраструктурного кода, прокси для LLM. Эти инструменты по своей природе имеют широкий доступ, и компрометация одного из них даёт атакующим доступ ко всем секретам, которые этот инструмент должен был защищать.</p><p>Устанавливать пакеты из публичного реестра без проверки хешей и без lock-файлов — значит фактически отдать root-доступ любому, кто сможет скомпрометировать аккаунт мейнтейнера. Как ёмко выразилась Ноэлль Мурата, старший инженер по безопасности в Xcape: «Это цифровой эквивалент того, чтобы съесть бутерброд, найденный в метро, и удивиться пищевому отравлению».</p><p>Подробный технический анализ от Datadog Security Labs доступен <a href="https://securitylabs.datadoghq.com/articles/litellm-compromised-pypi-teampcp-supply-chain-campaign/">здесь</a>. Оригинальный отчёт FutureSearch — <a href="https://futuresearch.ai/blog/litellm-pypi-supply-chain-attack/">здесь</a>. Официальный security-апдейт LiteLLM — <a href="https://docs.litellm.ai/blog/security-update-march-2026">здесь</a>.</p><p><b>Проверьте свои зависимости сегодня.</b> Команды для аудита — <a href="https://tproger.ru/#remediation">в разделе выше</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Создатели Claude рассказали, как ИИ мешает развитию разработчиков</title>
      <link>https://tproger.ru/news/sozdateli-claude-rasskazali--kak-ii-navykam-razrabotchikov</link>
      <comments>https://tproger.ru/news/sozdateli-claude-rasskazali--kak-ii-navykam-razrabotchikov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/sozdateli-claude-rasskazali--kak-ii-navykam-razrabotchikov</guid>
      <description><![CDATA[<p>Создатели Claude показали: ИИ ускоряет написание кода, но ухудшает обучение разработчиков, понимание логики и навыки отладки</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/sozdateli-claude-rasskazali--kak-ii-navykam-razrabotchikov">Создатели Claude рассказали, как ИИ мешает развитию разработчиков</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 30 Jan 2026 10:36:15 GMT</pubDate>
      <content:encoded><![CDATA[<p>Исследователи из Anthropic — компании, стоящей за ИИ-моделью Claude — <a href="https://arxiv.org/pdf/2601.20245" rel="nofollow">опубликовали</a><a href="https://arxiv.org/pdf/2601.20245" rel="nofollow"></a> работу о том, как ИИ-ассистенты влияют на обучение программистов.</p><p>Вывод получился неприятным: с ИИ код действительно появляется быстрее, но понимание того, <i>как и почему он работает, заметно проседает</i>.</p><p>Работа основана на рандомизированном эксперименте с разработчиками, которым предложили освоить новую для них Python-библиотеку асинхронного программирования.</p><p>Одной группе разрешили активно пользоваться ИИ-помощником, другой — нет. После выполнения заданий, участников проверяли на понимание кода, концепций и умение отлаживать ошибки.</p><h2>Быстрее — не значит умнее</h2><p>Главный результат — участники с ИИ в среднем показывали худшие знания. Их итоговые тесты оказались примерно на 17% ниже, чем у тех, кто писал код без помощи моделей.</p><p>Особенно сильно пострадали навыки отладки и концептуального понимания: разработчики реже сталкивались с ошибками и, соответственно, реже были вынуждены разбираться, что именно пошло не так.</p><p>При этом ожидаемого скачка продуктивности тоже не случилось. В среднем ИИ не дал статистически значимого ускорения по времени.</p><p>Да, небольшая часть участников просто делегировала код модели и действительно закончила быстрее — но именно у них уровень понимания оказался самым низким.</p><h2>Как именно ИИ «ломает» обучение</h2><p>Исследователи выделили несколько паттернов работы с ИИ. Самый проблемный — полная делегация: когда разработчик копирует готовый код и движется дальше. Такой подход экономит время, но почти не оставляет знаний.</p><p>Более «здоровыми» оказались сценарии, где ИИ используют как справочник или собеседника: задают концептуальные вопросы, просят объяснить уже сгенерированный код, проверяют гипотезы.</p><p>В этих случаях участники сохраняли уровень обучения, но и выигрыша по скорости почти не получали.</p>]]></content:encoded>
    </item>
    <item>
      <title>Топ-3 платформы для обучения детей программированию в Майнкрафт</title>
      <link>https://tproger.ru/articles/top-3-platformy-dlya-obucheniya-detej-programmirovaniyu-v-majnkraft</link>
      <comments>https://tproger.ru/articles/top-3-platformy-dlya-obucheniya-detej-programmirovaniyu-v-majnkraft?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Блог Школа программирования Пиксель]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/top-3-platformy-dlya-obucheniya-detej-programmirovaniyu-v-majnkraft</guid>
      <description><![CDATA[<p>Топ-3 платформы, где работает minecraft python для детей: программирование для детей python minecraft, python minecraft курс онлайн для детей. Разберем программирование на python в minecraft, python minecraft bot и мод на майнкрафт пайтон: какие моды майнкрафт на питоне выбрать и как стартовать без перегруза.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/top-3-platformy-dlya-obucheniya-detej-programmirovaniyu-v-majnkraft">Топ-3 платформы для обучения детей программированию в Майнкрафт</a>»</p>]]></description>
      <category><![CDATA[Партнёрский материал]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 29 Jan 2026 08:30:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В мире компьютерных игр Minecraft занимает особое место. Для многих родителей это просто популярная игра — цифровой конструктор с кубической графикой. Но если присмотреться внимательнее, становится ясно: дети в Minecraft не столько играют, сколько проектируют. Они возводят сложные механизмы, продумывают логику автоматических ферм, планируют города и координируют действия в команде. По сути, они уже занимаются инженерным творчеством, просто не называют его так.</p><p>Именно эта творческая составляющая и делает Майнкрафт лучшим для обучения программированию детей. Мир игры представляет собой бесконечную «цифровую песочницу» с четкими правилами физики и логики. Здесь можно не только построить замок, но и запрограммировать лифт для него, систему освещения или автоматическую дверь, которая откроется, только если игрок положит в сундук нужный предмет.</p><p>Что делает программирование в Майнкрафт простым и доступным детям:</p><ul><li>Мотивация и контекст. Ребенок видит немедленный и наглядный результат своего кода. Не абстрактная задача «вывести числа на экран», а конкретная цель: «заставить стреляющую пушку автоматически строиться и стрелять» или «создать поезд, который сам едет по заданному маршруту». Программирование в Майнкрафт становится не самоцелью, а ключом к новым игровым возможностям.</li><li>Визуальная обратная связь. Ошибка в коде часто приводит к забавным последствиям в игре (например, поезд уезжает не туда или механизм строит себя неправильно). Это превращает отладку из рутины в увлекательное расследование.</li><li>Естественный переход от роли игрока к роли создателя. Ребенок перестает быть пассивным потребителем контента. Он становится архитектором, инженером, творцом собственных миров и правил.</li></ul><p>В Майнкрафт обучение программированию проходит органично. Сначала ребенок учится алгоритмически мыслить, планируя свои действия в игре. Потом он может автоматизировать рутинные операции с помощью простых команд. И, наконец, переходит к написанию настоящих скриптов, которые оживляют его самые смелые идеи.</p><p>Сегодня существуют специальные платформы, которые превращают обычный Minecraft в мощную учебную лабораторию. Они встраивают в игровой процесс инструменты для написания кода — от визуальных блоков до строк на Python. В этой статье мы подробно разберем три лучшие из таких платформ, которые помогут превратить увлечение игрой в первый серьезный шаг в IT.</p><p>Также приглашаем на онлайн-курсы для детей по программированию в Майнкрафт:</p><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/4d83dbf8-8d5e-4e0f-8530-a0c3c99947ef.webp" alt="обучение детей программированию в Майнкрафт" /></figure><ul><li><a href="https://pixel.study/minecraft-junior">Minecraft Junior. Создание игр для детей 7-10 ле</a><a href="https://pixel.study/minecraft-junior?utm_source=tproger.ru&amp;utm_medium=shkola-piksel&amp;utm_campaign=top-3-platformy-dlya-obucheniya-detey-programmirovaniyu-v-majnkraft" rel="nofollow">т</a>.</li><li><a href="https://pixel.study/minecraft">Игровая вселенная Minecraft. Программирование Pytho</a><a href="https://pixel.study/minecraft-junior?utm_source=tproger.ru&amp;utm_medium=shkola-piksel&amp;utm_campaign=top-3-platformy-dlya-obucheniya-detey-programmirovaniyu-v-majnkraft" rel="nofollow">n</a>.</li><li><a href="https://pixel.study/traektoria-ingener-mirov?utm_source=tproger.ru&amp;utm_medium=shkola-piksel&amp;utm_campaign=top-3-platformy-dlya-obucheniya-detey-programmirovaniyu-v-majnkraft" rel="nofollow">И</a><a href="https://pixel.study/traektoria-ingener-mirov">нженер миров Minecraft для детей от 10 до 13 лет</a>.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/d28f1d4d-22a6-4f23-a9b5-49fdec363604.webp" alt="обучение детей программированию в Майнкрафт" /></figure><p>Первый урок любого курса можно пройти бесплатно. Это полноценный урок с преподавателем, на котором ребенок создаст собственный мини-проект, познакомится с форматом обучения и сможет задать любые вопросы. Запись на пробный бесплатный урок <a href="https://pixel.study/demo">здес</a><a href="https://pixel.study/demo?utm_source=tproger.ru&amp;utm_medium=shkola-piksel&amp;utm_campaign=top-3-platformy-dlya-obucheniya-detey-programmirovaniyu-v-majnkraft" rel="nofollow">ь</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/a83434ad-ba16-43b3-bce5-33c2417b4faf.webp" alt="" /></figure><h2>Как выбрать платформу для программирования в Майнкрафт</h2><p>Прежде чем мы перейдем к обзору конкретных сервисов, разберемся, по каким параметрам их стоит оценивать. Основные критерии, на которые стоит обратить внимание:</p><p><b>1. Безопасность и закрытость среды.</b></p><p>Это абсолютный приоритет. Образовательная платформа должна быть защищенным пространством.</p><ul><li>Это значит, что ребенок взаимодействует только с заранее одобренным контентом, без возможности случайного выхода в открытые сетевые миры Minecraft с неконтролируемым общением. Все действия происходят либо в локальном мире на компьютере, либо в специально созданном классе с доступом по коду.</li><li>Это важно, так как позволяет полностью сосредоточиться на обучении, исключив риски, связанные с онлайн-игрой. А вы как родитель можете быть спокойны, зная, что ребенок в цифровой безопасности.</li><li>На что обратить внимание: Есть ли у платформы режим «офлайн» или защищенные «образовательные серверы»? Требует ли она регистрации и какие данные запрашивает? Есть ли модерация или родительский контроль?</li></ul><p><b>2. Удобный интерфейс, подходящий по возрасту.</b></p><p>Интерфейс — это мост между идеей ребенка и ее реализацией. Он должен быть интуитивно понятным:</p><ul><li>Для младших школьников (6-10 лет) идеальным стартом является визуальное программирование блоками. Команды выглядят как цветные пазлы, которые нужно сложить в определенном порядке. Это позволяет усвоить логику алгоритмов, не отвлекаясь на синтаксис языка. Для подростков (11+) важен плавный или прямой переход к текстовому коду (Python, JavaScript), который выглядит как у профессиональных разработчиков.</li><li>Это важно, так как слишком сложный интерфейс отпугнет, а слишком простой — быстро наскучит. Правильный инструмент, соответствующий уровню и амбициям ребенка, давая ему почувствовать прогресс.</li><li>На что смотреть: Предлагает ли платформа разные режимы (блоки/текст)? Насколько логично организовано меню? Есть ли подсказки и обучающие миссии «внутри» игры?</li></ul><p><b>3. Связь программ в Майнкрафт с реальными языками программирования.</b></p><p>Конечная цель — получить навык, применимый в реальном мире.</p><ul><li>Платформа должна учить принципам и синтаксису языков, которые используются в веб-разработке, анализе данных, создании игр (например, Python или JavaScript). Даже работа с блоками должна в итоге показывать, как этот же алгоритм выглядит в текстовом виде.</li><li>Это важно, так как обеспечивает преемственность в обучении. Навыки, полученные при программировании в Майнкрафт, ребенок сможет использовать для создания сайта, чат-бота или мобильного приложения.</li><li>На что смотреть: На каком языке пишется код? Можно ли переключиться с блоков на текст и увидеть эквивалент своего алгоритма? Есть ли справочник по командам (документация)?</li></ul><p><b>4. Стоимость и простота начала.</b></p><p>Этот критерий касается не только бюджета, но и организационных усилий.</p><ul><li>Нужно ли покупать специальную версию игры? Есть ли подписка на саму платформу? Можно ли начать с бесплатного полноценного курса, чтобы оценить интерес ребенка? Сколько стоит переход на следующий уровень?</li><li>Понимание ответов на эти вопросы позволяет планировать. Бесплатный старт — это возможность «попробовать без обязательств». Разумная плата за углубленный курс — это инвестиция в качественный контент и поддержку.</li><li>На что смотреть: Что входит в бесплатную версию? За что именно взимается плата (лицензия на игру, доступ к урокам, техподдержка)? Нужно ли устанавливать дополнительное программное обеспечение?</li></ul><p>Ориентируясь на эти четыре критерия, вы сможете оценить возможности каждой платформы.</p><h2>Программирование в Minecraft Education Edition</h2><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/5509ae20-b95f-45aa-88f4-95cfe74bcb9c.webp" alt="" /></figure><p>Minecraft Education Edition (M:EE) — это не модификация игры и не сторонний сервис. Это специальная версия игры, созданная компанией Microsoft именно для учебных целей.</p><h2>Как устроен Minecraft Education Edition</h2><p>Платформа представляет собой автономное приложение, которое можно установить на компьютер или планшет. Его ключевой компонент — Code Builder. Это специальный режим, который открывается нажатием одной клавиши (C) прямо в игровом мире. Он запускает панель программирования, не прерывая процесс игры в Майнкрафт.</p><p>Главный герой Code Builder — Исполнитель (The Agent), мобильный робот, которого ученик программирует. Вы не управляете им вручную, а пишете код, который заставляет Исполнителя двигаться, разрушать или ставить блоки, собирать предметы. Он идеально иллюстрирует основу программирования: мы даем четкие инструкции виртуальному объекту.</p><h2>С чего начать программирование в Minecraft Education Edition</h2><p><b>Доступ:</b></p><p>Платформа платная. Лицензию обычно приобретает учебное заведение (школа, кружок) для своих учеников. Для домашнего использования также можно купить индивидуальную подписку через официальный сайт Microsoft. Есть и бесплатная пробная версия.</p><p><b>Первые шаги:</b></p><p>После запуска ученик попадает в Библиотеку миров. Здесь — десятки готовых, методически продуманных уроков не только по программированию в Майнкрафт, но и по математике, истории, биологии. Для обучения коду нужно выбрать мир из раздела «Информатика» или «Основы программирования».</p><p><b>Интерфейс программирования:</b></p><p>Внутри мира нажимаем C и видим выбор: MakeCode (визуальные блоки) или Python. Можно начать с блоков, а в любой момент переключиться на вкладку «Python» и увидеть, как тот же самый код выглядит на текстовом языке.</p><h2>Пример реального задания</h2><p><b>Уровень «Островной старт» (для новичков)</b></p><p><b>Задача:</b></p><p>Исполнитель стоит на одном берегу реки. Нужно запрограммировать его так, чтобы он перешел на другой берег и построил там мост.</p><p><b>Что делает ребенок в MakeCode (блоки):</b></p><ol><li>Из меню перетаскивает блок при запуске (аналог главной функции).</li><li>Внутрь добавляет блок агент.двигаться вперед и выбирает количество шагов.</li><li>Добавляет блок агент.поставить и выбирает тип блока (например, дубовые доски).</li><li>Запускает код и наблюдает, как Исполнитель в точности выполняет последовательность.</li></ol><p><b>Переход к Python:</b></p><p>Ребенок переключает вкладку и видит сгенерированный код:</p><p>from mcpi.minecraft import Minecraft</p><p>mc = Minecraft.create()</p><p>agent.move("forward", 5)</p><p>agent.place("oak_planks")</p><p>Он понимает, что блоки — это оболочка для настоящих команд. Теперь он может экспериментировать, меняя числа и названия блоков прямо в текстовом редакторе.</p><h2>Ключевые плюсы программирования в Minecraft Education Edition</h2><ul><li>Педагогический подход. Каждый урок имеет четкие учебные цели, встроенные подсказки и постепенное увеличение сложности.</li><li>Полная безопасность. Миры локальны или защищены. Нет открытых сетевых серверов. Есть инструменты для учителя: возможность давать ресурсы, телепортироваться к ученику, управлять настройками мира.</li><li>Прямой путь к Python. Это главное преимущество. Ребенок с самого начала работает с синтаксисом одного из самых востребованных языков в мире, видя прямую связь между визуальными блоками и строками кода.</li><li>Учебный план. Платформа предлагает готовые проекты на 10-12 занятий, что удобно для учителей и родителей, ведущих систематическое обучение.</li></ul><h2>В каком возрасте можно изучать программирование в Minecraft Education Edition?</h2><ul><li>Начальная школа (6-10 лет). Старт в визуальном редакторе MakeCode внутри M:EE. Выполнение сюжетных заданий по строительству, фермерству.</li><li>Средние школьники 11-13 лет. Параллельная работа: выполнение задачи на блоках, затем анализ и редактирование кода на Python. Создание более сложных механизмов: автоматических дверей с редстоуном, сортировщиков предметов.</li><li>Подростки (14+). Программирование на чистом Python для решения комплексных задач: генерация ландшафтов по алгоритму, создание мини-игр с условиями и циклами.</li></ul><p>Minecraft Education Edition — это стандарт для обучения программированию детей в школе или на курсах. Это не самая дешевая платформа, но она предлагает максимально целостный путь от игрового конструирования к написанию кода на Python.</p><h2>Программирование в Майнкрафт с Microsoft MakeCode</h2><p>Microsoft MakeCode для Minecraft — это бесплатный веб-редактор, который превращает обычную, самую распространенную версию игры (Minecraft: Bedrock Edition) в инструмент для изучения программирования. Его главная философия — минимальный порог входа и максимальная свобода творчества.</p><h2>Как устроен Microsoft MakeCode</h2><p>MakeCode — это облачная платформа, работающая прямо в браузере. Она не требует установки дополнительного программного обеспечения, кроме самой игры. Принцип работы основан на создании поведенческих пакетов (behavior packs) — специальных дополнений, которые изменяют логику мира.</p><p>Редактор предлагает два равноценных интерфейса, между которыми можно мгновенно переключаться:</p><ul><li>Блочный редактор для визуального программирования в Майнкрафт с цветными блоками-командами.</li><li>Текстовый редактор JavaScript — код пишется в нем на упрощенном JavaScript с автодополнением и подсветкой синтаксиса.</li></ul><p>Код, написанный в MakeCode, вы экспортируете в один файл, который затем копируете в папку с сохранениями Minecraft. После этого в игре активируется новый мир с вашими правилами и возможностями.</p><h2>Программирование в Майнкрафт с MakeCode: как начать?</h2><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/bade159e-9439-4c1c-a4be-92abfac2423c.webp" alt="обучения детей программированию в Майнкрафт" /></figure><p>Доступ полностью бесплатный. Достаточно зайти на сайт <a href="https://makecode.com/">makecode.com</a> и выбрать раздел «Minecraft». Нужна только лицензия на базовую версию Minecraft (Bedrock Edition) на ПК, Xbox или планшете.</p><p><b>Первые шаги:</b></p><p>На сайте есть встроенные туториалы по программированию в Майнкрафт. Они пошагово объясняют, как создать первый проект — например, «Волшебную палочку», которая при использовании строит мост, или «Чат-бота», который отвечает в игровом чате.</p><p><b>Процесс:</b></p><p>Работа идет в браузере. После написания алгоритма можно нажать кнопку «Запустить» и увидеть симуляцию работы кода прямо в окне. Когда проект готов, его скачивают и активируют в игре.</p><h2>Пример реального задания</h2><p><b>Проект «Умная кирка».</b></p><p><b>Задача:</b></p><p>Создать кирку, которая при разрушении блока руды автоматически дропает (выбрасывает) вдвое больше ресурсов.</p><p><b>Что делает ребенок (в блочном редакторе):</b></p><ol><li>Использует событие при разрушении блока — это триггер, который запускает код при определенном действии.</li><li>Добавляет условие if для проверки типа разрушенного блока (например, железная руда).</li><li>Внутри условия размещает команду выбросить предмет с указанием предмета (железный слиток) и количества (умножая стандартное количество на 2).</li></ol><p><b>Переход к JavaScript:</b></p><p>Переключившись на вкладку JavaScript, ребенок увидит структурированный код:</p><p>blocks.onBlockBroken(function (block) {</p><p>if (block == IRON_ORE) {</p><p>mobs.dropItem(block.position, IRON_INGOT, 2)</p><p>}</p><p>})</p><p>Так он изучает базовые конструкции языка: функции, условия, работу с событиями — но в контексте знакомой игры.</p><h2>Ключевые плюсы платформы</h2><ul><li>Нулевая стоимость и доступность. Сам редактор бесплатен и работает онлайн — можно попробовать программирование в Майнкрафт без дополнительных инвестиций, используя уже купленную игру.</li><li>Прямая интеграция с Minecraft, уже установленным на домашнем ПК. Ребенок видит результат своего труда там, где он привык играть, что усиливает его мотивацию.</li><li>Мощный событийно-ориентированный подход. MakeCode учит современной парадигме программирования — реагированию на события (игрок наступил на плиту, ударил по блоку, нажал кнопку). А это — фундамент для создания интерактивных приложений.</li><li>Плавный переход к JavaScript. Язык, на котором пишет ребенок в текстовом режиме, — это актуальный JavaScript, один из главных языков веб-разработки. Навыки, полученные здесь, применимы для создания сайтов и веб-приложений.</li></ul><h2>Оптимальный возраст</h2><ul><li>Начальная школа (6-10 лет). Младшим школьникам, которые хотят научиться программированию в Майнкрафт с MakeCode, стоит начинать с блочного редактора и повторять готовые туториалы. Цель — понять цепочку «событие — условие — действие».</li><li>Средняя школа (11-14 лет). Создание собственных модификаций: оружие со спецэффектами, автоматические фермы, простые мини-игры. Активное переключение между визуальными блоками и JavaScript для анализа кода.</li><li>Подростки (14+). Программирование сложных систем на чистом JavaScript: многопользовательские игры в одном мире, алгоритмы procedural generation (создание ландшафтов), подключение внешних датчиков (через MakeCode для микроконтроллеров).</li></ul><p>Microsoft MakeCode для Minecraft подходит детям, которые изучают программирование дома, самостоятельно, или в кружках. Это прямой путь к пониманию того, как создаются моды и интерактивные приложения.</p><h2>Code.org — курс по программированию для детей «Час кода: Приключение в Minecraft»</h2><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/a2cedab5-3984-4a2e-8476-17e612cf8bfb.webp" alt="обучения детей программированию в Майнкрафт" /></figure><p>Если первые две платформы требуют наличия самой игры, то подход <a href="https://code.org/">Code.org</a> принципиально иной. Это не инструмент для модификации Minecraft, а отдельный интерактивный курс, который лишь использует эту вселенную и ее героев для обучения основам алгоритмики. Главная цель такого подхода — снять барьеры для первого знакомства с программированием и сделать его увлекательным.</p><h2>Как устроен курс по программированию «Час кода в Minecraft»</h2><p>Code.org — крупнейшая некоммерческая образовательная платформа, ее акция «Час кода» известна во всем мире. Курс «Приключение в Minecraft» — это серия головоломок, которые нужно решить прямо в браузере.</p><p>Ребенок не устанавливает игру и не работает в игровом движке. Вместо этого он видит упрощенный изометрический мир (вид сверху под углом), похожий на Minecraft, и управляет действиями персонажа (Стив, Алекс или другими), составляя программу из визуальных блоков. Каждый урок — это короткая задача: пройти к овце, собрать ресурсы, построить дом, избежать опасностей.</p><h2>Как попасть на курс программирования для детей «Час кода в Minecraft»</h2><p>Доступ абсолютно бесплатный, регистрация тоже не обязательна (хотя она позволяет сохранять прогресс). Все, что нужно, — компьютер или планшет с доступом в интернет и современным браузером.</p><p>Первые шаги:</p><p>Зайдите на страницу курса <a href="https://code.org/minecraft">code.org/minecraft</a> — ребенок сразу может приступить к курсу программирования. Он разбит на несколько тематических разделов (например, «Первое путешествие», «Создай свой мир», «Герой»). Начните с самого первого задания, где за две минуты объясняется базовый принцип: перетащить блок двигаться вперед в рабочую зону и нажать «Запустить».</p><p>Процесс:</p><p>Интерактивный туториал ведет пользователя шаг за шагом. Есть голосовые подсказки на русском языке и визуальные указатели, которые объясняют, что делать. Если программа составлена неверно, персонаж в симуляторе упрется в стену или выполнит действие не так. Нужно вернуться, исправить последовательность блоков и попробовать снова.</p><h2>Пример реального задания</h2><p>Уровень «Сбор урожая» (из раздела «Первое путешествие»).</p><p>Задача:</p><p>Дойти до тыквы, срубить ее и вернуться на старт.</p><p>Что делает ребенок:</p><ol><li>Анализирует карту: сколько шагов до тыквы? Нужно ли поворачивать?</li><li>Из палитры перетаскивает блоки двигаться вперед (2 раза) в область «Когда запущено».</li><li>Добавляет блок разрушить вперед (чтобы срубить тыкву).</li><li>Добавляет еще два блока двигаться вперед, чтобы вернуться.</li><li>Нажимает «Выполнить» и наблюдает за анимацией.</li></ol><p>Такие задачи по программированию в Майнкрафт учат ребенка последовательности команд, предварительному планированию маршрута, понятию «алгоритм» как четкому плану действий. На следующих уровнях появляются циклы повторить, чтобы не тащить много одинаковых блоков, и условия если, чтобы реагировать на препятствия.</p><h2>Ключевые плюсы программирования в Майнкрафт на курсе «Час кода»</h2><ul><li>Максимальная доступность и безопасность. Не нужно ничего устанавливать, платить за подписку, нет рисков онлайн-игр. Ребенок находится в полностью контролируемой учебной среде, которая отлично подходит для первого знакомства с программированием дома.</li><li>Рассчитан для новичков. Курс построен по принципу «от простого к сложному» с очень плавным нарастанием. Каждая новая концепция (цикл, условие) вводится в максимально наглядной и простой форме и связана с решением конкретной игровой проблемы.</li><li>Фокус на фундаментальных концепциях, а не синтаксисе. Здесь учат не языку Python или JavaScript, а вычислительному мышлению: разбивать задачу на шаги, видеть закономерности, использовать абстракции (циклы) и логические условия. Это основа для любого программирования в будущем, а не только в Майнкрафт.</li><li>Мотивирующая среда. Узнаваемые персонажи и предметы Minecraft создают высокий уровень вовлеченности. Ребенок чувствует, что решает «настоящие» игровые задачи.</li></ul><h2>Возраст и оптимальный путь обучения</h2><ul><li>Младшие школьники (6-10 лет) — основная целевая аудитория этого курса программирования в Майнкрафт. Он подходит для детей, которые еще не готовы работать в сложной среде, но уже хотят попробовать запрограммировать любимых героев. Все уроки достаточно просты и их можно пройти самостоятельно.</li><li>Школьники от 11 лет. Для них «Час кода» может служить первой ступенькой перед переходом на MakeCode или Education Edition. За 4-6 часов прохождения курса ребенок усваивает базовую логику, что сделает переход к текстовым языкам программирования менее пугающим.</li><li>Для всех возрастов. Курс отлично подходит для совместного занятия родителей с детьми, где взрослый может выступать в роли наставника, обсуждая логику решений.</li></ul><p>Code.org — это не платформа для создания модов к Minecraft, а отлично сделанный курс для знакомства детей с основами программирования. Он использует узнаваемый игровой бренд, чтобы заинтересовать ребенка и погрузить его в мир алгоритмического мышления. Он не ведет напрямую к написанию кода для игры, но закладывает важный фундамент для любого последующего обучения программированию.</p><p><i>Реклама. Рекламодатель ООО «Пиксель.Стади», ИНН 5074078988, erid: 2W5zFJ5erR6</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Minecraft+python: превращаем обучение в полноценную игру!</title>
      <link>https://tproger.ru/articles/minecraft-python--prevrashhaem-obuchenie-v-polnocennuyu-igru-</link>
      <comments>https://tproger.ru/articles/minecraft-python--prevrashhaem-obuchenie-v-polnocennuyu-igru-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Блог Школа программирования Пиксель]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/minecraft-python--prevrashhaem-obuchenie-v-polnocennuyu-igru-</guid>
      <description><![CDATA[<p>Как учиться программировать на Пайтон с помощью Майнкрафт? Рассказали про пользу и подобрали инструменты, с помощью которых интересно обучаться</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/minecraft-python--prevrashhaem-obuchenie-v-polnocennuyu-igru-">Minecraft+python: превращаем обучение в полноценную игру!</a>»</p>]]></description>
      <category><![CDATA[Партнёрский материал]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 28 Jan 2026 11:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Python заслуженно считается одним из лучших языков для старта в программировании. Его простой синтаксис похож на обычный английский, а значит, ребенок сможет быстро перейти от заучивания правил к решению реальных задач. Остается вопрос: где найти эти «реальные задачи», которые будут по-настоящему увлекать ребенка? Ответ прост: в Minecraft. Соединив Python и Minecraft, мы получаем не просто учебный курс, а целую лабораторию для экспериментов. Здесь код мгновенно оживает: от простой команды, зажигающей факел, до сложного алгоритма, строящего целый город.</p><h2>Почему связка «Minecraft + Python» работает так эффективно</h2><p>Объяснять абстрактные концепции программирования бывает сложно. Но когда эти концепции встроены в знакомый и любимый игровой мир, обучение становится естественным. Поэтому программирование на Python в Minecraft — это продуманный симбиоз, где сильные стороны одной технологии дополняют другую. Рассказываем, почему это работает.</p><h2>Визуальный результат и немедленная обратная связь в знакомой вселенной</h2><p>В традиционном обучении код часто выполняется в «пустоте» консоли. Ребенок написал программу — она вывела текст или число. Это похоже на «взрослое» программирование, но не всегда впечатляет.</p><p>В Minecraft же каждая строчка кода Python превращается в физическое изменение мира. Написал команду — и перед тобой выросла стена из алмазных блоков. Создал цикл — и твой помощник-бот посадил целое поле деревьев. Ошибся в алгоритме — и вместо ровной дороги получил хаотичную груду камней, что только подстегивает найти и исправить баг.</p><p>Эта прямая, визуальная связь между действием (написание кода) и результатом (изменение мира) закрепляет понимание. Ребенок не заучивает синтаксис, а использует его как инструмент для воплощения своих идей в Minecraft — и в итоге изучает программирование на Python, играя.</p><h2>Понятные и достижимые задачи с плавным переходом от простого к сложному</h2><p>Интерес поддерживается, когда цели понятны и кажутся выполнимыми. Внутри Minecraft эти цели формулируются на языке самой игры, который ребенок уже прекрасно понимает. Например:</p><ul><li>На начальном уровне: сделать так, чтобы при ударе по земле деревянным мечом появлялся цветок. Это учит работе с событиями (on_block_hit) и простым командам.</li><li>На среднем уровне: запрограммировать автоматическую ферму, которая будет сажать и собирать пшеницу, когда нажимаешь на рычаг. Здесь подключаются циклы (for или while) и более сложная логика.</li><li>На продвинутом уровне: создать портал, который телепортирует игрока в случайно сгенерированную крепость. Это уже проект, объединяющий работу с координатами, случайными числами (random module) и алгоритмами генерации.</li></ul><p>Каждая игровая задача при программировании на Python в Minecraft — это мини-проект с четким, осязаемым результатом. Ребенок видит прогресс: сегодня он заставил зажечься лампу, а через месяц его код управляет целым умным домом в игре.</p><h2>Создание собственных модов и мотивация</h2><p>Для ребенка, который привык существовать по правилам игры, возможность эти правила изменить — настоящая суперсила. Именно это и позволяют сделать моды для Майнкрафт, написанные на Пайтон.</p><p>Мод — это уже не простой скрипт, это полноценное дополнение, которое меняет геймплей. И Python, с его мощными библиотеками, отлично подходит для этого.</p><p>Что можно создать:</p><ul><li>новый инструмент (например, кисть, которая закрашивает область выбранным блоком);</li><li>новое существо (ручного дракона, который помогает в строительстве);</li><li>новую механику (систему квестов с диалогами и наградами).</li></ul><p>Процесс создания мода для Майнкрафт на Пайтон учит структурированию кода, разбивке большой задачи на модули и тестированию. Это уже уровень настоящего разработчика игр. Осознание, что ты не просто использовал чужой мод, а создал свой, и можешь поделиться им с друзьями, дает мощный заряд уверенности и желания развиваться дальше.</p><p>Приглашаем на наш <a href="https://pixel.study/minecraft">онлайн-курс по Python и Minecraft для детей</a>. Первый урок курса можно пройти бесплатно. На нем ребенок с помощью преподавателя создаст свой первый уникальный проект, познакомится с форматом обучения и сможет задать любые вопросы. Программирование на Python в Minecraft — это возможность для ребенка увидеть, как теория становится практикой в увлекательной игровой форме. Записаться на бесплатный пробный урок можно <a href="https://pixel.study/demo">здесь</a>.</p><h2>Инструменты и платформы для программирования в Minecraft на Python</h2><p>Прелесть связки Minecraft и Python в том, что можно начать с самого комфортного уровня и постепенно двигаться к сложным проектам. Выбор инструмента зависит от опыта ребенка, технических возможностей и целей.</p><h2>Minecraft + Python для детей без опыта в программировании: изучение основ через визуальные среды (MakeCode)</h2><p>MakeCode — идеальная точка входа для детей от 6 лет и для всех, кто никогда не программировал. Здесь код заменяют цветные блоки-команды, которые нужно собирать, как пазл или конструктор.</p><p>Ссылка на платформу: <a href="https://minecraft.makecode.com/">Microsoft MakeCode для Minecraft</a></p><p>Как работает MakeCode:</p><p>Это бесплатный онлайн-редактор, работающий в браузере. Ребенок составляет программу из блоков, а затем скачивает файл-модификацию для версии Minecraft: Bedrock Edition (это версия для Windows 10/11, Xbox, мобильных устройств). В игре активируется новый мир с вашими правилами.</p><p>Огромное преимущество платформы в том, что не нужно настраивать сложные среды разработки. Редактор работает онлайн, а в Minecraft ребенок играет в изолированном мире, созданном его кодом, что гарантирует безопасность.</p><p>Задачи в MakeCode понятны и визуальны: «Запрограммируй кнопку, чтобы при нажатии появлялся фонтан», «Сделай волшебную палочку, которая сажает деревья». Ребенок осваивает ключевые концепции: последовательность команд, циклы repeat и условия if, даже не подозревая, что изучает серьезные темы.</p><p>Как создание модов связано с Python? В MakeCode есть возможность переключаться между блоками и кодом. Хотя ребенок не использует Python напрямую, он изучает принципы программирования. Написав алгоритм блоками, можно переключиться на другую вкладку и увидеть, как этот же алгоритм выглядит в виде текстового кода. Это первый и очень важный шаг к пониманию, что за визуальным интерфейсом стоит реальный язык программирования.</p><h2>Minecraft Education Edition для перехода на текстовый Python</h2><p>Когда визуальное программирование усвоено, наступает время для первого настоящего текстового языка. Лучшая платформа для изучения Python на этом уровне — Minecraft: Education Edition.</p><p>Ссылка на платформу: <a href="https://education.minecraft.net/">Minecraft: Education Edition</a></p><p>Это специальная версия игры с встроенной средой программирования Code Builder. Внутри игрового мира нужно нажать всего одну клавишу (C), и откроется редактор, подключенный непосредственно к игре.</p><p>Главный объект для обучения — Агент, робот-помощник. Ребенок программирует именно его. Например, команды agent.move("forward") или agent.place("down") заставляют его двигаться и строить.</p><p>В Minecraft: Education Edition ребенок пишет код на настоящем Python. Он изучает:</p><ul><li>Импорт библиотек: from mcpi.minecraft import Minecraft</li><li>Переменные: height = 10</li><li>Циклы: for i in range(height):</li><li>Функции: def build_wall():</li></ul><p>На этом уровне ребенок может создавать что-то масштабное и полезное:</p><ul><li>Автоматическое строительство: скрипт, который по команде строит башню, пирамиду или дом заданного размера, используя вложенные циклы.</li><li>Игры внутри Minecraft: например, игра «Угадай число», где программа загадывает число, а игрок вводит догадки через чат игры, получая подсказки «больше» или «меньше». Это учит работе с вводом-выводом и случайными числами (random.randint).</li></ul><h2>Python и Minecraft для разработчиков модов и ботов</h2><p>Это уровень для подростков, которые освоили основы Python и хотят создавать по-настоящему сложные проекты, максимально приближенные к работе реального разработчика.</p><p>Для этого уровня используется стандартный Minecraft: Java Edition и специальные Python-библиотеки, такие как mcpi (Minecraft Pi API) или более мощные, как SpockBot или mineflayer (через Node.js, но управляемый Python). Здесь уже понадобится настройка локального сервера игры.</p><p>Что можно сделать на этом уровне:</p><p>Бот на Python для Minecraft — ребенок пишет не просто скрипт, а автономную программу, которая может «самостоятельно» существовать в игровом мире:</p><ul><li>Бот для сражений: анализирует окружение, находит мобов, атакует их, уворачивается. Это требует работы с обработкой событий в реальном времени.</li><li>Бот-строитель по чертежу: программа может читать схему постройки из файла (например, изображения или простого текстового массива) и воспроизводить ее в мире блок за блоком.</li></ul><p>Работа с файлами и внешними API, когда проекты выходят за рамки самой игры:</p><ul><li>Работа с файлами: сохранение и загрузка построек из файлов .json или .txt.</li><li>Внешние API: подключение к внешним сервисам. Классический пример — бот, который меняет погоду в игре в зависимости от реальной погоды буквально у вас за окном. Для этого ваш скрипт будет делать запрос к публичному метеосервису, получать данные и преобразовывать их в игровую команду /weather.</li></ul><p>На этом уровне ребенок знакомится с архитектурой приложений, взаимодействием разных систем, работой с сетью и сложными библиотеками. Он получает полное представление о том, как создается профессиональное программное обеспечение.</p><p>Независимо от возраста, начинать стоит с MakeCode (даже если ребенок уже подросток). Визуальное программирование даст быстрое чувство успеха и укрепит понимание логики. Затем можно перейти ко второму уровню, чтобы глубже разобраться, как Python интегрирован в Minecraft. Третий уровень — это отличная цель для личного проекта или итоговой работы на серьезном курсе программирования.</p><h2>С чего начать</h2><p>Теория — это важно, но программирование познается на практике. Самый лучший способ загореться идеей — увидеть, как всего несколько строчек кода, которые ребенок написал сам, меняют знакомый ему мир.</p><h2>Настраиваем окружение</h2><p>Первое правило обучения — ничего не сломать. Нам нужен способ, чтобы Python мог «общаться» с Minecraft, не вмешиваясь в лицензию игры и без риска для стабильности компьютера. Есть два основных, проверенных пути, и мы начнем с самого простого и безопасного.</p><h2>Как начать программировать на Python в Minecraft: Education Edition</h2><p>Это рекомендуемый способ для старта. Платформа создана для обучения, а значит, все проблемы безопасности и совместимости уже решены за вас.</p><p>Что нужно и как настроить:</p><p>Нужны установленный Minecraft: Education Edition (доступен для учебных заведений и домашнего использования) и стандартный редактор кода, например, Mu Editor или Thonny (они простые и бесплатные). Настраивать ничего не нужно. Внутри игры встроен Code Builder, который сам обеспечивает связь между скриптом и игровым миром. Ребенок просто пишет код в редакторе и запускает его в игре нажатием одной клавиши.</p><p>Это полностью безопасная среда, вся работа идет в изолированном учебном мире. Никакие сетевые настройки или модификации файлов игры не требуются.</p><h2>Python в Minecraft для продвинутых — локальный сервер</h2><p>Этот путь ближе к работе реального создателя модов, но требует больше шагов. Он подходит для подростков или родителей, готовых помочь с настройкой.</p><p>Что нужно:</p><ol><li>Официальная версия Minecraft: Java Edition.</li><li>Установленные Java и Python на компьютере.</li><li>Локальный игровой сервер Spigot или Paper (это специальные версии сервера, которые поддерживают плагины).</li><li>Плагин, который позволяет запускать Python-скрипты на сервере, например, RaspberryJuice.</li></ol><p>Нужно запустить на своем компьютере небольшой сервер Minecraft. Плагин выступает переводчиком между командами на Python и языком сервера. Игрок подключается к этому серверу из клиента игры и может управлять им через Python-скрипты.</p><p>Поскольку это локальная сеть (ваш компьютер подключается сам к себе), рисков из внешнего интернета нет. Все происходит внутри вашего ПК.</p><h2>Что можно создать? 10 проектов на Python для Minecraft</h2><p>Когда инструменты подготовлены, самое время выбрать идею для реализации, ведь конкретные проекты — лучший способ учиться. Предлагаем познакомиться с десятью реальными задачами, которые можно выполнить в Minecraft на Python — от простых до сложных. Каждый проект развивает определенный набор навыков и приносит видимый, осязаемый результат.</p><h2>1. Автоматическая ферма</h2><p>По нажатию кнопки или по таймеру система самостоятельно сажает семена, использует костную муку для ускорения роста и собирает урожай в сундук.</p><p>Какие навыки развивает: циклы (for для прохода по рядам), работа с блоками (определение типа блока — getBlock, его замена — setBlock), использование задержек (time.sleep) для симуляции роста.</p><p>Вот как это выглядит в коде (фрагмент):</p><p># Упрощенная логика одного ряда</p><p>for x in range(start_x, end_x):</p><p>mc.setBlock(x, y, z, block.WHEAT_SEEDS)  # Посадить семя</p><p>mc.setBlock(x, y+1, z, block.BONE_BLOCK) # Использовать "костную муку"</p><p>time.sleep(0.1)</p><p>mc.setBlock(x, y, z, block.WHEAT)        # Заменить на зрелую пшеницу</p><h2>2. Чат-бот для игры, дающий подсказки</h2><p>Этот бот отвечает на сообщения игрока в игровом чате. Например, на команду /help выводит список подсказок, на /coords — текущие координаты игрока. Можно создать бота-гида для собственной карты с квестами.</p><p>Какие навыки развивает: обработка строк (разбор введенной команды player_message.split()), условные операторы (if player_message == "/help":), вывод информации (postToChat).</p><h2>3. Генератор лабиринтов</h2><p>Такой скрипт по команде создает на местности лабиринт заданной ширины и высоты из каменных стен. Это уже серьезный проект в Minecraft на Python, результат которого впечатляет своим масштабом.</p><p>Какие навыки развивает: работа с двумерными массивами (списками списков) для хранения карты, освоение простых алгоритмов (например, алгоритм поиска в глубину), использование случайных чисел (random.randint) для выбора путей.</p><h2>4. Волшебная палочка, строящая замки</h2><p>При нажатии правой кнопкой мыши с определенным предметом в руке (палкой) программа строит под игроком или перед ним небольшую прегенерованную структуру — например, башню с лестницей внутри.</p><p>Какие навыки развивает: обработка событий (определение, когда игрок использует предмет), работа с относительными координатами (отсчет от позиции игрока), создание и использование функций для строительства типовых элементов (build_wall(), build_roof()).</p><p>Как это выглядит в коде (фрагмент):</p><p># Функция построения башни</p><p>def build_tower(pos_x, pos_y, pos_z, height):</p><p>for i in range(height):</p><p>mc.setBlocks(pos_x-2, pos_y+i, pos_z-2,</p><p>pos_x+2, pos_y+i, pos_z+2,</p><p>block.STONE_BRICK)</p><p># ... код для создания внутреннего пространства и лестницы</p><h2>5. Бот на Python для помощи в строительстве в Minecraft</h2><p>Бот, который следует за игроком и по команде /build house строит рядом стандартный дом из заранее заданных материалов.</p><p>Какие навыки развивает: работа с сущностями (поиск и отслеживание бота в мире), создание сложных многоэтапных алгоритмов, использование функций с параметрами для настройки размера постройки.</p><p>Ребенок научится автоматизировать рутинные действия и сделает первый шаг к созданию умных NPC (неигровых персонажей).</p><h2>6. Метеостанция, связанная с реальным миром</h2><p>Программа получает данные о реальной погоде в вашем населенном пункте через открытое API и меняет игровую погоду в зависимости от них: если в реальности идет дождь — в игре тоже начинает лить.</p><p>Какие навыки развивает: работа с внешними сетевыми запросами (библиотека requests), парсинг данных в формате JSON, интеграция разных систем — реального мира и виртуального.</p><h2>7. Головоломка «Угадай пароль из факелов»</h2><p>Игрок попадает в комнату с рычагами и факелами. Нужно установить факелы в определенной комбинации (последовательности), имитирующей двоичный код, чтобы открыть дверь.</p><p>Какие навыки развивает: логические операции, работа с двоичными данными, обработка состояний множества блоков (проверка, getBlock для каждого факела), создание интерактивных игровых механик.</p><h2>8. Автоматический сортировщик предметов</h2><p>Система из воронок и сундуков в Minecraft, управляемая на Python. Когда игрок бросает предмет в принимающий сундук, программа определяет его тип и отправляет по трубам (или телепортирует) в соответствующий тематический сундук: еда — в один, руды — в другой, оружие — в третий.</p><p>Какие навыки развивает: работа с инвентарем сущностей, сложные условия (if-elif-elif), организация логики обработки предметов.</p><h2>9. Мини-игра «Выживание на платформе»</h2><p>Это генератор арены в Minecraft, написанный на Python. Под игроком исчезают блоки, оставляя только движущиеся платформы. Нужно перепрыгивать с одной на другую, чтобы не упасть в лаву. С каждым уровнем скорость увеличивается.</p><p>Какие навыки развивает: создание игровых циклов, управление временем (time.time() для отслеживания скорости), динамическое изменение мира (удаление и установка блоков в реальном времени).</p><h2>10. Симулятор гравитации (песок/гравий)</h2><p>Программа берет столб из песчаных блоков, убирает нижний блок, и все блоки сверху падают вниз, занимая его место, как это происходит с настоящей гравитацией в игре, но под полным контролем кода.</p><p>Какие навыки развивает: понимание физических симуляций в упрощенном виде, работа с циклами в обратном порядке (от нижнего блока к верхнему), визуализация алгоритмов.</p><p>Эти проекты — лишь отправные точки. Каждый из них можно упростить для новичка или невероятно усложнить для юного разработчика с опытом, добавляя новые материалы, случайные элементы или мультиплеерный режим. Главное — начать с идеи, которая по-настоящему зажигает.</p><p><i>Реклама. Рекламодатель ООО «Пиксель.Стади», ИНН 5074078988, erid: 2W5zFJSQLsz</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Как сделать калькулятор на Python: урок с нуля для детей и подростков</title>
      <link>https://tproger.ru/articles/kak-sdelat-kalkulyator-na-python--urok-s-nulya-dlya-detej-i-podro</link>
      <comments>https://tproger.ru/articles/kak-sdelat-kalkulyator-na-python--urok-s-nulya-dlya-detej-i-podro?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Блог Школа программирования Пиксель]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-sdelat-kalkulyator-na-python--urok-s-nulya-dlya-detej-i-podro</guid>
      <description><![CDATA[<p>Инструкция о том, как сделать калькулятор на Python: урок с нуля для детей и подростков.
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-sdelat-kalkulyator-na-python--urok-s-nulya-dlya-detej-i-podro">Как сделать калькулятор на Python: урок с нуля для детей и подростков</a>»</p>]]></description>
      <category><![CDATA[Партнёрский материал]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 28 Jan 2026 06:30:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Программирование — не для избранных, а доступно каждому. И в этой статье мы докажем это на практике. Всего один урок — и ваш ребенок сможет написать простую и полезную программу, которую не стыдно показать друзьям, — калькулятор на Python.</p><h2>Почему Python — идеальный «первый язык» для юного программиста</h2><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/9da8a26a-c613-494f-8c38-f80ab61977d8.webp" alt="" /></figure><p>Выбирая первый язык программирования для ребенка, мы ищем не просто инструмент, а первый позитивный опыт. Первый язык программирования должен не отпугнуть сложностью, а наоборот — с первых же шагов показать, что создавать что-то своими силами интересно и по силам. Python в этой роли проявляет себя идеально: он понятен ученикам без опыта и с ним легко построить обучение от простого к сложному.</p><p>Главное достоинство Python — ясность. Его синтаксис (правила написания команд) создан так, чтобы код был читаемым. Сравните, как выглядит одна и та же команда — вывод текста на экран — на разных языках.</p><ul><li>В Python это: print("Привет, мир!")</li><li>В Java это выглядит сложнее: System.out.println("Привет, мир!");</li><li>А в C++ иначе: std::cout &lt;&lt; "Привет, мир!" &lt;&lt; std::endl;</li></ul><p>Для новичка, особенно ребенка, команда print (на английском — «напечатать») интуитивно понятна. В Python много таких ключевых слов, взятых из обычной речи: if (если), else (иначе), while (пока). Это снижает барьер входа. Ребенок учится не просто запоминать странные символы, а формулировать логические мысли на языке, который понимает и он сам, и компьютер.</p><p>Еще одно преимущество Python — возможность получить мгновенную обратная связь и сразу увидеть действие, производимое кодом. Python поддерживает интерактивный режим в таких средах, как Thonny или онлайн-песочницы (например, Trinket).</p><p>Выглядит это так: ребенок вводит строчку 3 + 5 * 2 и сразу после нажатия Enter получает ответ 13. Потом он пишет первую команду с ошибкой, например primt("Привет"), и программа сообщает: NameError: name 'primt' is not defined, объясняя, что не знает такой команды. Это превращает обучение в диалог с компьютером и живой эксперимент, где каждая идея проверяется за секунду. Такой формат поддерживает любопытство и не дает заскучать.</p><p>Python часто называют языком с низким порогом входа, но высоким потолком. Это значит, что начать легко, а возможности для роста практически безграничны. Начав с наших простых уроков Python для начинающих, ребенок в будущем сможет обратиться к самым современным направлениям IT, используя те же базовые принципы, которые изучит сейчас:</p><ul><li>Веб-разработка: создание сайтов и веб-приложений с помощью фреймворков Django и Flask.</li><li>Анализ данных и наука: Python — популярнейший инструмент для исследований. Библиотеки Pandas и NumPy помогают обрабатывать огромные объемы информации.</li><li>Искусственный интеллект и машинное обучение: библиотеки TensorFlow и Scikit-learn делают Python лидером и в этой стремительно развивающейся области.</li><li>Автоматизация и скрипты: написание программ для автоматизации рутинных задач на компьютере.</li><li>Создание игр: разработка 2D-игр с помощью модуля PyGame или использование Python для создания модов в Minecraft.</li></ul><p>Это реальные перспективы, которые открываются перед ребенком уже после первых простых уроков Python.</p><p>Урок «калькулятор на Python», который мы сегодня предлагаем, — отличный способ «пощупать» язык, получить быстрый результат и вдохновиться на большее. Он точно докажет, что программирование — это творческий и увлекательный процесс.</p><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/48369807-df4e-4821-8409-00b058b97844.webp" alt="" /></figure><p>А если ребенок проявит настоящий интерес, встанет вопрос: что дальше? Самостоятельные эксперименты — это прекрасно, но для уверенного, глубокого освоения языка важны система, последовательность и обратная связь от опытного преподавателя.</p><p>Приглашаем вашего юного программиста на <a href="https://clubpixel.ru/python?utm_source=tproger.ru&amp;utm_medium=shkola-piksel&amp;utm_campaign=kak-sdelat-kalkulyator-na-python-urok-s-nulya-dlya-detey-i-podrostkov-v-pikse" rel="nofollow">онлайн-курсы Python в Школе программирования для детей «Пиксель»</a>. На них дети не просто повторяют готовые скрипты, а учатся понимать саму логику программирования. Под руководством наших педагогов-практиков ребенок освоит язык комплексно: от основ синтаксиса до работы с готовыми библиотеками и создания собственных модулей.</p><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/d8d17e4d-f5d6-4f2e-aec7-ee708382fd5d.webp" alt="" /></figure><p>К концу курса у каждого ученика будет портфолио из реализованных проектов: возможно, это будет многоуровневая аркада с анимацией, личный помощник-бот с элементами искусственного интеллекта или интерактивный веб-проект. Это превратит увлечение в реальный, структурированный навык.</p><p>Лучший способ понять, подходит ли такой формат вашему юному разработчику, — попробовать. Первый урок курса по Python для детей можно пройти бесплатно. Это полноценное занятие, где ребенок под руководством преподавателя сразу погрузится в процесс создания и сможет оценить, насколько ему интересно двигаться дальше в мире кода. Записаться на пробный урок можно <a href="https://pixel.study/demo">здесь</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/0d493dbc-d661-486f-a482-dc95227265c5.webp" alt="" /></figure><h2>Подготовка к старту: 10 минут на настройку</h2><p>Все уроки с нуля обычно начинаются с подготовки инструментов, и наш урок по Python для детей не исключение. В программировании роль инструмента играет среда разработки — специальная программа или сайт, где вы будете писать и запускать свой код. Для первого знакомства с Python идеально подходят два простых и бесплатных варианта. Выберите тот, что вам ближе.</p><p>Вариант 1, с установкой приложения на ПК: Thonny</p><p>Thonny — это среда, созданная специально для обучения. Она отлично подходит для первых шагов, а ее встроенный «отладчик» (инструмент для поиска ошибок) поможет понять, что пошло не так, если программа не работает.</p><p>Как установить (это займет 5 минут):</p><ol><li>Откройте браузер и перейдите на официальный сайт: <a href="https://thonny.org/">thonny.org</a>.</li><li>Найдите на главной странице кнопку «Download» («Скачать») и выберите вашу операционную систему (Windows, macOS или Linux) и нажмите на нее.</li><li>Сайт автоматически предложит версии, подходящие для вашей операционной системы. Просто скачайте установочный файл.</li><li>Запустите скачанный файл и следуйте подсказкам мастера установки (всегда нажимайте «Далее» или «Установить»). Никаких сложных настроек не требуется.</li></ol><p>Как выглядит интерфейс:</p><p>Вариант 2, онлайн-песочница в браузере: Trinket.io</p><p>Если вы не хотите ничего скачивать и устанавливать, начните прямо в интернете. Trinket.io — это идеальная онлайн-площадка для экспериментов. Все, что нужно в этом случае, — браузер и доступ в Сеть.</p><p>Как начать работу:</p><ol><li>Перейдите по адресу: <a href="https://trinket.io/">trinket.io</a>.</li><li>Создайте бесплатный аккаунт (это полезно, чтобы сохранять свои проекты), но для первого раза можно начать и без регистрации.</li><li>Нажмите на кнопку «New Trinket» выберите язык программирования (Python).</li><li>Откроется окно редактора кода.</li></ol><p>Как выглядит интерфейс:</p><p>Окно браузера разделится на две части. Слева вы будете видеть свой код на Python, а справа — результат его выполнения. Это наглядно и максимально полезно для тех, кто проходит уроки Python начинающих: вы пишете команду, нажимаете кнопку запуска и мгновенно видите, что получилось.</p><p>В мире программирования есть старая традиция: первой программой на новом языке всегда должно быть приветствие миру. Это простой ритуал, который проверяет, все ли настроено правильно, и дарит мгновенное чувство успеха.</p><p>Независимо от того, выбрали вы Thonny или Trinket, сделайте следующее:</p><ol><li>В главном окне редактора кода (в Thonny — большое верхнее окно, в Trinket — левая панель) напишите строки:</li><li>python</li><li>print("Привет, мир!")</li><li>Запустите программу. В Thonny нажмите зеленую кнопку с изображением «пуска» (▶) на панели инструментов или клавишу F5 на клавиатуре. В Trinket нажмите большую белую кнопку «Run» («Запустить») вверху.</li></ol><p>В окне результата (в Thonny — это нижняя «оболочка», в Trinket — правая панель) вы увидите надпись:</p><p>text</p><p>Привет, мир!</p><p>Давайте подробно разберем, что произошло, чтобы понять, как компьютер нас «услышал»:</p><ul><li>print — это функция, то есть команда для Python. Ее единственная задача — выводить то, что указано в скобках, на экран. Само слово переводится как «напечатать».</li><li>("Привет, мир!") — это аргумент, то, что мы «даем в руки» команде print. Круглые скобки — обязательный признак функции.</li><li>"Привет, мир!" — это строка, простой текст. В Python весь текст всегда заключается в кавычки (двойные " или одинарные '), чтобы компьютер понял: это не команда, а просто слова для человека.</li></ul><p>Поздравляем! Вы только что написали, скомпилировали и запустили свою первую программу. Если надпись появилась — ваш «рабочий стол» программиста настроен правильно. Если что-то пошло не так (например, появилось красное сообщение об ошибке), проверьте, точно ли вы повторили строчку: нет ли опечатки в слове print, все ли кавычки и скобки на месте.</p><p>Этот простой успех — самый важный шаг. Он доказывает, что диалог с компьютером возможен, и вы уже можете им управлять. Теперь можно переходить к более интересным задачам.</p><h2>Урок по созданию калькулятора на Python</h2><p>А теперь добро пожаловать на первый настоящий урок по Python для начинающих! Мы создадим не просто калькулятор, а интерактивную программу, которая будет общаться с пользователем, задавать вопросы и давать умные ответы. Это первый шаг к пониманию, как компьютер обрабатывает информацию и принимает решения.</p><p>Наша цель — создать программу, которая спрашивает у пользователя два числа и тип операции (сложение, вычитание, умножение или деление), а затем корректно вычисляет и выводит результат.</p><h2>Шаг 1: Запоминаем данные и знакомимся с переменными</h2><p>Представьте, что переменная — это коробка с названием, в которую можно положить какое-то значение: число, текст или что-то еще. Когда программе нужно вспомнить это значение, она просто смотрит в коробку с нужным именем.</p><p>Чтобы создать такую «коробку» и положить в нее значение, мы используем знак равенства =:</p><p>число_1 = 10</p><p>число_2 = 5</p><p>В этом примере мы создали две переменные: число_1 со значением 10 и число_2 со значением 5. Теперь, написав print(число_1), программа выведет 10.</p><h2>Шаг 2: Спрашиваем у пользователя нужные числа с помощью функции input()</h2><p>Но наша программа должна работать с любыми числами, а не только с теми, которые задают при написании кода. Для этого мы используем функцию input(). Она останавливает выполнение программы, выводит текст-подсказку (который мы укажем в скобках) и ждет, пока пользователь введет что-то с клавиатуры и нажмет Enter. Все, что будет введено, функция input() вернет как текст (строку).</p><p>число_1 = input("Введите первое число: ")</p><p>число_2 = input("Введите второе число: ")</p><p>Теперь при запуске программы в консоли появится вопрос «Введите первое число:», и пользователь сможет ввести любое число.</p><h2>Шаг 3: Преобразуем текст в число с функцией int()</h2><p>Важный момент: для компьютера то, что ввел человек через input() — это просто текст (строка), даже если ввели цифры. С текстом нельзя выполнять арифметические операции. Попробуйте в оболочке Python выполнить "5" + "3". Результатом будет не 8, а "53" (конкатенация, то есть сложение строк).</p><p>Чтобы исправить это, мы используем функцию int(), которая преобразует текстовую строку, содержащую целое число, в настоящий числовой формат:</p><p>число_1 = int(input("Введите первое число: "))</p><p>число_2 = int(input("Введите второе число: "))</p><p>Обратите внимание: int() «оборачивает» input(). Это значит, что порядок действий будет таким: 1) выполнится input() и вернет текст, 2) функция int() попытается превратить этот текст в число. Если ввести буквы, программа выдаст ошибку — так компьютер говорит нам, что не может из этого сделать число.</p><h2>Шаг 4: Учим компьютер принимать решения — if/elif/else</h2><p>Теперь нам нужно спросить пользователя, что он хочет сделать с числами. Логика программы должна быть такой: ЕСЛИ пользователь выбрал знак "+", ТО сложить числа. ИНАЧЕ, ЕСЛИ он выбрал "-", вычесть, и так далее.</p><p>В Python для этого используются условные операторы: if (если), elif (иначе если) и else (иначе). После каждого из этих слов нужно поставить условие и двоеточие. Все команды, которые должны выполниться при истинности условия, записываются с отступом (обычно 4 пробела):</p><p>операция = input("Выберите операцию (+, -, *, /): ")</p><p>if операция == "+":</p><p>результат = число_1 + число_2</p><p>elif операция == "-":</p><p>результат = число_1 - число_2</p><p>elif операция == "*":</p><p>результат = число_1 * число_2</p><p>elif операция == "/":</p><p>результат = число_1 / число_2</p><p>else:</p><p>print("Ошибка: такой операции нет")</p><p>результат = None  # Специальное значение "ничего"</p><p>Обратите внимание на двойной знак равенства ==. В программировании один знак = — это присвоение (положить в коробочку), а два знака == — это сравнение (проверить, равны ли значения).</p><h2>Шаг 5: Красиво выводим ответ с f-строками</h2><p>Осталось красиво показать результат. Самый удобный способ в Python — f-строки. Перед кавычками в строке нужно поставить букву f, а переменные, которые нужно подставить, поместить в фигурные скобки {}. Python автоматически заменит {число_1} на значение переменной:</p><p>print(f"Результат: {число_1} {операция} {число_2} = {результат}")</p><p>Эта строчка выведет, например: Результат: 10 + 5 = 15.</p><h2>Готовый код калькулятора на Python с комментариями</h2><figure><img src="https://media.tproger.ru/user-uploads/134865/2026-01-28/9abc6a8d-e6a7-434f-8140-6a0cf19d5356.webp" alt="" /></figure><p>1. Запрашиваем у пользователя два числа и сразу преобразуем их из текста в целые числа:</p><p>число_1 = int(input("Введите первое число: "))</p><p>число_2 = int(input("Введите второе число: "))</p><p>2. Спрашиваем, какую математическую операцию нужно выполнить:</p><p>операция = input("Выберите операцию (+, -, *, /): ")</p><p>3. Проверяем выбор пользователя и выполняем соответствующее действие:</p><p>if операция == "+":</p><p>результат = число_1 + число_2  # Складываем</p><p>elif операция == "-":</p><p>результат = число_1 - число_2  # Вычитаем</p><p>elif операция == "*":</p><p>результат = число_1 * число_2  # Умножаем</p><p>elif операция == "/":</p><p>результат = число_1 / число_2  # Делим</p><p>else:</p><p># Этот блок выполнится, если пользователь ввел что-то отличное от +, -, *, /</p><p>print("Ошибка: такой операции нет")</p><p>результат = None  # Указываем, что результата нет</p><p>4. Если результат был вычислен (то есть не равен None), выводим его на экран:</p><p>if результат is not None:</p><p># Используем f-строку для красивого форматирования ответа</p><p>print(f"Результат: {число_1} {операция} {число_2} = {результат}")</p><h2>Как можно улучшить калькулятор на Python: дополнение к уроку для новичков</h2><ol><li>Защита от деления на ноль. Добавьте проверку в блок с делением (/). Прежде чем делить, спросите: if число_2 == 0: и, если это так, выведите сообщение «На ноль делить нельзя!», а не выполняйте деление.</li><li>Бесконечный цикл. Оберните весь код (кроме первой строчки с приветствием) в цикл while True:. Это позволит производить много вычислений подряд, не перезапуская программу. Не забудьте добавить условие для выхода (например, если пользователь введет слово «стоп» вместо числа).</li></ol><h2>Куда двигаться, когда урок по Python для начинающих пройден</h2><p>Вы только что создали свою первую настоящую программу — интерактивный калькулятор на Python. Это небольшой, но очень важный шаг. Давайте посмотрим, какие фундаментальные концепции Python вы уже освоили:</p><ul><li>Переменные. Вы научились хранить данные (числа, текст) в «коробках» с именами.</li><li>Ввод и вывод. Теперь ваша программа может общаться с миром: получать данные от пользователя через input() и показывать результаты с помощью print().</li><li>Условные операторы (if/elif/else). Вы заставили компьютер принимать решения, выбирать разные действия в зависимости от выбора пользователя.</li><li>Работа со строками и числами. Вы поняли разницу между текстом "5" и числом 5 и научились преобразовывать одно в другое.</li><li>Основы логики. Вы увидели, как программа выполняет команды шаг за шагом, сравнивает значения и следует по разным «веткам» кода.</li></ul><p>Это отличный фундамент. И теперь, когда вы почувствовали силу — возможность заставить машину следовать вашим правилам, — наверняка интересно, куда двигаться дальше.</p><p>Самый лучший способ учиться — сразу применять новое знание. Попробуйте усовершенствовать калькулятор на Python, это станет отличной практикой:</p><ol><li>Защита от ошибок. Сделайте программу более надежной. Что будет, если вместо числа ввести буквы? А если при делении ввести ноль? Используйте try...except и дополнительные проверки, чтобы калькулятор давал полезные подсказки, а не просто завершался с ошибкой.</li><li>Новые операции. Добавьте возведение в степень, вычисление квадратного корня или остатка от деления. Это потребует изучить новые операторы или функции из библиотеки math.</li><li>История вычислений. Научите программу запоминать последние 3-5 операций и выводить их по отдельной команде (например, при вводе слова «история»). Для этого вам понадобится познакомиться со списками (list) — одной из самых полезных структур данных в Python.</li><li>Графический интерфейс (GUI). Самый эффектный шаг — превратить консольную программу в окно с кнопками и полями ввода. Начните с простой библиотеки tkinter, которая уже есть в Python. Создание даже самого простого окошка с кнопками даст базовое понимание того, как устроены привычные всем программы.</li></ol><p>Не бойтесь экспериментировать и искать идеи. Если что-то не получается — это нормально. Каждая ошибка приближает к решению.</p><p>Где искать вдохновение: посмотрите проекты других начинающих на платформах вроде Code.org или CheckiO. Попробуйте повторить или улучшить их.</p><p>Также важна постоянная практика. Пытайтесь автоматизировать с помощью Python что-то из своей жизни: подсчет карманных денег, генератор случайных заданий для тренировок, простой шифровальщик для секретных сообщений друзьям.</p><p>Главное — не останавливаться. Один работающий проект, который вы сделали сами, ценнее десятка прочитанных глав учебника.</p><p><i>Реклама. Рекламодатель ООО «Пиксель.Стади», ИНН 5074078988, erid: 2W5zFJnMUoz</i></p>]]></content:encoded>
    </item>
  </channel>
</rss>