Вчера вечером, 10 января 2024 г., в OpenAI официально запустили GPT Store.

Давайте разберемся, что это такое, и что оно дает. А затем создадим свой собственный GPT и добавим его в GPT Store.

Концепция "custom GPT" в терминах OpenAI - это кастомный набор инструкций (custom prompt), который может быть создан любым пользователем ChatGPT с подпиской Plus. Каждый такой custom GPT за счет своего набора инструкций хорошо заточен под решение своей конкретной задачи. Есть custom GPT, которые хорошо пишут код, есть custom GPT, которые играют роль репетитора или психотерапевта и т.д.

Таким образом, GPT store - это большая библиотека инструкций для разных задач внутри ChatGPT. Она создается и поддерживается коммьюнити и очень сильно напоминает google play market или apple app store.

Процесс создания Custom GPT заключается в том, что в интерфейсе ChatGPT пользователь составляет подробные инструкции, что и как его GPT должен делать, дает описание, придумывает название, примеры использования и т.д. - всё это сохраняется на серверах OpenAI.

В результате автор custom GPT может:

  1. во-первых, самостоятельно использовать созданный им custom GPT, чтобы не было необходимости делать copy/paste одних и тех же же инструкций, если он часто ими пользуется

  2. во-вторых, и это основное нововведение - автор может расшарить созданный им Custom GPT со всеми другими пользователями ChatGPT

При этом, механика такова, что, когда другие пользователи ChatGPT используют чужой custom GPT, то его автор не видит, что они пишут (сохраняется приватность данных).

И в обратную сторону - пользователи не видят "внутренностей" (инструкций) чужого custom GPT, т.е. подразумевается защита авторских прав (это можно потенциально взломать с помощью хитро подобранных промптов, но сейчас не об этом статья).

Монетизации пока нет, но в OpenAI говорят, что добавят ее в 1кв 2024 года.

Скриншот GPT Store
Так выглядит GPT store
Так выглядит GPT store

Скри

Дополнительные возможности custom GPT

С моей точки зрения, сила custom GPT не столько и не только в хорошо подобранных для решения определенных задач кастомных инструкций, сколько в том, что custom GPT умеют вызывать сторонние внешние API сервисы для своей работы.

Т.е., например, у вас есть свой ресторан. Вы делаете сервер, который поддерживает API вызовы запроса меню, заказа блюд на дом по определенному адресу, и бронирования столика. Далее вы пишете свой custom GPT, которые "знает" этот API, и предоставляете всем пользователям chatGPT прямо не уходя с платформы делать заказ в вашем ресторане в режиме чата.

Более того, я думаю, что в будущем openAI дополнят концепцию Custom GPTs концепцией и функционалом агентов. Т.е. Custom GPTs, сделанные разными пользователями и заточенные под разные задачи, смогут по цепочке вызывать друг друга для выполнения некоего большого задания, которое было им поручено.

Пример разработки custom GPT

Так как ресторана у меня нет, можно потренироваться и создать custom GPT, который по запросу пользователя выдает текущие цены акций, торгуемых на Московской Бирже.

Для этого нам надо:

  • Разобраться откуда и как брать цены

  • Написать соответствующие инструкции для нашего custom GPT

У Московской Биржи есть бесплатный публично доступный API сервер (https://iss.moex.com), который выдает текущие цены с задержкой 15 минут, его и будем использовать.

Документация на него тут и тут.

Пользователь может попросить дать цену акции по названию ("Сколько сейчас стоит Газпром"), по тикеру ("Дай цену GAZP") или по ISIN коду акции ("Напиши котировку RU0007661625")

Получение цены по определенной бумаге

Прочитав документацию, выясняем, что для получения текущей цены акции с тикером (securityID) GAZP, необходимо выполнить GET запрос такого вида:

https://iss.moex.com/iss/engines/stock/markets/shares/boards/tqbr/securities/GAZP.json?iss.only=marketdata&marketdata.columns=SECID,LAST

Параметры iss.only=marketdata и marketdata.columns=SECID,LAST ограничивают получаемый по API JSON секцией marketdata и двумя столбцами - SECID и LAST price. Если этого не сделать, то в отдаваемом JSON будет очень много лишних данных, из-за чего GPT может "запутаться" и выдать галлюцинацию.

Из примера строки запроса видно, что необходимо передавать SECID как ключ акции.

Что же делать, если пользователь не указал SECID, а задал вопрос по названию или по ISIN акции?

Получение списка торгуемых акций

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

https://iss.moex.com/iss/engines/stock/markets/shares/boards/tqbr/securities.json?iss.only=securities&securities.columns=SECID,SHORTNAME,SECNAME,ISIN

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

Создаем непосредственно Custom GPT

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

Скриншот создания нового "чистого" GPT
Выглядит создание нового GPT вот так
Выглядит создание нового GPT вот так

Даем название, которое отображается в библиотеке GPTs, и по которому другие пользователи могут найти нашу GPT - "Цены акций Московская Биржа (Moex)", заполняем описание.

И самое главное (но не самое сложное) - инструкции, по которым работает наш custom GPT и которые выглядят таким образом:

Your task is to retrieve and to display to the user the current price of a stock on Moscow Exchange (Moex).

User should provide you with either security ID (SECID) of the stock, or with ISIN or with stock name. Example of SECID: AFLT. Example of ISIN: RU0009062285. Example of stock name: Аэрофлот.

Once you know the stock the user is interested in, you use  the following algorythm:
1. You request Moscow Exchange API to give you all stocks that are currently traded (API operationId GetSecuritiesList)
2. In the list of all stocks you find the security ID (SECID) of the stock requested by the user. If you can't find the stock, let the user know that the stock he requested could not be found and stop the algorythm.
3. Using the SECID (if you found it) you then request the price of this stock from Moscow Exchange API (API operationId GetSecurityPrice) and display it to the user with a disclamer that the displayed price has a 15-minutes delay due to Exchange price publishing rules


Be very polite with the user and ask the user whether you can help with any other stock after completing the request.

В теле инструкций я замечу несколько основных моментов:

  • Мы просим модель сначала выяснить SECID для акции, указанной пользователем

  • А затем, узнав SECID, выполнить запрос ее цены отдельным запросом (третий пункт)

  • Первый шаг выполнить с помощью API operationId GetSecuritiesList, второй шаг - с помощью API operationId GetSecurityPrice

Теперь перейдем к самому сложному - как правильно задать спецификацию API, чтобы наш GPT сделал правильные вызовы к серверу московской биржи.

Все API вызовы, которые может делать GPT задаются в секции Actions
Все API вызовы, которые может делать GPT задаются в секции Actions

Внутри настройки Actions нам необходимо задать спецификацию схемы API вызовов, чтобы GPT не запутался и сделал всё правильно. OpenAI рекомендует указывать эту спецификацию в формате openapi. Скорее всего они специально натаскивали модель на хорошее понимание этой спеки.

Чтобы не делать спецификацию самостоятельно (зачем нам разбираться в куче скобочек, запятых и кавычек?), можно попросить chatGPT это сделать за нас.

У меня это получилось примерно так:

Большой скриншот

Далее я скормил в GPT пример реального ответа, полученного от сервера биржи, попросил уточнить спецификацию, провел то же самое для второго URL (получение общего списка бумаг), попросил сделать еще несколько корректировок, и в результате у меня получилась следующая спецификация openAPI для наших двух запросов, которую я с радостью вставил в поле Schema.

Кому интересно, вот лог чата, в результате которого GPT помог мне подготовить схему: ссылка

Получившаяся спецификация OpenAPI
{
  "openapi": "3.1.0",
  "info": {
    "title": "Stock Market Data API for Moscow Exchange (Moex)",
    "version": "1.0.0",
    "description": "API for retrieving stock market data on the Moscow Exchange (Moex)."
  },
  "servers": [
    {
      "url": "https://iss.moex.com/iss"
    }
  ],
  "paths": {
	"/engines/stock/markets/shares/boards/tqbr/securities.json": {
      "get": {
        "summary": "Get full list of securities",
		"operationId": "GetSecuritiesList",
        "description": "Returns the full list of securities currently traded on the Moscow Exchange.",
        "parameters": [
          {
            "name": "iss.only",
            "in": "query",
            "required": true,
            "description": "Specify data sections to be returned. Always set to 'securities'.",
            "schema": {
              "type": "string",
              "enum": ["securities"]
            }
          },
          {
            "name": "securities.columns",
            "in": "query",
            "required": true,
            "description": "Specify columns to be returned in the securities section. Always set to 'SECID,SHORTNAME,SECNAME,ISIN'.",
            "schema": {
              "type": "string",
              "enum": ["SECID,SHORTNAME,SECNAME,ISIN"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "securities": {
                      "type": "object",
                      "properties": {
                        "metadata": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "type": { "type": "string" },
                              "bytes": { "type": "integer" },
                              "max_size": { "type": "integer" }
                            }
                          }
                        },
                        "columns": {
                          "type": "array",
                          "items": { "type": "string" }
                        },
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "items": [
                              { "type": "string", "description": "Security ID (SECID)" },
                              { "type": "string", "description": "Short Name (SHORTNAME)" },
                              { "type": "string", "description": "Security Name (SECNAME)" },
                              { "type": "string", "description": "ISIN code (ISIN)" }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/engines/stock/markets/shares/boards/tqbr/securities/{SECID}.json": {
      "get": {
        "summary": "Get market data for a security with specific security ID (SECID)",
		"operationId": "GetSecurityPrice",
        "description": "Returns market data for the specified security on the Moscow Exchange.",
        "parameters": [
          {
            "name": "SECID",
            "in": "path",
            "required": true,
            "description": "Security identifier (e.g., 'AFLT')",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "iss.only",
            "in": "query",
            "required": true,
            "description": "Specify data sections to be returned",
            "schema": {
              "type": "string",
              "enum": ["marketdata"]
            }
          },
          {
            "name": "marketdata.columns",
            "in": "query",
            "required": true,
            "description": "Specify columns to be returned in the marketdata section",
            "schema": {
              "type": "string",
              "enum": ["SECID,LAST"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful response",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "marketdata": {
                      "type": "object",
                      "properties": {
                        "metadata": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "type": {
                                "type": "string"
                              },
                              "bytes": {
                                "type": "integer"
                              },
                              "max_size": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "columns": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "data": {
                          "type": "array",
                          "items": {
                            "type": "array",
                            "minItems": 2,
                            "maxItems": 2,
                            "items": [
                              {
                                "type": "string",
                                "description": "Security ID (SECID)"
                              },
                              {
                                "type": "number",
                                "description": "Last traded price (LAST)"
                              }
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Скриншот настроек Actions создаваемого Custom GPT
Так это выглядит в интерфейсе
Так это выглядит в интерфейсе

С

Последний штрих - примеры запросов

Это кнопки, которые появляются у пользователя, когда он "заходит" в выбранный GPT

Конфигурация примеров запросов
Конфигурация примеров запросов

Финальное тестирование

Так выглядит экран, когда пользователь "заходит" в custom GPT, выбирая его из GPT Store:

Экран пользователя, когда он выбрал наш GPT из GPT store
Экран пользователя, когда он выбрал наш GPT из GPT store

ИИИИ.. Барабанная дробь...

пример работы с GPT
пример работы с GPT

Бинго, оно работает!

Надеюсь, статья была полезной

Комментарии (8)


  1. dimitrii_z
    14.01.2024 07:21
    +5

    Вот это реально полезно, теперь можно юзать ChatGPT в своих онлайн-чатах на сайте для разгрузки операторов в случаях если для ответа можно получить данные по своему или чужому API. А ещё, например, подключить обработку фильтров по API если кому-то лень подбирать товар вручную, типа "какие у вас есть зимние шины на такую-то машину производства nokian?"


  1. rajce
    14.01.2024 07:21
    +1

    Спасибо очень интересно, а главное оперативно!


  1. anonymous
    14.01.2024 07:21

    НЛО прилетело и опубликовало эту надпись здесь


  1. sneg2015
    14.01.2024 07:21
    +1

    К сожалению пока работать с магазином могут только те у кого есть подписка на gpt.


  1. time2vec
    14.01.2024 07:21

    Пока есть только одна проблема, получая данные с внешних источников, например OHLCV данные торгов на бирже в формате csv, он не умеет сохранить этот файл и обработать его в pandas как датафрейм. Он читает его raw и далее в коде начинает строка за строкой переписывать, таким образом упирается в лимиты символов в коде.


  1. utya
    14.01.2024 07:21

    Круто. Подключу к своей nocodb. Только вопрос, как ограничить доступ?


    1. Ilya999Ilya Автор
      14.01.2024 07:21

      не открывать доступ to everyone. Есть варианты: только для личного пользования, только для пользователей, у которых есть ссылка и everyone. + использовать API авторизацию (GPT умеет авторизовываться)


  1. vanderlyap
    14.01.2024 07:21

    Верно ли я понимаю что настоящая настоящая сила custom GPT в том что с пользователей не будет спроса за дообучение на каком угодно материале