НСПК / 24.12.24 / перетяжка / 2W5zFK76vmn
НСПК / 24.12.24 / перетяжка / 2W5zFK76vmn
НСПК / 24.12.24 / перетяжка / 2W5zFK76vmn

Claude Artifacts: как добиться рабочей игры

Артефакты в Claude позволяют пользователям создавать интерактивные и редактируемые выходные данные, в том числе и код. Рассказываем, как работает эта фича и как создать приложение с помощью нейросети.

329 открытий4К показов
Claude Artifacts: как добиться рабочей игры

Что такое артефакт

Claude Artifacts — это публикуемый код чего угодно, что вы сгенерировали с помощью нейронки компании Anthropic. HTML-страницы даже рендерятся на месте. Для любителей форкнуть что-то годное есть кнопка Remix: с ее помощью код проекта клонируется, и мы получаем возможность видоизменять все под себя.

Артефактом, будь то веб-сайт, игра или приложение, делятся с помощью ссылки. И на мой взгляд, это новый уровень общения в комьюнити энтузиастов чего угодно. Если вам хочется быстрее обработать результаты опроса — сверстайте презентацию-отчет, Claude сама проведет EDA (разведочный анализ данных) и поищет интересное. Если вашему приложению не хватает кросс-платформенности — попросите адаптировать существующий код Telegram-бота в сайт.

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

Если вы хотите научиться писать промпты для LLM, как профи, обратитесь к этой статье. Разработчики поделились 20 принципами хорошего промпта.

Лимиты на обращения в Claude 3.5 Sonnet теперь носят временный характер:

1 message remaining till 2 PM.

Создаем рабочий артефакт

Прежде чем попасть к 3.5 Sonnet, мне потребовалось пройти регистрацию с помощью Google oAuth, причем российский +79… не принимался. С этим шагом помогают сторонние сервисы временных номеров для приема кодов вроде Grizzly SMS. Anthropic почему-то не сработал с +1 (США), но сдружился с +44 из Великобритании. Получить код обошлось мне в ~20 рублей.

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

На мой взгляд, массивное ТЗ, вгруженное в любую GPT разом, почти наверняка сделает артефакт нерабочим. Я пользуюсь принципом декомпозиции и подаю системе фичи небольшими кусками. Да, это потребует бóльшего количества сообщений и токенов, однако значительно повысит шансы на собираемость проекта.

Первоначальный промпт выглядит несложно:

			Напиши приложение на Python для игры в "Угадай мелодию". У нас будут следующие рубрики:
- Верните мой 2007-й (песни нулевых)
- Дискотека 90-х
- Современное
- Классика рока
В каждой секции по 8 песен, за четыре из них дают по 200 очков, за другие четыре -- по 300. Список треков придумай сама, я потом отредактирую. Приложение будет исполняться локально, на моем ноутбуке, потому пропиши пути до .mp3, как будто игра запущена на macOS.
		

Claude отдал мне CLI-версию, которая собралась сразу:

			import random
import os
from playsound import playsound

class Song:
    def __init__(self, title, file_path, points):
        self.title = title
        self.file_path = file_path
        self.points = points

class Category:
    def __init__(self, name, songs):
        self.name = name
        self.songs = songs

class Game:
    def __init__(self):
        self.categories = [
            Category("Верните мой 2007-й", [
                Song("Звери - Районы-кварталы", "/Users/user/Music/2007/zveri.mp3", 200),
                …
                Song("Токио - Если ты меня", "/Users/user/Music/2007/tokio.mp3", 300)
            ]),
            Category("Дискотека 90-х", [
                Song("Руки Вверх - Крошка моя", "/Users/user/Music/90s/rukivverh.mp3", 200),
                …
            Category("Современное", [
                Song("Моргенштерн - Cadillac", "/Users/user/Music/modern/morgenshtern.mp3", 200),
                …
            Category("Классика рока", [
                Song("Кино - Группа крови", "/Users/user/Music/rock/kino.mp3", 200),
                Song("ДДТ - Что такое осень", "/Users/user/Music/rock/ddt.mp3", 200),
                …
                Song("Би-2 - Полковнику никто не пишет", "/Users/user/Music/rock/bi2.mp3", 300)
            ])
        ]
        self.score = 0

    def play(self):
        print("Добро пожаловать в игру 'Угадай мелодию'!")
        for category in self.categories:
            print(f"\nКатегория: {category.name}")
            random.shuffle(category.songs)
            for song in category.songs[:4]:  # Выбираем только 4 песни из каждой категории
                print(f"\nЗа эту песню вы можете получить {song.points} очков.")
                try:
                    playsound(song.file_path)
                except:
                    print("Ошибка воспроизведения. Пропускаем трек.")
                    continue
                guess = input("Введите название песни: ").strip().lower()
                if guess == song.title.lower():
                    print(f"Правильно! Вы заработали {song.points} очков.")
                    self.score += song.points
                else:
                    print(f"Неправильно. Правильный ответ: {song.title}")
                print(f"Ваш текущий счет: {self.score}")
        
        print(f"\nИгра окончена! Ваш итоговый счет: {self.score}")

if __name__ == "__main__":
    game = Game()
    game.play()
		

Первый MVP (минимально жизнеспособный продукт) получен. В дальнейшем я не буду перекопировать все сниппеты, чтобы объем статьи оставался оптимальным.

Проблема появилась со стороны: macOS отказался устанавливать библиотеку playsound любым способом. Потому я решила использовать аналог — pydub:

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

Нейронка отдала следующую рабочую версию, попутно описав улучшения:

			В этой обновленной версии игры я внес следующие изменения:

- Заменил библиотеку playsound на pydub.
- Добавил метод play_audio, который воспроизводит только первые 10 секунд песни с помощью pydub.
- Использовал многопоточность для одновременного воспроизведения музыки и ожидания ввода пользователя.
- Добавил ограничение по времени в 30 секунд на каждый ответ.
- Реализовал метод stop_audio для прекращения воспроизведения после ввода ответа или истечения времени.
		

После установки pydub и соответствующей вспомогалочки ffmpeg я получила игру в командной строке, причем музыка воспроизводилась прямо в командной строке:

Такая ностальгия по CLI будет понятна только мне, скорее всего, а модератором будет другой человек — не программист. Так что добавляем веб-интерфейс:

			Я бы хотела играть с коллегами не с помощью командной строки, а на веб-странице. Ты можешь добавить этому приложению HTML-оболочку?
		

Нейронка избрала для этой задачи Flask — популярный минималистичный фреймворк. Для сборки артефакта на его базе нужно всего два файла: index.html и app.py. Их Claude аккуратно упаковывает в средство предпросмотра (жаль, Python не компилируется прямо на сайте, как JavaScript). Приятная мелочь — в дальнейшем файлы будут обрастать версиями, между которыми удобно переключаться:

Claude Artifacts: как добиться рабочей игры 1

Далее я уперлась в лимит бесплатных сообщений. Пришлось подождать чуть больше часа. Естественно, после 14:00 сессия «помнит» все: от переписки до файлов, которые отдала нейронка.

Следующий шаг — возврат категорий. Чат-бот сократил их до троеточий, что допустимо в условиях ограниченного контекста, но все же восстановим листинг песен:

			Обнови код так, чтобы игрок мог выбрать категорию ("Верните мой 2007-й", "Классика русского рока" и еще две).
		

Здесь наступил лимит контекстного окна. Если верить документации, для Free-тарифа его размер будет варьироваться, а для платного составит 200+К токенов (~500 страниц текста).

Ремикс артефакта почему-то схватил только app.py (без index.html) и благополучно понизил значимость моих реплик. Так что создаем новый чат и отправляем ей два файла с запросом на команды:

Claude Artifacts: как добиться рабочей игры 2

У меня не работает кнопка:

			Кнопка "Начать игру" почему-то не работает ни в Chrome, ни в Safari. Исправь, пожалуйста.
		

Здесь внезапно случился запуск рендеринга HTML, но Python-код все равно просто лежит, так что тыкать прототип в браузере бесполезно.

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

Попытка ремиксовать опубликованный артефакт Claude не сильно помогла: LLM в рамках новой сессии не придает происходящему в приложенных файлах такое значение, как репликам пользователя, и высока вероятность модификации кода, который не собирается.

Полет мысли с помощью GPT в 2024 году имеет ограничения по объему. Пытки нейронки на предмет отладки своих же багов затягивают в бесперспективную трясину: с таким объемом она теряет важные детали или плодит баги.

Потому компромисс заключается в сокращении хотелок. Я выкидываю фичи, чтобы сохранить работоспособность. Начав новую сессию, я прошу «Угадайку» с чистого листа, оставив добрую половину функционала в оффлайне:

			Напиши мне игру "Угадай мелодию" на Python + Flask + pydub с веб-интерфейсом.
В игре может быть до 10 игроков. Для каждого из них должно быть по div с порядковым номером, редактируемым на месте именем в поле ввода и текущим числом очков.
Модератор будет воспроизводить музыку из приложения извне, но очки будут начисляться нажатием кнопки "+100 очков" у каждого игрока.
		

ИИ-ассистент отдал мне рабочую минимальную версию игры. Артефакт можно посмотреть по ссылке (вам потребуется VPN). Теперь код выглядит так:

			# app.py
from flask import Flask, render_template, request, jsonify
from pydub import AudioSegment
from pydub.playback import play
import threading
import os

app = Flask(__name__)

players = [{"id": i, "name": f"Player {i+1}", "score": 0} for i in range(10)]

@app.route('/')
def index():
    return render_template('index.html', players=players)

@app.route('/update_player', methods=['POST'])
def update_player():
    data = request.json
    player_id = data['id']
    new_name = data['name']
    players[player_id]['name'] = new_name
    return jsonify(success=True)

@app.route('/add_score', methods=['POST'])
def add_score():
    data = request.json
    player_id = data['id']
    players[player_id]['score'] += 100
    return jsonify(success=True, new_score=players[player_id]['score'])

if __name__ == '__main__':
    app.run(debug=True)
		

А веб-страница так:

			# index.html


    
    
    Guess the Melody
    
    
    
       # CSS-стили (довольно объемные даже для лонгрида)
    


    Guess the Melody
    
        {% for player in players %}
        
            Player {{ player.id + 1 }}:
            
            Score: {{ player.score }}
            +100 points
        
        {% endfor %}
    

    
        function updatePlayerName(id, newName) {
            axios.post('/update_player', {
                id: id,
                name: newName
            })
            .then(function (response) {
                console.log('Player name updated');
            })
            .catch(function (error) {
                console.error('Error updating player name:', error);
            });
        }

        function addScore(id) {
            axios.post('/add_score', {
                id: id
            })
            .then(function (response) {
                document.getElementById(`score-${id}`).textContent = response.data.new_score;
            })
            .catch(function (error) {
                console.error('Error adding score:', error);
            });
        }
    


		
Запрос на игру такой сложности лучше разбивать на последовательные небольшие просьбы. Каждую новую реплику-задачу можно снабжать 1-2 новыми некрупными фичами. Но в бесконечность сложность такого проекта не устремится: рано или поздно наступит момент, когда контекст будет исчерпан и на запрос дополнительных фичей модель отдаст нечто нерабочее.

Есть ли среди читателей обладатели платной подписки? Если да, поделитесь опытом: насколько сложные программы может создавать Claude Artifacts при контекстном окне в 200К+ токенов?

Заключение

Даже если Claude Artifacts и не будет содержать сложный код, его экстенсивно растущие способности очень удивляют. Выходит, что у любого среднестатистического разработчика сегодня есть в подчинении свой ИИ-джун, на которого можно «свесить» простые вещи вроде верстки веб-страницы в стиле Material Design, развертывания Flask-приложения или элементарной логики начисления очков в игре. А это уже огромная экономия времени.

Интересный факт напоследок: Anthropic назвали свою модель в честь творца теории информации Клода Шеннона (на обложке статьи). Аж в 1948 году он ввел понятие битов как единиц измерения информации и разработал формулы вычисления количества данных.

Следите за новыми постами
Следите за новыми постами по любимым темам
329 открытий4К показов