Тестируем AI на создании прикладного приложения

Всем привет, я Григорий Тумаков, CTO в Моризо Диджитал.

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

Поэтому решил на этом не останавливаться. Если есть инструменты — их надо протестировать на какой-то реальной задаче.

Далее в статье мой опыт сравнения для прикладной задачи трех AI-инструментов: Phind, ChatGPT, Machinet.

Идея и промпт

Создай страницу с использованием JS и html для получения погоды по названию города. На странице должна быть форма заполнения названия города, кнопка «Узнать погоду». Не забудь учесть проблемы с CORS.

Работать это должно таким образом:

  1. Пользователь вводит название города.  

  2. По названию города нужно запросить долготу и широту по этому API

    curl -X POST
    -H "Content-Type: application/json"
    -H "Accept: application/json"
    -H "Authorization: Token {API_TOKEN}"
    -H "X-Secret: {SECRET_TOKEN}"
    -d '[ "{Название города}" ]'
    https://cleaner.dadata.ru/api/v1/clean/address

  1. Далее долготу и широту необходимо подставить в запрос —  https://api.openweathermap.org/data/2.5/weather? lat={lat}&lon={lon}&appid={API key}

  2. Вывести результат обращения к API.

Для сравнения я загрузил промпт в каждую из трех нейросетей.

Рассказываю, что получилось.

ChatGPT

*Вот пример кода HTML и JavaScript, который реализует описанную вами функциональность:*


<html lang=«en»>
<head>
<meta charset=«UTF-8»>
<meta name=«viewport» content=«width=device-width, initial-scale=1.0»>
<title>Получить погодуtitle>
head>
<body>
<h2>Узнать погодуh2>
<form id=«weatherForm»>
  <label for=«cityInput»>Введите название города: label><br>
  <input type=«text» id=«cityInput» name=«cityInput»><br>
  <button type=«submit»>Узнать погодуbutton>
form>
<div id=«weatherResult»>div>

<script>
document.getElementById ('weatherForm').addEventListener ('submit', function(event) {
  event.preventDefault (); // Prevent default form submission
 
  var city = document.getElementById ('cityInput').value.trim ();
  if (city === '') {
    alert ('Пожалуйста, введите название города.');
    return;
  }
 
  fetchCoordinates (city)
    .then (function(coordinates) {
      return fetchWeather (coordinates.lat, coordinates.lon);
    })
    .then (function(weather) {
      displayWeather (weather);
    })
    .catch (function(error) {
      console.error ('Произошла ошибка:', error);
    });
});

function fetchCoordinates(city) {
  var apiToken = '{API_TOKEN}';
  var secretToken = '{SECRET_TOKEN}';
 
  return fetch ('https://cleaner.dadata.ru/api/v1/clean/address', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': 'Token ' + apiToken,
      'X-Secret': secretToken
    },
    body: JSON.stringify ([city])
  })
  .then (function(response) {
    if (! response.ok) {
      throw new Error ('Ошибка при получении координат города.');
    }
    return response.json ();
  })
  .then (function(data) {
    var coordinates = data[0].geo_lat && data[0].geo_lon? { lat: data[0].geo_lat, lon: data[0].geo_lon } : null;
    if (! coordinates) {
      throw new Error ('Координаты для указанного города не найдены.');
    }
    return coordinates;
  });
}

function fetchWeather(lat, lon) {
  var apiKey = '{API_KEY}';
 
  return fetch (`https://api.openweathermap.org/data/2.5/weather? lat=${lat}&lon=${lon}&appid=${apiKey}`)
    .then (function(response) {
      if (! response.ok) {
        throw new Error ('Ошибка при получении данных о погоде.');
      }
      return response.json ();
    });
}

function displayWeather(weather) {
  var weatherResultDiv = document.getElementById ('weatherResult');
  weatherResultDiv.innerHTML = '';
 
  var cityName = weather.name;
  var weatherDescription = weather.weather && weather.weather[0] ? weather.weather[0].description: '';
  var temperature = weather.main && weather.main.temp? Math.round (weather.main.temp — 273.15) : '';
 
  var weatherInfo = document.createElement ('p');
  weatherInfo.textContent = `Погода в городе ${cityName}: ${weatherDescription}, Температура: ${temperature}°C`;
  weatherResultDiv.appendChild (weatherInfo);
}
script>
body>
html>

*Пожалуйста, обратите внимание, что вы должны заменить `{API_TOKEN}`, `{SECRET_TOKEN}` и `{API_KEY}` на свои реальные значения. Кроме того, помните, что без использования сервера для обхода CORS вы можете столкнуться с проблемами безопасности при попытке выполнить этот код в браузере.* 

Очевидно, что этот код не выведет нам результат, мы получим ошибку CORS. 

86908cee6c8afe59925aed251db00339.png

Поэтому я попросил придумать решение этой проблемы.

66133d3a1123e0fd9c0ed0f2ebb2997c.png

Вот, что предложил ChatGPT

*Чтобы обойти ошибку CORS при обращении к API с клиентской стороны, можно воспользоваться серверной прокси. Например, можно создать простой сервер с использованием Node.js, который будет проксировать запросы к API `cleaner.dadata.ru` и передавать результат обратно на клиент.*

*Вот пример кода для сервера на Node.js:*

const express = require ('express');
const fetch = require ('node-fetch');
const cors = require ('cors');

const app = express ();
const PORT = process.env.PORT || 3000;

app.use (cors ());

app.post ('/getCoordinates', async (req, res) => {
  try {
    const cityName = req.body.cityName;
    const response = await fetch ('https://cleaner.dadata.ru/api/v1/clean/address', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': 'Token {API_TOKEN}',
        'X-Secret': '{SECRET_TOKEN}'
      },
      body: JSON.stringify ([cityName])
    });

    const data = await response.json ();
    const coordinates = data[0].geo_lat && data[0].geo_lon? { lat: data[0].geo_lat, lon: data[0].geo_lon } : null;

    if (coordinates) {
      res.json (coordinates);
    } else {
      res.status (404).json ({ error: 'Coordinates not found for the provided city name' });
    }
  } catch (error) {
    console.error ('Error:', error);
    res.status (500).json ({ error: 'Internal Server Error' });
  }
});

app.listen (PORT, () => {
  console.log (`Server is running on port ${PORT}`);
});

*Изменения в коде:*

1. *Добавлена библиотека `express` для создания сервера.*

2. *Добавлена библиотека `node-fetch` для выполнения HTTP запросов на сервере.*

3. *Добавлена библиотека `cors` для обработки CORS политики.*

4. *Создан маршрут `/getCoordinates`, который принимает POST запрос с именем города, отправляет запрос к API `cleaner.dadata.ru` через сервер, получает координаты города и отправляет их обратно клиенту.*

*На стороне клиента теперь нужно обратиться к серверу для получения координат города, затем использовать полученные координаты для запроса погоды к API `api.openweathermap.org`.*

<html lang=«en»>
<head>
  <meta charset=«UTF-8»>
  <meta name=«viewport» content=«width=device-width, initial-scale=1.0»>
  <title>Weather Apptitle>
head>
<body>
  <h1>Weather Apph1>
  <form id=«cityForm»>
    <label for=«cityName»>Enter city name: label>
    <input type=«text» id=«cityName» name=«cityName»>
    <button type=«submit»>Get Weatherbutton>
  form>
  <div id=«weatherData»>div>

  <script>
    document.getElementById ('cityForm').addEventListener ('submit', async (event) => {
      event.preventDefault ();
     
      const cityName = document.getElementById ('cityName').value;
      const response = await fetch ('http://localhost:3000/getCoordinates', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify ({ cityName })
      });

      const data = await response.json ();

      if (data.error) {
        document.getElementById ('weatherData').textContent = data.error;
      } else {
        const weatherResponse = await fetch (`https://api.openweathermap.org/data/2.5/weather? lat=${data.lat}&lon=${data.lon}&appid={API key}`);
        const weatherData = await weatherResponse.json ();
        document.getElementById ('weatherData').textContent = JSON.stringify (weatherData, null, 2);
      }
    });
  script>
body>
html>

*Теперь клиентский JavaScript делает запрос к серверу для получения координат города, затем использует эти координаты для запроса погоды к API `api.openweathermap.org`.*

К сожалению, снова получаю ошибку.

Error [ERR_REQUIRE_ESM]: require () of ES Module …/ai/chat-gpt/node_modules/node-fetch/src/index.js from …/ai/chat-gpt/server.js not supported. Instead change the require of index.js in …/ai/chat-gpt/server.js to a dynamic import () which is available in all CommonJS modules. at Object. (…/ai/chat-gpt/server.js:50:15) { code: 'ERR_REQUIRE_ESM' }

Прошу поправить и получаю новый блок кода, с указанием необходимости установки axios.

const express = require ('express');
const axios = require ('axios');
const cors = require ('cors');

const app = express ();
const PORT = process.env.PORT || 3000;

app.use (cors ());
app.use (express.json ());

app.post ('/getCoordinates', async (req, res) => {
  try {
    const cityName = req.body.cityName;
    const response = await axios.post ('https://cleaner.dadata.ru/api/v1/clean/address', [cityName], {
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': 'Token {API_TOKEN}',
        'X-Secret': '{SECRET_TOKEN}'
      }
    });

    const data = response.data;
    const coordinates = data[0].geo_lat && data[0].geo_lon? { lat: data[0].geo_lat, lon: data[0].geo_lon } : null;

    if (coordinates) {
      res.json (coordinates);
    } else {
      res.status (404).json ({ error: 'Coordinates not found for the provided city name' });
    }
  } catch (error) {
    console.error ('Error:', error);
    res.status (500).json ({ error: 'Internal Server Error' });
  }
});

app.listen (PORT, () => {
  console.log (`Server is running on port ${PORT}`);
});

И наконец получаю нужный результат, приложение работает

05e5ede75f9f55af792f7497fdccd979.png

Попрошу отформатировать полученный ответ от API.

*Выведи красиво ответ от API, чтобы было понятно какая сейчас погода, скорость ветра и так далее*

685f30356276e679bd567d2e421b1c72.pngdf985a6a6190c534514695b4dab70c40.png

Phind

Загружаю промпт и сразу вижу различия в результате. Phind предпочел разделить html и js код в разные файлы, что радует.

c5963526936082f4baf8c6ae755565c0.pnge7381a8456f20080a88953e898db2626.png

К сожалению, не получаю работающую программу с первого запроса, поэтому прошу добавить веб-сервер для обхода проблемы с CORS. Вижу, что Phind дает более развернутые ответы и предлагает несколько вариантов.

a11552b601ea69e347d7dfe08d43307f.pnge5e417d8c85f85977677a61e71adf01e.png

Пробую сначала первый вариант. Спрашиваю, как его использовать, и получаю достаточно подробную инструкцию. 

3338227ce1ce9b21b07e2f11233ce51f.png815f6a05cad7caa86eea5ea03ecbdcfd.png

После изменения кода пробую запустить сервер, но получаю уже знакомую ошибку.

```

Error [ERR_REQUIRE_ESM]: require () of ES Module …/ai/chat-gpt/node_modules/node-fetch/src/index.js from …/ai/chat-gpt/server.js not supported. Instead change the require of index.js in …/ai/chat-gpt/server.js to a dynamic import () which is available in all CommonJS modules. at Object. (…/ai/chat-gpt/server.js:50:15) { code: 'ERR_REQUIRE_ESM' }

```

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

dde34b349a8c351c8661d4c86f958041.png

Пробую использовать предложенное решение — все работает.

d7d55e921526b438c06d7ee7808c8291.png

Прошу красиво вывести ответ от апи, чтобы было понятно, какая погода, ветер и т.д.

eb8409a02cd3d43d32731069bfbaa120.png

Правлю код и пробую получить данные.

a841b95d3c53d854ef659daba4f81ad4.png

Теперь тестирую второй предложенный вариант. Применяю изменения, запускаю.

Но выходит ошибка — прошу ее исправить.

18402aec45ea720846f2465f8ad11d8a.png

Снова получаю развернутый ответ с объяснением ошибки и несколькими решениями, но теперь уже знаю, что первое решение поможет, поэтому не продолжаю.

Machinet

Загружаю промпт, получаю ответ:

*Хорошо, я могу помочь вам создать страницу для получения погоды по названию города с использованием JavaScript и HTML. Для начала, вам понадобится создать HTML-разметку для страницы. Вот пример кода:*


<html>
<head>
  <title>Получение погодыtitle>
  <script src=«https://code.jquery.com/jquery-3.6.0.min.js»>script>
head>
<body>
  <h1>Получение погоды по названию городаh1>
  <form id=«weatherForm»>
    <label for=«cityInput»>Введите название города: label>
    <input type=«text» id=«cityInput» required>
    <button type=«submit»>Узнать погодуbutton>
  form>
  <div id=«weatherResult»>div>

  <script>
    // Обработчик события отправки формы
    $(»#weatherForm»).submit (function(event) {
      event.preventDefault (); // Предотвращаем отправку формы

      var city = $(»#cityInput»).val (); // Получаем значение поля ввода

      // Запрос для получения долготы и широты
      $.ajax ({
        url: «https://cleaner.dadata.ru/api/v1/clean/address»,
        type: «POST»,
        headers: {
          «Content-Type»: «application/json»,
          «Accept»: «application/json»,
          «Authorization»: «Token {API_TOKEN}»,
          «X-Secret»:»{SECRET_TOKEN}»
        },
        data: JSON.stringify ([city]),
        success: function(response) {
          var latitude = response[0].geo_lat;
          var longitude = response[0].geo_lon;

          // Запрос для получения погоды
          $.ajax ({
            url: «https://api.openweathermap.org/data/2.5/weather»,
            type: «GET»,
            data: {
              lat: latitude,
              lon: longitude,
              appid:»{API key}»
            },
            success: function(weatherData) {
              // Выводим результат обращения к API
              $(»#weatherResult»).html («Текущая погода в городе » + city + »:» + weatherData.weather[0].description);
            },
            error: function() {
              $(»#weatherResult»).html («Ошибка при получении погоды»);
            }
          });
        },
        error: function() {
          $(»#weatherResult»).html («Ошибка при получении координат города»);
        }
      });
    });
  script>
body>
html>

*В этом примере мы используем jQuery для упрощения работы с AJAX-запросами. При отправке формы, мы сначала отправляем запрос на получение долготы и широты города с помощью API от Dadata, а затем используем полученные координаты для запроса погоды с помощью API от OpenWeatherMap. Результат выводится в элементе с id «weatherResult».*

*Обратите внимание, что вам нужно будет заменить {API_TOKEN}, {SECRET_TOKEN} и {API key} на соответствующие значения, полученные при регистрации на соответствующих сервисах.*

Пробую запустить код и выполнить запрос. Снова получаю ошибку CORS, прошу исправить, используя тот же текст, что и в предыдущих нейросетях — и получаю интересный ответ.

0fff4ff1fde97fafd9aea78fbbff3023.png

Выбираю второй подход.

9e61633d2a39ddd06f4486b671dcacfd.png

Пробую запустить сервер и получаю ошибку.

app.use(corsAnywhere ({
        ^
TypeError: corsAnywhere is not a function

Прошу исправить.

45587273ef4f458e66b91932ed71f6fb.png

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

3494afaad6095294f8fc451924c46c08.png

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

d35f5c658e9364d327d25036778f873f.png

Пробую дать больше информации об ошибке.

851c505981ff015dfb423a3bf12a5639.png

К сожалению, не получаю никакого решения, только рекомендации. Здесь нет смысла дальше продолжать.

Результаты

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

Если же выбирать из ChatGPT и Phind, то я бы предпочел Phind, поскольку она давала более развернутые ответы и предлагала больше вариантов решения задач.

© Habrahabr.ru