Pull to refresh

Comments 44

Смешно с того как программисты с опытом начинают относиться к ИИ как к быстрому джуниору)

Мне сложно говорить про отношение других программистов, но лично я просто проводил эксперимент. :) Без всяких планов на будущее. :)

Вы пошли не совсем верной дорогой, использовав ChatGPT - он для "домохозяек".

Попробуйте в Claude Code или Cursor те же задачи с разными моделями. Удивитесь результату.

Я несколько лет скептически относился к нейросетям. Но год назад попробовав, теперь смотрю на себя прошлого как на пещерного человека.

Пользуюсь Cursor. Он и по SSH подключится, посмотрит что на сервере случилось, гигабайтные логи почитает, в проекте нужный файл найдет с нужной по смыслу мне строкой. И даже при подключении MCP браузера может сам открывать сайт и тестировать.

Я превратился из раба в рабовладельца. Из наборщика кода на клавиатуре в архитектора.

Когнитивные способности не снизились, я могу и сейчас писать код сам.

Да, я понимаю, что ЖПТ не для программирования. Но я хотел бесплатные сети, а вроде бы и Курсор, и Клауде - платные, разве нет?
Насчет того, чтобы давать роботу доступ к сайту своему - нет, не решусь. :) Нет доверия. Спросить - да, я спрашивал один раз уж не помню что, когда саппорт хостинга был завален работой и долго отвечали. Кажется, как раз ЖПТ и помог (я кроме него только Джемени иногда применял). Так что я не отрицаю пользу нейросетей, я просто не согласен с наблюдаемым фанатизмом и разговорами про "все перевести на ИИ" даже в рамках государства. Не знаю как лет через 10, но пока точно нет. Да и потом стремно...

Они могут работать бесплатно, но хотят забугорный номер. Попробуйте Ollama cloud и сцепите его с каким нибудь vscode

Номер? Там регистрация по телефону? Ради тестирования нет смысла...

Там хорошие лимиты и Sonnet Max очень хороша даже в каких-то забытых языках программирования

Даже в режиме чата она вам создаст реальные файлы и позволит их скачать и запустить

Спасибо, буду иметь в виду!

Поражаюсь конечно временами. Как люди с опытом и пониманием программирования не врубаются как это работает. Агентные системы настолько несутся уже, а вы все на вбейсике кодите. Я уже полгода код сам не пишу, забыл вообще об этом. Зачем?джун не нужен, верстальщик не нужен, тестировшик не нужен. Нужен крутой диз, который умеет в нетртвиальные дизайны, нужен хороший ui/ux - который понимает что человеку УДОБНО и как интерфейсы делать. Нужен сетевик безопасник, хорошо знающий какие уязвимости МОГУТ БЫТЬ. А вот кодописец не нужен. Делай ревью, не копи техдолг, ставь норм тз, и этот конвейер сумасшедший начнёт тебе строить такие штуки что глаза на лоб полезут. И да в бесплатных вариантах не очень работает, начните с 20$ в месяц. Уверен через пару недель купите макс прдписку, в через два месяца и пару тройку завершенных проектов на которые бы раньше ушёл ГОД, пойдёте еще дальше.

За советы, конечно, спасибо, но Вы исходите из собственного опыта и потребностей. :) А они у всех разные. Нет у меня никаких "проектов", я работаю в НИИ, где иногда есть потребность в написании узкоспециальной программы. А в остальное время я пишу программы для себя как замену готовым, но мне не подходящим (типа редактора вместо дурацкого блокнота) или как решение нужных мне в жизни задач (как написал себе базу контактов).

Сайты я тоже делаю в основном для себя или для редких конкретных заказчиков. То есть, никаких "гонок" у меня нет, никаких "джунов" в подчинении тоже. А писать программы мне нравится, это мое давнее хобби (еще с советских ПМК), которое много лет назад стало профессией в силу обстоятельств. Мне интересно и приятно писать программы, понимаете? :) И платить 20 долларов в месяц (а в полном пакете, видимо, намного больше) непонятно кому и зачем - извините, не стану.

А что касается языка - так чем плох Бейсик? Да, нынешние его версии "опущены" до уровня Си (раньше было больше возможностей), но тут уж ничего не поделаешь. Так что в целом меня все вполне устраивает. А все эти LLM пусть идут лесом, чтобы еще им платить... :)

лолшто? Нейросети это инстрмент, даже с голым чатом можно разное творить, главное что бы контекст влазило, а уж codex и вовсе держит пальму первества после того как антропики нахрен сломали нормально работающий опус 4.6 (и 4.7-4.8 не сильно улучшили ситуацию)

К слову текущий мифос как раз дает ощущение того самого, "зимнего" клода на 4.6. Так что возможно что даунгрейд, что был в начале года, был только для того что бы новая модель казалась революционной.

А Cursor как по мне не дотягивает ни до чата ни но антропиков.

Я не так глубоко в теме разных ИИ, ибо на практике пользуюсь своим интеллектом, а не искусственным. :) Но тенденция такая активная, что захотелось вообще проверить - а насколько В ПРИНЦИПЕ это возможно. Понятно, что чатжпт тут не самый лучший образец. Но многие пользуются им...

Смотрите, вам человек пишет чуть-чуть неочевидную вещь: ChatGPT ChatGPT рознь. Есть ChatGPT в браузере, а есть ChatGPT в Codex - десктопном приложении. Так вот, писать код в чате - это было очень плохо еще два года назад, когда альтернатив в виде приложений с агентами не было. Сейчас никто в здравом уме не будет вайбкодить в чате, это делается в Codex или Claude Code и аналогах. И лучший результат достигается, если сначала через plan mode описать, что у тебя есть, и что хочешь получить. Потом ручками подредактировать план, где не согласен, прогнать "ask me questions" и уже после всего этого, по вылизанному плану поставить прогон "вайбкодинга". Тогда да, что-то может и получится.

Можете мне в ЛС или сюда прислать задачку из тех, что вы скармливали чату - я вам пришлю, что получилось БЕЗ ручных изменений.

Вы правы, я прекрасно понимаю, что взялся тестировать далеко не максимально продвинутую платформу. Но с другой стороны, я предлагал достаточно простые задачи. И в этом плане мне нечего писать в личку, потому что все описано в статье, ничего иного я чату не предлагал. :)
И да - мне было бы интересно увидеть вариант использования других ИИ (более профессиональных) с такими же входными данными (задачами). Просто у меня там нет аккаунтов, нет этих приложений и т.д., поскольку я не планирую их использовать, так и ставить смысла не вижу. Но у кого есть - да, мне было бы интересно посмотреть!

Проблема тут в том, что во всех примерах кроме первого (где у ChatGPT получилось), я не вижу реального промпта, который вы использовали. Только воспоминания и ощущения. Ткните меня, или киньте в меня реальным промптом, с которым не получилось - я попробую на Opus или Fable. Чтобы сравнение было прямым и честным.

Ну, промпт в часах был примерно таким как указано. И в целом задачу ИИ решил, хотя чисто эстетически не все идеально, но это я и не оговаривал.
А вот с третьей задачей сложнее. Я ведь тестировал не только сам ИИ, но и то, насколько сложно объяснить нейросети, что именно мне нужно! Чисто практически это не менее важно: если промпт по сложности и объему равен программе, то зачем нужен ИИ? :)

Поэтому я не писал четкий промпт, а именно своими словами объяснил задачу примерно так, как и описано. Тут ведь многое потом в диалоге с ЖПТ еще уточнялось... Я сейчас не могу авторизоваться там, чтобы восстановить весь диалог (а первая попытка была вовсе без авторизации и там не сохранилось ничего).
Если запустите мой вариант (как эталон), то я как раз буду рад посмотреть, какой промпт Вы используете для Fable или Opus. Возможно, я действительно просто не умею формулировать задачи, ибо не имею опыта с ИИ. Но изначально и предполагалось, что я - дилетант и хочу подставить работу ИИ в качестве якобы своей. Иначе зачем мне ИИ? Сам напишу... :)

Чисто практически это не менее важно: если промпт по сложности и объему равен программе, то зачем нужен ИИ? :)

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

Если запустите мой вариант (как эталон), то я как раз буду рад посмотреть, какой промпт Вы используете для Fable или Opus.

Попробую.

Вы только пишете чего хотите достичь, а как - это уже не ваша забота в идеале.

Так это же тоже промпт по сути! И от того, как я его напишу (насколько подробно и однозначно) будет зависеть и результат!
Кстати, ЖПТ пару раз задавал уточняющие вопросы именно в плане моих формулировок. То, что мне казалось однозначно понятным, он трактовал по-разному и требовал конкретики. В частности, про вращение колец в двух плоскостях. Мою формулировку он не понял, хотя вроде она очевидна...

Вот код, что получился:
# -*- coding: utf-8 -*-
"""
Графическая демка на чистой стандартной библиотеке Python (tkinter).

Окно 640x480.

Режим 1 (синий фон):
    Два жёлтых кольца вращаются синхронно — одно "справа налево"
    (вокруг вертикальной оси), другое "сверху вниз" (вокруг горизонтальной).

Режим 2 (чёрный фон):
    Из центра растут круги стандартных цветов по кругу:
    красный, жёлтый, зелёный, голубой, синий, фиолетовый, снова красный ...
    Два варианта (переключаются пробелом):
        A — новые круги поверх старых, яркость падает от центра к периферии;
        B — всё изображение постепенно темнеет до чёрного, поэтому каждый
            цвет как будто рисуется на чёрном фоне.

Управление:
    Enter  — переключение между режимом 1 и режимом 2
    Space  — в режиме 2 переключение варианта A / B
    Esc    — выход
"""

import math
import tkinter as tk

# --------------------------------------------------------------------------- #
#  Константы
# --------------------------------------------------------------------------- #
W, H = 640, 480                 # размер окна
CX, CY = W // 2, H // 2         # центр
FRAME_MS = 20                   # ~50 кадров в секунду

BG_BLUE = "#0000a0"             # синий фон режима 1
BG_BLACK = "#000000"           # чёрный фон режима 2
YELLOW = "#ffff00"

# Режим 1 — кольца
RING_R = 150                    # радиус колец
RING_WIDTH = 5                  # толщина линии кольца
SPIN = 0.045                    # прирост угла за кадр (скорость вращения)

# Стандартные цвета (по кругу цветового спектра)
STD_COLORS = [
    (255, 0, 0),     # красный
    (255, 255, 0),   # жёлтый
    (0, 255, 0),     # зелёный
    (0, 255, 255),   # голубой
    (0, 0, 255),     # синий
    (255, 0, 255),   # фиолетовый
]
NCOL = len(STD_COLORS)

# Режим 2 — общие
R_MAX = 430                     # радиус, покрывающий углы окна (диагональ/2 ~ 400)

# Вариант A (яркость по радиусу)
BAND_STEP = 5                   # шаг вложенных окружностей (px)
BAND_W = 44                     # ширина одной цветовой полосы (px)
SPEED_A = 1.3                   # скорость "выползания" полос из центра

# Вариант B (затухание в чёрный)
SPEED_B = 3.0                   # скорость роста радиуса кольца (px/кадр)
RING_W_B = 4                    # толщина растущего кольца
SPAWN_EVERY = 3                 # каждые N кадров рождается новое кольцо
FADE = SPEED_B / R_MAX          # за сколько кадров кольцо гаснет до чёрного


def dim_hex(rgb, k):
    """Цвет rgb, приглушённый коэффициентом k (0..1), в виде #rrggbb."""
    if k < 0:
        k = 0.0
    elif k > 1:
        k = 1.0
    r, g, b = rgb
    return "#%02x%02x%02x" % (int(r * k), int(g * k), int(b * k))


class Demo:
    def __init__(self, root):
        self.root = root
        self.canvas = tk.Canvas(root, width=W, height=H,
                                highlightthickness=0, bg=BG_BLUE)
        self.canvas.pack()

        # общее состояние
        self.mode = 1            # 1 — кольца, 2 — круги
        self.variant = "A"       # A / B (только для режима 2)
        self.theta = 0.0         # угол вращения колец
        self.g = 0.0             # фаза роста кругов
        self.frame = 0           # счётчик кадров (для рождения колец в B)

        # идентификаторы графических объектов
        self.ring1 = None
        self.ring2 = None
        self.bands = []          # вложенные окружности варианта A
        self.rings = []          # активные растущие кольца варианта B: {id, r, col, bright}

        # горячие клавиши
        root.bind("<Return>", self.toggle_mode)
        root.bind("<KP_Enter>", self.toggle_mode)
        root.bind("<space>", self.toggle_variant)
        root.bind("<Escape>", lambda e: root.destroy())

        self.build()
        self.tick()

    # ------------------------------------------------------------------ #
    #  Переключение режимов
    # ------------------------------------------------------------------ #
    def toggle_mode(self, event=None):
        self.mode = 2 if self.mode == 1 else 1
        self.build()

    def toggle_variant(self, event=None):
        if self.mode == 2:
            self.variant = "B" if self.variant == "A" else "A"
            self.build()

    # ------------------------------------------------------------------ #
    #  Пересборка сцены под текущий режим
    # ------------------------------------------------------------------ #
    def build(self):
        self.canvas.delete("all")
        self.bands = []
        self.rings = []
        self.g = 0.0
        self.frame = 0

        if self.mode == 1:
            self.root.title("Режим 1: два вращающихся кольца   |   Enter — сменить режим,  Esc — выход")
            self.canvas.config(bg=BG_BLUE)
            # два кольца — контур (fill=""), обновляются в кадре
            self.ring1 = self.canvas.create_oval(0, 0, 0, 0, outline=YELLOW,
                                                 width=RING_WIDTH, fill="")
            self.ring2 = self.canvas.create_oval(0, 0, 0, 0, outline=YELLOW,
                                                 width=RING_WIDTH, fill="")
        else:
            title_v = "A: ярче в центре" if self.variant == "A" else "B: затухание в чёрный"
            self.root.title("Режим 2 [%s]   |   Enter — сменить режим,  Space — вариант A/B,  Esc — выход"
                            % title_v)
            self.canvas.config(bg=BG_BLACK)
            if self.variant == "A":
                # набор вложенных закрашенных окружностей: внешние снизу, внутренние сверху
                r = R_MAX
                while r > 0:
                    oid = self.canvas.create_oval(CX - r, CY - r, CX + r, CY + r,
                                                  outline="", fill=BG_BLACK)
                    self.bands.append((oid, r))
                    r -= BAND_STEP
                # сдвигаем фазу так, чтобы в центре цикл стартовал с красного
                if self.bands:
                    self.g = float(self.bands[-1][1])
            # вариант B создаёт кольца динамически в tick()

    # ------------------------------------------------------------------ #
    #  Один кадр анимации
    # ------------------------------------------------------------------ #
    def tick(self):
        if self.mode == 1:
            self.draw_rings()
        elif self.variant == "A":
            self.draw_circles_a()
        else:
            self.draw_circles_b()

        self.root.after(FRAME_MS, self.tick)

    # --- Режим 1: вращающиеся кольца ---------------------------------- #
    def draw_rings(self):
        self.theta += SPIN
        c = abs(math.cos(self.theta))   # фаза кольца 1
        s = abs(math.sin(self.theta))   # фаза кольца 2 — сдвиг на 90°, чтобы оба кольца
                                        # были видны всегда (не сливались в одно) и вращались
                                        # синхронно, каждое в своей плоскости

        # кольцо 1: вращение вокруг вертикальной оси -> меняется ширина (справа налево)
        a = max(1, RING_R * c)
        b = RING_R
        self.canvas.coords(self.ring1, CX - a, CY - b, CX + a, CY + b)

        # кольцо 2: вращение вокруг горизонтальной оси -> меняется высота (сверху вниз)
        a = RING_R
        b = max(1, RING_R * s)
        self.canvas.coords(self.ring2, CX - a, CY - b, CX + a, CY + b)

    # --- Режим 2, вариант A: яркость по радиусу ----------------------- #
    def draw_circles_a(self):
        self.g += SPEED_A
        for oid, r in self.bands:
            idx = int((self.g - r) // BAND_W) % NCOL
            bright = 1.0 - r / R_MAX          # ярко в центре, темно по краям
            self.canvas.itemconfig(oid, fill=dim_hex(STD_COLORS[idx], bright))

    # --- Режим 2, вариант B: затухание в чёрный ----------------------- #
    def draw_circles_b(self):
        self.frame += 1

        # рождение нового кольца из центра (первый круг — красный)
        if self.frame % SPAWN_EVERY == 0:
            idx = (self.frame // SPAWN_EVERY - 1) % NCOL
            oid = self.canvas.create_oval(CX, CY, CX, CY,
                                          outline=YELLOW, width=RING_W_B)
            self.rings.append({"id": oid, "r": 1.0,
                               "col": STD_COLORS[idx], "bright": 1.0})

        # рост + затухание существующих колец
        alive = []
        for ring in self.rings:
            ring["r"] += SPEED_B
            ring["bright"] -= FADE
            r = ring["r"]
            if r > R_MAX or ring["bright"] <= 0:
                self.canvas.delete(ring["id"])
                continue
            self.canvas.coords(ring["id"], CX - r, CY - r, CX + r, CY + r)
            self.canvas.itemconfig(ring["id"],
                                   outline=dim_hex(ring["col"], ring["bright"]))
            alive.append(ring)
        self.rings = alive


def main():
    root = tk.Tk()
    root.resizable(False, False)
    Demo(root)
    root.mainloop()


if __name__ == "__main__":
    main()
Вот история чата:

Окно 640*480, синий фон. Вращаются два желтых кольца справа налево и сверху вниз, синхронно. Это один режим. Переключение на второй - клавишей “Enter”.

Второй режим. Черный фон окна и из центра растут круги стандартных цветов: красный, желтый, зеленый, голубой, синий и фиолетовый. Потом снова красный и т.д. Этот режим имеет два варианта (переключение клавишей “Space”):

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

Отличная задача — классическая графическая демка. Прежде чем писать, мне нужно уточнить одну вещь, которая полностью определяет результат: на чём делать. Остальное я реализую по спецификации (интерпретацию колец опишу ниже и вы поправите, если что).

● User answered Claude’s questions: · На какой платформе/языке сделать программу с окном 640×480, обработкой Enter/Space и анимацией? → HTML5 Canvas + JS Interrupted · What should Claude do instead?

Я передумал - хочу на Python

Понял — делаю на Python (pygame). Сначала проверю, что установлено на машине.

Ran 1 shell command Остальное я реализую по спецификации (интерпретацию колец опишу ниже и вы поправите, если что).

Ran 1 shell command

Python 3.12.9 есть, а pygame нет. Ставлю pygame:

Ran 1 shell command Interrupted · What should Claude do instead?

Давай только стандартные библиотеки

Понял — только стандартная библиотека, без pygame. Тогда GUI на tkinter (входит в стандартную поставку Python), рисование на Canvas. Проверю, что tkinter доступен:

Ran 1 shell command

tkinter на месте (Tk 8.6). Пишу программу целиком на стандартной библиотеке — окно на tkinter, рисование на Canvas, анимация через after().

● Write(demo.py) Wrote 227 lines to demo.py -- coding: utf-8 -- “” рафическая демка на чистой стандартной библиотеке Python (tkinter).

Окно 640x480.

Режим 1 (синий фон): Два жёлтых кольца вращаются синхронно — одно “справа налево” (вокруг вертикальной оси), другое “сверху вниз” (вок … +217 lines

Как закончишь, перепроверь ADVERSARIAL агентом

Программа готова. Теперь прогоню собственный «дымовой» тест (без всплывающего окна — withdraw()), чтобы убедиться, что все три состояния рисуются без исключений и жизненный цикл колец в варианте B корректен.

● Write(C:\Users\mistd\AppData\Local\Temp\claude\D–habr\141d40eb-0ceb-4b0a-a95d-d449fbf8ab07\scratchpad\smoke.py) Wrote 74 lines

Ran 1 shell command

Собственный дымовой тест зелёный:

  • Режим 1 — 402 кадра без ошибок, кольца схлопываются в линии на θ=90°.

  • Вариант A — 86 вложенных окружностей, центр яркий (#00fc00), край тёмный (#000000) → градиент «ярко в центре, темно по краям». ✓

  • Вариант B — колец рождается и гаснет стабильно (пик 48, утечки нет), все цвета валидны. ✓

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

● Agent(Adversarial review of demo.py) Done (5 tool uses · 48.0k tokens · 5m 40s)

Allowed by auto mode classifier

Адверсариальный агент отработал: крашей, утечек и невалидных цветов нет (прогнал 200k/100k/5k кадров, фаззинг dim_hex, переключения — всё чисто). Нашёл 3 реальных замечания, которые стоит поправить:

  1. Вариант B начинается с жёлтого, а не красного — off-by-one: спека явно требует «красный, жёлтый, …», а первый круг выходит жёлтым.

  2. Режим 1: кольца при одинаковой фазе дважды за цикл идеально совпадают → второе кольцо «пропадает» (сливается в одно). Читается как пульсация, а не как две разнонаправленные оси.

  3. Вариант A стартует с magenta в центре (та же несогласованность фазы) + мёртвая строка self.g += SPEED_B в B.

Исправляю все три.

● Update(demo.py) Added 5 lines, removed 2 lines # — Режим 1: вращающиеся кольца ---------------------------------- # def draw_rings(self): self.theta += SPIN c = abs(math.cos(self.theta)) c = abs(math.cos(self.theta)) # фаза кольца 1 s = abs(math.sin(self.theta)) # фаза кольцакольца # были видны и вращались # синхронно, каждое в своей плоскости

    # кольцо 1: вращение вокруг вертикальной оси -> меняется ширина (справа налево)    a = max(1, RING_R * c)    # кольцо 2: вращение вокруг горизонтальной оси -> меняется высота (сверху вниз)    a = RING_R    b = max(1, RING_R * c)    b = max(1, RING_R * s)    self.canvas.coords(self.ring2, CX - a, CY - b, CX + a, CY + b)
# --- Режим 2, вариант A: яркость по радиусу ----------------------- #

● Update(demo.py) Added 3 lines outline=“”, fill=BG_BLACK) self.bands.append((oid, r)) r -= BAND_STEP # сдвигаем фазу так, чтобы в центре цикл стартовал с красного if self.bands: self.g = float(self.bands[-1][1]) # вариант B создаёт кольца динамически в tick()

# ------------------------------------------------------------------ #

● Update(demo.py) Added 2 lines, removed 3 lines

# --- Режим 2, вариант B: затухание в чёрный ----------------------- #
def draw_circles_b(self):    self.g += SPEED_B    self.frame += 1    # рождение нового кольца из центра    # рождение нового кольца из центра (первый кр    if self.frame % SPAWN_EVERY == 0:        idx = (self.frame // SPAWN_EVERY) % NCOL        idx = (self.frame // SPAWN_EVERY - 1) % NCOL        oid = self.canvas.create_oval(CX, CY, CX, CY,                                      outline=YELLOW, width=RING_W_B)        self.rings.append({"id": oid, "r": 1.0,

Обновляю тест-харнесс под новую семантику режима 1 и проверяю, что фиксы сработали (B и A стартуют с красного, кольца больше не совпадают, утечек по-прежнему нет).

● Write(C:\Users\mistd\AppData\Local\Temp\claude\D–habr\141d40eb-0ceb-4b0a-a95d-d449fbf8ab07\scratchpad\smoke2.py) Wrote 71 lines

Ran 1 shell command

Ассерт упал из-за бага в тесте, а не в коде: draw_rings инкрементирует theta в начале, поэтому после d.theta=0 фактически рисуется θ=0.045 (кольцо 2 почти схлопнуто, высота 13.5, а не <5). Код корректен. Правлю тест (выставляю θ с поправкой на инкремент):

● Update(C:\Users\mistd\AppData\Local\Temp\claude\D–habr\141d40eb-0ceb-4b0a-a95d-d449fbf8ab07\scratchpad\smoke2.py) Added 6 lines, removed 2 lines

Ran 1 shell command

Опять баг теста, не кода: я мерил суммарную «светимость» цвета, а она зависит ещё и от оттенка (magenta ярче green по сумме каналов). Спека говорит про коэффициент яркости k = 1 − r/R_MAX, который монотонен по постением: каждый band должен равняться dim_hex(цвет, k).

● Update(C:\Users\mistd\AppData\Local\Temp\claude\D–habr\141d40eb-0ceb-4b0a-a95d-d449fbf8ab07\scratchpad\smoke2.py) Added 21 lines, removed 13 lines

Готово

Файл: D:\habr\demo.py — чистая стандартная библиотека (tkinter, math), никаких зависимостей.

Запуск: python D:\habr\demo.py

Управление: Enter — режим 1 ↔ режим 2 · Space — вариант A ↔ B (в режиме 2) · Esc — выход. Текущее состояние дублируется в заголовке окна.

Что делает:

  • Режим 1 — синий фон, два жёлтых кольца вращаются синхронно: одно вокруг вертикальной оси («справа налево»), второе вокруг горизонтальной («сверху вниз»).

  • Режим 2 — чёрный фон, из центра растут круги по цветовому кругу (красный → жёлтый → зелёный → голубой → синий → фиолетовый → снова красный):

    • A — новые поверх старых, яркость падает от центра к краю (сплошной градиент);

    • B — каждый круг по мере роста гаснет до чёрного, поэтому новый цвет появляется как бы на чёрном фоне (тонкие расходящиеся кольца).

Что нашёл и исправил адверсариальный агент

Крашей, утечек памяти и невалидных цветов не найдено (прогнал 200k/100k/5k кадров, фаззинг dim_hex, сотни переключений). Три реальных замечания — исправлены:

  1. Вариант B стартовал с жёлтого → теперь с красного, как в спеке.

  2. В режиме 1 при одинаковой фазе кольца дважды за оборот сливторого кольца на 90° (эффект «гироскопа»), оба видны всегда.Остаётся синхронным (одна скорость).

  3. Вариант A стартовал с magenta в центре → теперь с красного; заодно убрал мёртвую строку.

Два места, где я выбрал интерпретацию — скажите, если нужно иначе

  • Кольца (режим 1): сделал сдвиг фазы 90° (гироскоп). Если хотите строго одинаковую фазу (оба схлопываются одновременно в «плюс») — в draw_rings замените s = abs(math.sin(…)) на abs(math.cos(…)).

  • Вариант B: каждое кольцо гаснет индивидуально, центр всегда яркий (полотно ушло бы в чёрный, только если перестать плодить кольца). Это соответствует «каждый цвет на чёрном фоне». Если нужен буквально глобальный fade всего кадра — это другая механика, могу переделать.

Хотите — подправлю скорость/радиус/толщину/цвета или запущу у вас для визуальной проверки.

А это комментарий по статье:

Прочитал. Это оказалась статья «Мой первый вайб‑кодинг» (hauserich): автор сравнивает свой код с кодом ChatGPT на трёх задачах, и Задача 3 — ровно наша (кольца + растущие круги, два варианта по Space). Причём и ChatGPT, и автор, и я независимо сошлись на Python + tkinter, create_oval, предсоздании объектов и правке через coords()/itemconfig(), одинаковом списке из 6 цветов и формуле яркости k = 1 − r/R. Так что выбор платформы подтвердился с трёх сторон.

Дальше — честно, что бы я пересмотрел в своём коде и что думаю про листинги из статьи. Свой код не трогаю, только разбор.

Что бы я пересмотрел у себя

  1. Главное: «растущие круги» — последовательно, а не рейнбоу‑полосами

И у ChatGPT, и у автора круги растут по одному цвету за раз: красный вырастает из центра до края → потом жёлтый из центра → потом зелёный, и по кругу. У ChatGPT это массив длительностей TIMES=[3,3.5,4,4.5,5,5.5] (один цвет = один «прогресс» p), у автора — внешний цикл for J in range(1,7) в SFERA().

У меня же в обоих вариантах на экране одновременно много цветов — концентрический радужный набор полос (idx = (g − r)//BAND_W). Это валидное прочтение слов «растут круги стандартных цветов… потом снова красный», но каноничное (то, что реально имел в виду автор задачи, переводя со своего BASIC) — последовательное: один цвет вырос — сменился следующим. Строка спеки «красный, жёлтый… потом снова красный» читается именно как временна́я последовательность.

Это не «баг», но если цель — соответствовать первоисточнику, я бы переключил режим 2 на «один цвет растёт из центра до края, затем следующий». Моя версия визуально богаче, их — ближе к заданию.

  1. Вариант B: у них темнеет всё изображение, у меня — каждое кольцо по отдельности

Спека варианта «Второй»: «в ходе вывода все изображение темнеет вплоть до черного». Обе версии из статьи делают это буквально: растущий диск одного цвета целиком гаснет до чёрного по мере роста —

  • ChatGPT: k = 1 − p, и itemconfig перекрашивает все текущие овалы в одну яркость;

  • автор (ZAL!=0): for K in range(1,I+1): itemconfig(…) — перекрашивает весь диск 1…I в одну убывающую яркость Q=(128−I)*2.

То есть перед сменой цвета экран реально уходит в чёрный, и новый цвет стартует «на чёрном фоне».

У меня в B центр всегда яркий — гаснут только отдельные кольца по мере ухода на периферию. Это тоже читается как «каждый цвет на чёрном», но глобального затемнения нет (мой же адверсариальный агент это отметил как «plausible concern»). После статьи я склонен признать: буквальному тексту точнее соответствует их механика, и я бы привёл B к ней.

  1. Кольца: одинаковая фаза — это и есть канон, а мой «гироскоп» — отклонение

Тут поучительно. Мой адверсариальный агент счёл, что кольца, идеально совпадающие в одно дважды за оборот, — дефект, и я сдвинул фазу на 90°. Но в статье обе версии используют одну фазу для обоих эллипсов:

  • ChatGPT: один и тот же c=abs(cos(t)) для r1 и r2;

  • автор: одно и то же B=R/I для OVALG и OVALV.

Значит совпадение в круг (при c=1) и «плюс» из двух линий (при c=0) — это задуманный пульсирующий вид, а не баг. Мой сдвиг фазы «чинит» то, что чинить не просили. Хорошо, что я оставил ревёрт в одну строку (sin → cos в draw_rings) — им и восстанавливается каноничный вид.

Мелочь в плюс мне: моя (и ChatGPT‑овская) математика R·|cos| — это физически точная ортопроекция равномерно вращающегося кольца (естественно «залипает» анфас, быстро проскакивает ребром). У автора B=R/I с нелинейным I и переменной задержкой WREM — стилизованный ручной easing, не косинус.

  1. Разная скорость по цветам (по желанию)

У них каждый цвет растёт со своей скоростью (TIMES у ChatGPT; TIM=0.01·KOF^J, KOF=1.333 у автора — каждый следующий цвет быстрее). У меня скорость единая. В моей спеке этого не было, так что это не упущение, но приятная деталь — могу добавить.

Где мой код держится хорошо (или лучше)

  • Событийный цикл. У автора анимация крутится в while‑циклах с busy‑wait WREM() (спинит ядро на 100%, блокирует event loop, требует ручного POLE.update()), а ROTOR/SFERA вызываются прямо из обработчика клавиш — это классический tkinter‑footgun: update() внутри цикла может снова диспатчить KLAW, отсюда риск ре‑энтрантности/RecursionError при частом нажатии. И я, и ChatGPT используем идиоматичный root.after(…) — обе «ИИ‑версии» тут аккуратнее человеческой.

  • Нет утечки объектов. У ChatGPT в circles() при смене цвета делается self.items=[] без удаления овалов с холста — старые объекты копятся на канве бесконечно (≈128–168 овалов за цвет, чистятся только при delete(“all”) на смене режима). Забавно: это ровно «видит локальный момент, но не общую картину» из тезиса статьи. У меня A — фиксированные 86 овалов с перекраской, B — кольца удаляются по гашению (проверено: пик 48, роста нет). У автора тоже без утечки — предвыделенные 384 овала.

  • Старт с красного. Обе версии из статьи стартуют красным (self.color=0 / J=1). У меня в первой редакции был off‑by‑one (B начинался с жёлтого) — тот самый, что я уже поправил.

И честно — одно место, где ChatGPT лучше меня: он считает время по стенным часам (time.perf_counter()), поэтому скорость анимации не зависит от частоты кадров; у меня приращения на кадр, и при просадке ниже 50 fps демка замедлится. Для такой задачи неважно, но подход ChatGPT робастнее.

Комментарий к самим листингам

Версия ChatGPT. Компактно и грамотно: after()‑цикл, perf_counter, аккуратный rgb() с клампом max(0,min(255,…)), для ресайза. Слабости: (1) описанная утечка овалов; (2) одинаковая фаза колец — «второе кольцо исчезает» в круг дважды за оборот; (3) круги заполняют лишь центральный радиус 0.35·min = 168 px, а не всё окно. Крепкий «локальный» код с одной неочевидной «глобальной» дырой — прямо иллюстрация к выводу статьи.

Версия автора. Это фактически эталон задачи, и спеку она реализует полностью и вдумчиво: предсоздание овалов против мерцания; тройной овал (OVAL1/2/3 со сдвигом на 1 px) — хак, чтобы соседние 1‑пиксельные контуры не оставляли щелей; ZAL чисто разводит оба варианта (radial gradient ↔ равномерное затемнение всего диска); радиальный градиент Q=(128−I)·2; переменный easing вращения. Минусы — «бейсиковые» привычки: busy‑wait WREM, управление анимацией из while внутри обработчиков (ре‑энтрантность), from tkinter import *, глобальные переменные повсюду, транслит‑имена (OKNO/POLE/PRIZ/ROTOR/SFERA/KLAW), O(n²) перекраска в ветке ZAL!=0. Работает и точно по заданию, но по структуре — самый «олдскульный» из трёх.

Итог

Ничего в моём коде не «сломано», но статья вскрыла три места, где я разошёлся с первоисточником: (1) я показываю все цвета сразу, а канон — по одному растущему цвету; (2) мой B гасит кольца поштучно, а канон — затемняет весь кадр до чёрного; (3) мой сдвиг фазы колец — красивое, но отклонение от задуманной пульсации. По инженерии (event loop, утечки) моя версия и версия ChatGPT аккуратнее человеческой, но у ChatGPT — утечка овалов, а у меня был off‑by‑one с первым цветом (уже поправлен).

Если захочешь «привести к первоисточнику» — скажи, и я (а) переключу режим 2 на последовательный рост одного цвета, (б) сделаю вариант B глобальным затемнением всего диска, (в) верну кольцам одну фазу. Каждое — маленькая правка, но менять пока ничего не буду, как договорились.

Я несколько в шоке. :) Если честно, такой объем текста читать в виде комментария не совсем удобно. Но вообще - это какая нейросеть? Потому что такое впечатление, что куски ее диалога - это куски моей беседы с ЖПТ, хотя по идее где она могла их взять?
Насчет разбора - я понял, что в целом написал все верно, а насчет "транслита имен" - а что тут плохого? Зато наглядно. :)
В плане организации - да, я переводил "в лоб" с бейсика, понимаю, что формально так не принято на питоне.

Но вообще я думал сделать не анализ моей статьи на качество вариантов, а отдельно предложить другой нейросети сделать свой вариант программы, НЕ ГЛЯДЯ на уже созданное...

Я несколько в шоке. :) Если честно, такой объем текста читать в виде комментария не совсем удобно.

Понимаю. Могу куда-нибудь закинуть.

Но вообще - это какая нейросеть?

Это Claude Opus 4.8 на xhigh effort с ultracode (которым она не воспользовалась).

Потому что такое впечатление, что куски ее диалога - это куски моей беседы с ЖПТ, хотя по идее где она могла их взять?

Я копипастил ТЗ как его увидел из статьи. Потом еще две правки внес, когда прочитал развитие истории (с HTML на Python поменял, потом отказался от pygame).

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

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

Я там ответил уже на другой Ваш комментарий. У меня совсем другая "ниша" и другие условия деятельности. Не сомневаюсь, что в бизнесе, тем более большом, можно использовать платные нейросети для решения каких-то задач, особенно если самому нет желания и удовольствия заниматься кодингом. Но у меня все иначе... :)

Дико извиняюсь, но я запустил код, который идет первым. Он работает абсолютно неправильно!

Не за что извиняться :)

Я думаю, что по большей части проблема в формулировке ТЗ. Оно действительно открыто к интерпретации, посмотрите анализ (третий спойлер). Я лично не увидел особых расхождений со скопированным ТЗ, я думаю тут вопрос кто как понял.

Вот это кстати промпт, по которому adverserial агент пытался оспорить написанное:

Обратите внимание на "The author's INTERPRETATION (which you should also scrutinise, not just accept):"

You are an ADVERSARIAL code reviewer. Your job is to BREAK and DISPROVE, not to praise. Assume the code is subtly wrong until proven otherwise. Be skeptical and concrete.

The file to review is D:\habr\demo.py — a graphics demo written in pure Python standard library (tkinter Canvas). You may READ it and you may RUN Python to test it (Windows, python 3.12, tkinter 8.6 available). To test without popping a window, create the Tk root and call root.withdraw(), then drive the drawing methods directly and call root.update_idletasks() — do NOT call root.mainloop() (it blocks). There is an existing headless harness at C:\Users\mistd\AppData\Local\Temp\claude\D--habr\141d40eb-0ceb-4b0a-a95d-d449fbf8ab07\scratchpad\smoke.py you can read/adapt.

THE ORIGINAL SPEC (Russian, must be satisfied faithfully): “Окно 640*480, синий фон. Вращаются два желтых кольца справа налево и сверху вниз, синхронно. Это один режим. Переключение на второй - клавишей Enter. Второй режим. Черный фон окна и из центра растут круги стандартных цветов: красный, желтый, зеленый, голубой, синий и фиолетовый. Потом снова красный и т.д. Этот режим имеет два варианта (переключение клавишей Space): Один - новые круги рисуются поверх старых, яркость кругов уменьшается от центра к периферии. Второй - в ходе вывода кругов все изображение темнеет вплоть до черного, поэтому каждый цвет рисуется как бы на черном фоне.”

The author’s INTERPRETATION (which you should also scrutinise, not just accept):

  • Two yellow rings = two concentric ellipses sharing one angle theta; ring1 varies horizontal semi-axis (spins about vertical axis => “right-to-left”), ring2 varies vertical semi-axis (spins about horizontal axis => “top-to-bottom”). “Synchronous” = same theta/phase.

  • Mode 2 colors cycle: red, yellow, green, cyan, blue, magenta, repeat.

  • Variant A: nested filled ovals recolored per-frame; brightness = 1 - r/R_MAX (bright center, dark edge); inner drawn on top of outer.

  • Variant B: expanding ring outlines spawned from center, each fading toward black over time via subtractive dimming; whole picture would go black if spawning stopped.

ADVERSARIAL CHECKLIST — investigate each and try to find a real defect:

  1. CRASHES / EXCEPTIONS: any input (long runtime, integer overflow of self.g/self.frame after millions of frames, division, empty lists, tkinter color-string format like negative or >255 channel values, coords with x1<x0) that throws. Actually reproduce by running many iterations.

  2. RESOURCE LEAK: in variant B, do canvas items accumulate without being deleted over a long run? Prove with a count over thousands of frames. Also: switching modes repeatedly — are old items deleted (canvas.delete(‘all’))? Any orphaned after timers stacking up if you toggle modes fast?

  3. SPEC CONFORMANCE:

    • Mode 1 background actually blue? rings actually yellow and actually reading as “rotating”? Do BOTH directions (right-to-left AND top-to-bottom) actually appear, or do both rings collapse identically (same phase) making it a pulsing plus-sign rather than two distinct rotations? Is that a faithful reading of “справа налево и сверху вниз”? Argue it.

    • Mode 2 background black? Colors exactly the 6 named standard colors and in the right cycle order?

    • Variant A: is brightness truly decreasing from center to periphery at all times? Any radius where it inverts or bands read wrong because of the (g - r)//BAND_W sign when g is small (g starts at 0, so g - r is negative early — does int(negative)//positive behave as intended, and does modulo give the right color)? Check the very first frames.

    • Variant B: does it truly “darken to black”? With SPAWN_EVERY spawning and FADE = SPEED_B/R_MAX, do rings actually reach black before deletion, or get deleted by radius first while still bright (leaving a visible bright ring popping out of existence at the edge)? Compute the numbers.

    • Are A and B VISUALLY DISTINCT enough to be recognisably “two variants”?

  4. KEY HANDLING: Enter (Return AND KP_Enter) toggles mode; Space toggles variant only in mode 2; Esc quits. Any dead keys, or Space doing something wrong in mode 1?

  5. Off-by-one / color math in dim_hex: does it ever produce an invalid Tk color (e.g. ‘#100’ or channel 256)? Feed extreme k.

  6. Anything that would look visibly wrong or janky that a user would immediately complain about.

Deliverables: Return a concise, ranked list of CONFIRMED defects (state how you reproduced each — command/observation), plus PLAUSIBLE concerns you couldn’t fully confirm. If something is fine, say so briefly. Do NOT fix anything — only report. Be specific with file line numbers where possible. Prefer running code to prove claims over speculation.

А еще возможно проблема банально в русском языке и трудностях перевода - как видите агенты между собой общаются на английском, пытаются переводить ваше ТЗ блоками.

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

Нет, я специально использовал только ваш промпт. Потому что - без наездов и без претензий - если вы неправильно по своему коду сформулировали ТЗ, то по нему будет неправильный (отличающийся) результат.

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

Что касается ТЗ, то я выше (изначально) уже писал:

И лучший результат достигается, если сначала через plan mode описать, что у тебя есть, и что хочешь получить. Потом ручками подредактировать план, где не согласен, прогнать "ask me questions" и уже после всего этого, по вылизанному плану поставить прогон "вайбкодинга". Тогда да, что-то может и получится.

Правильно поставленная и вылизанная задача - бОльшая часть работы. Поэтому я третий день пишу план вместо кода для своего проекта :)

Понятно, но получается, что ЖПТ лучше? Ведь он в конечном итоге сделал правильный вариант, задавая мне правильные вопросы?

Это очень точно. Я примерно к тому же пришёл когда пробовал вайбкодинг на своём pet-проекте. Первые два дня шли быстро потому что задача была маленькая и хорошо у меня в голове структурирована. Как только начались пограничные случаи модель стала предлагать решения которые противоречили тому что уже написано. И я понял что проблема была не в модели, а в том что я сам не мог чётко объяснить как части системы должны взаимодействовать. Особенно учитывая что каждый раз приходилось править по структуре из головы.

Plan mode это интересно. Не пробовал, буду смотреть.

Так в том и дело, что на сложной задаче написание промпта может по сложности превысить самостоятельное написание программы. :) Игра не стоит свеч просто...

Не может: вы пишете строчку, оно вам ее переписывает в 10+ строчек плана. Вам только проверять остается.

Ну, так до бесконечности можно... Не знаю, может я неправильно действовал по неопытности... :)

Вообще да, можно :)

Я последний план писал три дня и убил в него весь недельный лимит Fable. Зато теперь по нему можно пускать даже самые тупые модели - любая по этому плану код напишет хорошо (это было частью ТЗ).

Ну, тут каждый сам выбирает. :) Мне писать программы нравится, т.ч. сам пишу... :)

Я просто не знаю языков и синтаксисов (AHK не считается). Представляю только логику :)

А, ну понятно. Я-то программистом работаю...

Звучит логично. :)

Именно поэтому я веду свой проект — потому что не разбираться в одностраничных комиксах крайне сложно (хотя некоторым удаётся).

Да, серьезная работа. Просто я с картинками вообще не работаю, только один раз решил "заказать" картинку для блога - и что? Долго объяснял, в итоге получил все равно не то. Но махнул рукой, ибо картинка чисто оформительская. Но и тут опыт печальный...

Я не себя проверял, а нейросеть. :) Меня чего проверять - со мной все ясно. :) А вот ее проверить было как раз интересно...

Sign up to leave a comment.

Articles