Как заставить ИИ на базе LLM писать полноценные приложения на HTML + CSS + JavaScript

Как заставить ИИ на базе LLM писать полноценные приложения на HTML + CSS + JavaScript

Зачем вообще это делать?

Во-первых, это настоящий вызов современным LLM моделям, и очень интересно как разные модели будут справляться с такой задачей.

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

Ну раз надо так надо, делаем )

Казалось бы что может быть проще? Берем любой чат с ИИ и говорим "напиши игру Змейка"? И нет сомнений что вы получите код страницы с рабочей версией игры.

Как заставить ИИ на базе LLM писать полноценные приложения на HTML + CSS + JavaScript

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

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

А вот как!

Давайте любой код HTML разобьем на блоки/кирпичики/слоты и заставим ИИ писать код используя такие блоки, а самое главное потом заменять/удалять/добавлять такие блоки САМОСТОЯТЕЛЬНО.

Вот как будет выглядеть тогда пустая страница, разбитая на такие виртуальные блоки:

<html> <head> <!-- HEAD_BLOCK_1 --><meta charset="UTF-8"><title>My Page</title><!-- /HEAD_BLOCK_1 --> <!-- HEAD_BLOCK_2 --><style>body {color:#333;background:#ccc;}</style><!-- /HEAD_BLOCK_2 --> <!-- HEAD_BLOCK_3 --><script>alert('Hello World!')</script><!-- /HEAD_BLOCK_3 --> </head> <body> <!-- BODY_BLOCK_1 --><header><h1>Welcome</h1></header><!-- /BODY_BLOCK_1 --> <!-- BODY_BLOCK_2 --><main><p>Main content here</p></main><!-- /BODY_BLOCK_2 --> <!-- BODY_BLOCK_3 --><footer>Footer content</footer><!-- /BODY_BLOCK_3 --> </body> </html>

Главный фокус

И сразу разоблачение: мы НИКОГДА не будем показывать ИИ сам код HTML!

Мы подготовим словарь, содержащий блоки и показывать будем только его.

Вот код функции, которая создает словарь в Python по нашей структуре HTML документа:

import re def html_to_dict(html): structure = { 'html': { 'head': {'blocks': []}, 'body': {'blocks': []} } } head_pattern = r'<!-- HEAD_BLOCK_\d+ -->(.*?)<!-- /HEAD_BLOCK_\d+ -->' body_pattern = r'<!-- BODY_BLOCK_\d+ -->(.*?)<!-- /BODY_BLOCK_\d+ -->' head_blocks = re.findall(head_pattern, html, re.DOTALL) body_blocks = re.findall(body_pattern, html, re.DOTALL) structure['html']['head']['blocks'] = head_blocks structure['html']['body']['blocks'] = body_blocks return structure

В результате наш код пустой странице после выполнения этого кода будет выглядеть так:

{ "html": { "body": { "blocks": [ "<header><h1>Welcome</h1></header>", "<main><p>Main content here</p></main>", "<footer>Footer content</footer>" ] }, "head": { "blocks": [ "<meta charset=\"UTF-8\"><title>My Page</title>", "<style>body {color:#333;background:#ccc;}</style>", "<script>alert('Hello World!')</script>" ] } } }

И вот именно в этом виде ИИ будет работать с кодом.

Теперь немного промптинга, куда уж без него )

Роль для ИИ может выглядеть так:

Твоя задача - создавать и редактировать HTML/CSS/JavaScript код. При этом код страницы соответствует такому шаблону: ``` <html> <head> <!-- HEAD_BLOCK_1 --><meta charset="UTF-8"><title>My Page</title><!-- /HEAD_BLOCK_1 --> <!-- HEAD_BLOCK_2 --><style>body {color:#333;background:#ccc;}</style><!-- /HEAD_BLOCK_2 --> <!-- HEAD_BLOCK_3 --><script>alert('Hello World!')</script><!-- /HEAD_BLOCK_3 --> </head> <body> <!-- BODY_BLOCK_1 --><header><h1>Welcome</h1></header><!-- /BODY_BLOCK_1 --> <!-- BODY_BLOCK_2 --><main><p>Main content here</p></main><!-- /BODY_BLOCK_2 --> <!-- BODY_BLOCK_3 --><footer>Footer content</footer><!-- /BODY_BLOCK_3 --> </body> </html> ``` Твой ответ должен быть всегда только в виде строк с описанием изменений: ``` head:::1:::@@@<meta charset="UTF-8"><title>My Website</title>@@@&&& head:::2:::@@@<style>body {color:#000;}</style>@@@&&& head:::3:::@@@<meta name="viewport" content="width=device-width, initial-scale=1.0">@@@&&& head:::4:::@@@<script>alert('Hello');</script>@@@&&& body:::1:::@@@<header><h1>Welcome to our site</h1></header>@@@&&& body:::2:::@@@<nav><ul><li>Home</li><li>About</li><li>Contact</li></ul></nav>@@@&&& body:::3:::@@@<main><article><h2>Latest News</h2><p>Content here</p></article></main>@@@&&& body:::4:::@@@<aside><div class="widget">Sidebar content</div></aside>@@@&&& body:::5:::@@@<footer><p>© 2024 My Company</p></footer>@@@&&& ``` Важно чтобы стили и скрипты были описаны внутри HTML, исключения могут быть только ссылки на библиотеки стилей (например bootstrap) или JavaScript (jquery) должны быть только в виде внешних ссылок. Если ты добавляешь код, которого еще не было, то добавляй его в новый номер блока. Если ты хочешь изменить код в блоке, который уже есть, то обязательно ответь полным кодом этого блока.

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

Теперь посмотрим как выглядит запрос к ИИ:

# html_dict - словарь, полученный из HTML на предыдущем шаге # task - Задача, например: "Создай игру Змейка" api_key = 'sk-XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' client = openai.OpenAI(api_key=api_key) response = client.chat.completions.create( model="gpt-4o-mini", #"o1-mini", messages=[ { "role": "user", "content": role # - это наш промпт, приведенный выше }, { "role": "user", "content": f"Текущий код страницы: ```{html_dict}``` и вот новая задача: ```{task}```" } ] ) ai_reply = response.choices[0].message.content

И вот какой ответ мы получим от ИИ при пустой странице и запросе "создай игру Змейка" (на модели gpt-4o-mini):

head:::1:::@@@<meta charset="UTF-8"><title>Snake Game</title>@@@&&& head:::2:::@@@<style>body {color:#000; background:#f0f0f0; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } canvas { border: 1px solid #000; }</style>@@@&&& head:::3:::@@@<script>let canvas, ctx, snake, food, score, direction; function init() { canvas = document.createElement('canvas'); ctx = canvas.getContext('2d'); document.body.appendChild(canvas); canvas.width = 400; canvas.height = 400; snake = [{ x: 10, y: 10 }]; food = { x: Math.floor(Math.random() * 20), y: Math.floor(Math.random() * 20) }; score = 0; direction = { x: 0, y: 0 }; window.addEventListener('keydown', changeDirection); gameLoop(); } function gameLoop() { update(); draw(); setTimeout(gameLoop, 100); } function update() { const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; snake.unshift(head); if (head.x === food.x && head.y === food.y) { score++; food = { x: Math.floor(Math.random() * 20), y: Math.floor(Math.random() * 20) }; } else { snake.pop(); } } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let segment of snake) { ctx.fillStyle = 'green'; ctx.fillRect(segment.x * 20, segment.y * 20, 20, 20); } ctx.fillStyle = 'red'; ctx.fillRect(food.x * 20, food.y * 20, 20, 20); ctx.fillStyle = 'black'; ctx.fillText('Score: ' + score, 10, 20); } function changeDirection(event) { if (event.key === 'ArrowUp') { direction = { x: 0, y: -1 }; } else if (event.key === 'ArrowDown') { direction = { x: 0, y: 1 }; } else if (event.key === 'ArrowLeft') { direction = { x: -1, y: 0 }; } else if (event.key === 'ArrowRight') { direction = { x: 1, y: 0 }; } } window.onload = init;</script>@@@&&& body:::1:::@@@<header><h1>Snake Game</h1></header>@@@&&& body:::2:::@@@<main><p>Use arrow keys to move the snake and eat the food!</p></main>@@@&&& body:::3:::@@@<footer><p>© 2024 Snake Game</p></footer>@@@&&&

Назад в HTML!

Теперь наша задача из этой абракадабры обратно собрать HTML код )

Сначала напишем функцию, которая в нашем словаре внесет правки предлложенные ИИ:

import copy def merge_changes(current_structure, changes): new_structure = { 'html': { 'head': {'blocks': copy.deepcopy(current_structure['html']['head']['blocks'])}, 'body': {'blocks': copy.deepcopy(current_structure['html']['body']['blocks'])} } } for change in changes: if change and ':::' in change: section, number, content = change.split(':::', 2) section = section.replace('\n', '').replace(' ', '') content = content.split('@@@')[1].replace('\n', '').replace('\r', '') block_index = int(number) - 1 if section == 'head': while len(new_structure['html']['head']['blocks']) <= block_index: new_structure['html']['head']['blocks'].append('') new_structure['html']['head']['blocks'][block_index] = content elif section == 'body': while len(new_structure['html']['body']['blocks']) <= block_index: new_structure['html']['body']['blocks'].append('') new_structure['html']['body']['blocks'][block_index] = content return new_structure # changes - это ответ от ИИ с предложенным списком изменений new_structure = merge_changes(current_structure, changes.split('&&&\n'))

Итогом работы этой функции будет обновленный словарь вида:

{ "html": { "body": { "blocks": [ "<header><h1>Snake Game</h1></header>", "<main><p>Use arrow keys to move the snake and eat the food!</p></main>", "<footer><p>\u00a9 2024 Snake Game</p></footer>" ] }, "head": { "blocks": [ "<meta charset=\"UTF-8\"><title>Snake Game</title>", "<style>body {color:#000; background:#f0f0f0; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } canvas { border: 1px solid #000; }</style>", "<script>let canvas, ctx, snake, food, score, direction; function init() { canvas = document.createElement('canvas'); ctx = canvas.getContext('2d'); document.body.appendChild(canvas); canvas.width = 400; canvas.height = 400; snake = [{ x: 10, y: 10 }]; food = { x: Math.floor(Math.random() * 20), y: Math.floor(Math.random() * 20) }; score = 0; direction = { x: 0, y: 0 }; window.addEventListener('keydown', changeDirection); gameLoop(); } function gameLoop() { update(); draw(); setTimeout(gameLoop, 100); } function update() { const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; snake.unshift(head); if (head.x === food.x && head.y === food.y) { score++; food = { x: Math.floor(Math.random() * 20), y: Math.floor(Math.random() * 20) }; } else { snake.pop(); } } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let segment of snake) { ctx.fillStyle = 'green'; ctx.fillRect(segment.x * 20, segment.y * 20, 20, 20); } ctx.fillStyle = 'red'; ctx.fillRect(food.x * 20, food.y * 20, 20, 20); ctx.fillStyle = 'black'; ctx.fillText('Score: ' + score, 10, 20); } function changeDirection(event) { if (event.key === 'ArrowUp') { direction = { x: 0, y: -1 }; } else if (event.key === 'ArrowDown') { direction = { x: 0, y: 1 }; } else if (event.key === 'ArrowLeft') { direction = { x: -1, y: 0 }; } else if (event.key === 'ArrowRight') { direction = { x: 1, y: 0 }; } } window.onload = init;</script>" ] } } }

Ну и наконец-то мы теперь можем собрать обратно HTML код вот такой функцией:

def dict_to_html(structure): html = ['<html>', '<head>'] for i, block in enumerate(structure['html']['head']['blocks'], 1): if block: # Проверяем, что блок не пустой html.append(f' <!-- HEAD_BLOCK_{i} -->{block}<!-- /HEAD_BLOCK_{i} -->') html.append('</head>') html.append('<body>') for i, block in enumerate(structure['html']['body']['blocks'], 1): if block: # Проверяем, что блок не пустой html.append(f' <!-- BODY_BLOCK_{i} -->{block}<!-- /BODY_BLOCK_{i} -->') html.append('</body>') html.append('</html>') return '\n'.join(html)

На выходе получим HTML код работающей игры Змейка:

<html> <head> <!-- HEAD_BLOCK_1 --><meta charset="UTF-8"><title>Snake Game</title><!-- /HEAD_BLOCK_1 --> <!-- HEAD_BLOCK_2 --><style>body {color:#000; background:#f0f0f0; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; } canvas { border: 1px solid #000; }</style><!-- /HEAD_BLOCK_2 --> <!-- HEAD_BLOCK_3 --><script>let canvas, ctx, snake, food, score, direction; function init() { canvas = document.createElement('canvas'); ctx = canvas.getContext('2d'); document.body.appendChild(canvas); canvas.width = 400; canvas.height = 400; snake = [{ x: 10, y: 10 }]; food = { x: Math.floor(Math.random() * 20), y: Math.floor(Math.random() * 20) }; score = 0; direction = { x: 0, y: 0 }; window.addEventListener('keydown', changeDirection); gameLoop(); } function gameLoop() { update(); draw(); setTimeout(gameLoop, 100); } function update() { const head = { x: snake[0].x + direction.x, y: snake[0].y + direction.y }; snake.unshift(head); if (head.x === food.x && head.y === food.y) { score++; food = { x: Math.floor(Math.random() * 20), y: Math.floor(Math.random() * 20) }; } else { snake.pop(); } } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); for (let segment of snake) { ctx.fillStyle = 'green'; ctx.fillRect(segment.x * 20, segment.y * 20, 20, 20); } ctx.fillStyle = 'red'; ctx.fillRect(food.x * 20, food.y * 20, 20, 20); ctx.fillStyle = 'black'; ctx.fillText('Score: ' + score, 10, 20); } function changeDirection(event) { if (event.key === 'ArrowUp') { direction = { x: 0, y: -1 }; } else if (event.key === 'ArrowDown') { direction = { x: 0, y: 1 }; } else if (event.key === 'ArrowLeft') { direction = { x: -1, y: 0 }; } else if (event.key === 'ArrowRight') { direction = { x: 1, y: 0 }; } } window.onload = init;</script><!-- /HEAD_BLOCK_3 --> </head> <body> <!-- BODY_BLOCK_1 --><header><h1>Snake Game</h1></header><!-- /BODY_BLOCK_1 --> <!-- BODY_BLOCK_2 --><main><p>Use arrow keys to move the snake and eat the food!</p></main><!-- /BODY_BLOCK_2 --> <!-- BODY_BLOCK_3 --><footer><p>© 2024 Snake Game</p></footer><!-- /BODY_BLOCK_3 --> </body> </html>

Игра Змейка

Вот тут можете поиграть в начальную версию игру, описанную в статье: Змейка 1.0

А вот версия игры после нескольких последовательных запросов на улучшение кода: Змейка 3.0

Игра Змейка, созданная ИИ за несколько запросов
Игра Змейка, созданная ИИ за несколько запросов

Ну и как этим пользоваться если я не знаю HTML?

Очень хороший вопрос! Ответ - сделаем Телеграм бота, который может делать все что описано выше и может получать от вас инструкции голосом.

Бота соберем на No-code платформе ProTalk, так как там вся описанная в статье механика сделана мной в виде готового плагина/функции к боту.

Создать бота в Телеграм вы можете по этому видео-примеру: Тык

Обзор платформы в целом тут: Тык

В итоге вы получаем такого бота:

Бот, созданный на платформе ProTalk
Бот, созданный на платформе ProTalk

Возможности бота

  • Создавать код HTML страниц с поддержкой скриптов
  • Задачи можно ставить голосом
  • Результат работы над страницей виден сразу
  • Если возможность сохранять и загружать код в GitHub
  • Можно взять код страницы по внешней ссылке и доработать его

Что еще можно создавать ботом?

  • Любые калькуляторы цен на сайт
  • Готовые игры для Телеграм MiniApp
  • Одностраничный сайт

И многое другое …

А посмотреть можно?

Да, процесс работы с ботом показан на видео: Смотреть

Итоги

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

2
1 комментарий
\n \n \n

Welcome

\n

Main content here

\n \n \n","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Главный фокус"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

И сразу разоблачение: мы НИКОГДА не будем показывать ИИ сам код HTML!

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Мы подготовим словарь, содержащий блоки и показывать будем только его.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вот код функции, которая создает словарь в Python по нашей структуре HTML документа:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import re\n\ndef html_to_dict(html):\n structure = {\n 'html': {\n 'head': {'blocks': []},\n 'body': {'blocks': []}\n }\n }\n head_pattern = r'(.*?)'\n body_pattern = r'(.*?)'\n head_blocks = re.findall(head_pattern, html, re.DOTALL)\n body_blocks = re.findall(body_pattern, html, re.DOTALL)\n structure['html']['head']['blocks'] = head_blocks\n structure['html']['body']['blocks'] = body_blocks\n return structure","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В результате наш код пустой странице после выполнения этого кода будет выглядеть так:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"{\n \"html\": {\n \"body\": {\n \"blocks\": [\n \"

Welcome

\",\n \"

Main content here

\",\n \"\"\n ]\n },\n \"head\": {\n \"blocks\": [\n \"My Page\",\n \"\",\n \"\"\n ]\n }\n }\n}","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

И вот именно в этом виде ИИ будет работать с кодом.

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Теперь немного промптинга, куда уж без него )"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Роль для ИИ может выглядеть так:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"Твоя задача - создавать и редактировать HTML/CSS/JavaScript код.\n \nПри этом код страницы соответствует такому шаблону:\n```\n\n \n My Page\n \n \n \n \n

Welcome

\n

Main content here

\n \n \n\n```\n\nТвой ответ должен быть всегда только в виде строк с описанием изменений:\n```\nhead:::1:::@@@My Website@@@&&&\nhead:::2:::@@@@@@&&&\nhead:::3:::@@@@@@&&&\nhead:::4:::@@@@@@&&&\nbody:::1:::@@@

Welcome to our site

@@@&&&\nbody:::2:::@@@@@@&&&\nbody:::3:::@@@

Latest News

Content here

@@@&&&\nbody:::4:::@@@@@@&&&\nbody:::5:::@@@@@@&&&\n```\n\nВажно чтобы стили и скрипты были описаны внутри HTML, исключения могут быть только ссылки на библиотеки стилей (например bootstrap) или JavaScript (jquery) должны быть только в виде внешних ссылок.\nЕсли ты добавляешь код, которого еще не было, то добавляй его в новый номер блока.\nЕсли ты хочешь изменить код в блоке, который уже есть, то обязательно ответь полным кодом этого блока.","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Теперь посмотрим как выглядит запрос к ИИ:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"# html_dict - словарь, полученный из HTML на предыдущем шаге\n# task - Задача, например: \"Создай игру Змейка\"\n\napi_key = 'sk-XXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'\nclient = openai.OpenAI(api_key=api_key)\nresponse = client.chat.completions.create(\n model=\"gpt-4o-mini\", #\"o1-mini\",\n messages=[\n {\n \"role\": \"user\",\n \"content\": role # - это наш промпт, приведенный выше\n },\n {\n \"role\": \"user\",\n \"content\": f\"Текущий код страницы: ```{html_dict}``` и вот новая задача: ```{task}```\"\n }\n ]\n)\nai_reply = response.choices[0].message.content","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

И вот какой ответ мы получим от ИИ при пустой странице и запросе \"создай игру Змейка\" (на модели gpt-4o-mini):

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"head:::1:::@@@Snake Game@@@&&&\nhead:::2:::@@@@@@&&&\nhead:::3:::@@@@@@&&&\nbody:::1:::@@@

Snake Game

@@@&&&\nbody:::2:::@@@

Use arrow keys to move the snake and eat the food!

@@@&&&\nbody:::3:::@@@@@@&&&","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Назад в HTML!"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Теперь наша задача из этой абракадабры обратно собрать HTML код )

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Сначала напишем функцию, которая в нашем словаре внесет правки предлложенные ИИ:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import copy\n\ndef merge_changes(current_structure, changes):\n new_structure = {\n 'html': {\n 'head': {'blocks': copy.deepcopy(current_structure['html']['head']['blocks'])},\n 'body': {'blocks': copy.deepcopy(current_structure['html']['body']['blocks'])}\n }\n }\n for change in changes:\n if change and ':::' in change:\n section, number, content = change.split(':::', 2)\n section = section.replace('\\n', '').replace(' ', '')\n content = content.split('@@@')[1].replace('\\n', '').replace('\\r', '')\n block_index = int(number) - 1\n if section == 'head':\n while len(new_structure['html']['head']['blocks']) <= block_index:\n new_structure['html']['head']['blocks'].append('')\n new_structure['html']['head']['blocks'][block_index] = content\n elif section == 'body':\n while len(new_structure['html']['body']['blocks']) <= block_index:\n new_structure['html']['body']['blocks'].append('')\n new_structure['html']['body']['blocks'][block_index] = content\n return new_structure\n \n # changes - это ответ от ИИ с предложенным списком изменений\n new_structure = merge_changes(current_structure, changes.split('&&&\\n'))","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Итогом работы этой функции будет обновленный словарь вида:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"{\n \"html\": {\n \"body\": {\n \"blocks\": [\n \"

Snake Game

\",\n \"

Use arrow keys to move the snake and eat the food!

\",\n \"\"\n ]\n },\n \"head\": {\n \"blocks\": [\n \"Snake Game\",\n \"\",\n \"\"\n ]\n }\n }\n}","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Ну и наконец-то мы теперь можем собрать обратно HTML код вот такой функцией:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"def dict_to_html(structure):\n html = ['', '']\n for i, block in enumerate(structure['html']['head']['blocks'], 1):\n if block: # Проверяем, что блок не пустой\n html.append(f' {block}')\n html.append('')\n html.append('')\n for i, block in enumerate(structure['html']['body']['blocks'], 1):\n if block: # Проверяем, что блок не пустой\n html.append(f' {block}')\n html.append('')\n html.append('')\n return '\\n'.join(html)","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

На выходе получим HTML код работающей игры Змейка:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"\n\n Snake Game\n \n \n\n\n

Snake Game

\n

Use arrow keys to move the snake and eat the food!

\n \n\n","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Игра Змейка"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вот тут можете поиграть в начальную версию игру, описанную в статье: Змейка 1.0

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

А вот версия игры после нескольких последовательных запросов на улучшение кода: Змейка 3.0

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"Игра Змейка, созданная ИИ за несколько запросов","image":{"type":"image","data":{"uuid":"b7d02eac-051a-565a-a343-6d45aec58b79","width":1150,"height":1312,"size":51855,"type":"png","color":"2d2d2d","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIASwBLAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAwEI/8QAHhAAAgMBAAIDAAAAAAAAAAAAAQIDBBEAEiEiQYH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABcRAQEBAQAAAAAAAAAAAAAAAAABESH/2gAMAwEAAhEDEQA/AMXy27ddVirykJ4LqhBg+I++JN28BYVRYlAGAO3r94qx2J1ruizyBdX0GOcAEknSdJ4P/9k="}}}]}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Ну и как этим пользоваться если я не знаю HTML?"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Очень хороший вопрос! Ответ - сделаем Телеграм бота, который может делать все что описано выше и может получать от вас инструкции голосом.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Бота соберем на No-code платформе ProTalk, так как там вся описанная в статье механика сделана мной в виде готового плагина/функции к боту.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Создать бота в Телеграм вы можете по этому видео-примеру: Тык

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Обзор платформы в целом тут: Тык

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В итоге вы получаем такого бота:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"Бот, созданный на платформе ProTalk","image":{"type":"image","data":{"uuid":"01fcfc40-fbae-5c6f-a08c-b7eb1ef16968","width":2336,"height":1340,"size":457960,"type":"png","color":"26272c","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQIASwBLAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAAKAAoDAREAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAABAUGCP/EACEQAAIBAwQDAQAAAAAAAAAAAAECAwAFEQQGBzEhQVKz/8QAGAEAAgMAAAAAAAAAAAAAAAAAAgMBBQb/xAAhEQABAwMEAwAAAAAAAAAAAAACAAEDBBEyFCExVJLR4f/aAAwDAQACEQMRAD8Ay9y/uWLc/K1mvcEmst8kOlgWfVhlmkgdHctIowBJgYx1nFFRU50riBlvflvSI5QmJyjazIVrgxYkcnXUgnwTaFz+lanUt2S8PqWp7fwC7hilUAOsKgMOwMt7qhlyd0uDFJ2mlDECV+/o1N05f//Z"}}}]}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Возможности бота"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Создавать код HTML страниц с поддержкой скриптов","Задачи можно ставить голосом","Результат работы над страницей виден сразу","Если возможность сохранять и загружать код в GitHub","Можно взять код страницы по внешней ссылке и доработать его"],"type":"UL"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Что еще можно создавать ботом?"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Любые калькуляторы цен на сайт","Готовые игры для Телеграм MiniApp","Одностраничный сайт"],"type":"UL"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

И многое другое …

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"А посмотреть можно?"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Да, процесс работы с ботом показан на видео: Смотреть

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Итоги"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":1,"favorites":0,"reposts":0,"views":1133,"hits":290,"reads":null,"online":0},"dateFavorite":0,"hitsCount":290,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id1528778/1676333-kak-zastavit-ii-na-baze-llm-pisat-polnocennye-prilozheniya-na-html-css-javascript","author":{"id":1528778,"name":"Андрей Тиунов","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"1ab59640-0c4d-5399-9273-731a3ddcdb90","width":200,"height":200,"size":12845,"type":"jpg","color":"d6c3bf","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3925980,"userId":1528778,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3925980"}],"lastModificationDate":1765050288,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":1528778,"name":"Андрей Тиунов","nickname":null,"description":null,"uri":"","avatar":{"type":"image","data":{"uuid":"1ab59640-0c4d-5399-9273-731a3ddcdb90","width":200,"height":200,"size":12845,"type":"jpg","color":"d6c3bf","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":3925980,"userId":1528778,"count":0,"shareImage":"https://api.vc.ru/achievements/share/3925980"}],"lastModificationDate":1765050288,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[{"id":1,"count":2}],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null,"keywords":[],"media":{"type":"image","data":{"uuid":"5ec5ae95-4334-523b-a2bb-d0bb48878a3c","width":1024,"height":1024,"size":299143,"type":"jpg","color":"42494a","hash":"","external_service":[],"base64preview":"/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAQDAwQDAwQEAwQFBAQFBgoHBgYGBg0JCggKDw0QEA8NDw4RExgUERIXEg4PFRwVFxkZGxsbEBQdHx0aHxgaGxr/2wBDAQQFBQYFBgwHBwwaEQ8RGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGhr/wAARCAAKAAoDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAwQFCP/EACQQAAICAAUDBQAAAAAAAAAAAAECAwQABQYREiExURMyQUJh/8QAFAEBAAAAAAAAAAAAAAAAAAAAAf/EABkRAQADAQEAAAAAAAAAAAAAAAEAAgMREv/aAAwDAQACEQMRAD8AjaqlpXM/SY2rKUrcgMbo4aAhn4hVHuB32+O3jDNjRmTmeXleoKeZ3BCdOp/cF1TXhq2PRqxRwxRpKyJGoVVPAHcAdjvjPk9mYzykyyElz9j5wZLY9MNQLc5P/9k="}},"customCover":null,"robotsTag":null,"categories":[13],"isAnonymized":true}};