Приветствую, коллеги! Я ведущий системный аналитик в компании ЛАНИТ. В настоящее время я работаю на крупном государственном проекте в области финансов. В своей статье я решила поделиться этапами совершенствования процесса описания интеграционного взаимодействия на отдельно взятом проекте нашей компании и рассказать, как благодаря использованию генеративной модели ChatGPT мы ускорили процесс формирования документации OpenAPI и частично снизили связанные с этим трудозатраты системных аналитиков.
Статья может быть полезна системным аналитикам и командам, проектирующим API по принципу Design API First (Contract first). Материал основан на реальном опыте системных аналитиков нашего проекта и не является руководством или призывом к действию.

На проекте с микросервисной архитектурой для меня и для многих моих коллег обыденной задачей является формирование требований на реализацию API (Application programming interface) – контрактов взаимодействия между различными компонентами системы, микросервисами, смежными системами и т.д.
Этапы совершенствования процесса документирования API
Можно выделить несколько этапов усовершенствования описания API в команде нашего проекта. Далее я кратко остановлюсь на каждом из них, перечислю их достоинства и недостатки, а также поделюсь опытом и результатами использования ChatGPT при написании спецификации OpenAPI.
Первый этап: генерируем спецификацию API на основе кода (подход Code first)
Требования на реализацию API, как и в целом к системе, мы оформляем в Atlassian Confluence. Для операций API указываются путь запроса, выполняемое действие, тело запроса и ответа (в виде модели данных, DTO (Data Transfer Object)), статусы ответов.
Наше описание DTO – это человеческое описание JSON-схемы. Оно включает единичные объекты, массивы объектов и атрибуты. Вложенность объектов друг в друга или атрибутов в объекты имитируется иерархичностью представления. Для каждого объекта/атрибута указываются название, тип, комментарий, сведения об обязательности. Такой подход к описанию спецификаций требований к разработке создан и внедрен нашим руководителем группы аналитиков на проекте.
Вот пример одного из таких DTO. В этом случае также указывается способ формирования значений атрибутов.

В целом эти правила описания DTO специфичны исключительно для нашего проекта, но их стоит упомянуть, потому что это важно для дальнейшего повествования.
На первом этапе модели данных, описанные приведенным выше способом, и описания API аналитики передавали в виде постановок на разработку команде разработчиков, которые на их основе реализовывали в коде API. Далее уже на базе существующего кода генерировалась спецификация OpenAPI. Фактически использовался подход Code first: сначала пишем код, потом генерируем по нему контракт.
Основной недостаток первого подхода заключается в том, что API и контракт, который мы получаем на выходе от разработчика, часто не соответствует тому, что было на входе в постановке от аналитика (разработка может реализовать немного другую модель данных, переименовать атрибуты и т.п.). К сожалению, аналитик об этом узнает только тогда, когда разработчик предоставит реализованный API. Да, эту проблему можно в какой-то степени нивелировать дополнительными этапами согласования постановки между аналитиком и разработкой, но разница между постановкой и конечным результатом, как правило, имеется. Иногда существенная. Как следствие:
-
достаточная разница по времени между тем, как аналитик описал API в спецификации в Confluence и когда получил готовый жесткий контракт API;
-
аналитику приходится дорабатывать спецификации в Confluence по факту реализации API, чтобы иметь его актуальное описание, доступное для дальнейшего использования другими членами команды, например, представителями тестирования и сопровождения.
Второй этап: генерируем программный код на основе спецификации OpenAPI (подход Contract first)
На втором этапе описанный выше процесс создания документации и разработки API был усовершенствован.
Мы применили так называемый подход Contract first – сначала создаем контракт, потом по нему пишем или генерируем код.
Процесс можно представить в виде последовательности шагов.
-
Аналитик по-прежнему начинает с описания требований к API в Confluence.
-
Требования к API оформляются аналитиком в виде YAML-файла – спецификации OpenAPI (ссылка на актуальную версию спецификации).
Такой файл можно оформлять в любом доступном текстовом редакторе, но мы используем Swagger Editor. Он позволяет редактировать спецификации OpenAPI в YAML внутри браузера. У вас есть возможность просматривать документацию в режиме реального времени – слева пишем текст спецификации, справа отображается полнофункциональный интерфейс Swagger UI.

-
Разработанная таким образом спецификация OpenAPI согласовывается с разработкой и в дальнейшем является конечным жестким контрактом взаимодействия между всеми членами ИТ-команды. YAML-файл – место, где изложены единые, согласованные и используемые всеми членами команды требования к API.
-
На основе спецификации OpenAPI разработка осуществляет генерацию программного кода.
Такой подход позволил решить основные проблемы первого этапа и имеет ряд преимуществ.
-
Удалось сформировать единый согласованный контракт API еще до начала процесса разработки. Еще до реализации API в коде он может использоваться членам ИТ-команды при написании постановок на смежные задачи, алгоритмов, использующих этот API, и т.п.
-
Сократилось время на разработку API со стороны разработки.
-
Комментарии (description) и примеры (example) ― очень важная часть спецификации. При таком подходе они добавляются в спецификацию аналитиками с учетом знания системы с точки зрения бизнеса и понимания потребностей и целей пользователей.
Однако вместе с преимуществами мы получили и недостатки.
В частности, трудоемкость работы аналитика увеличилась. Ведь кроме описания требований в Confluence, теперь нужно формировать YAML-файл с описанием спецификации OpenAPI. В итоге для аналитика добавилось много дополнительной, рутинной, достаточно нудной работы, в основном заключающейся в копировании текстов из спецификации в YAML с преобразованием текста по определенному формату, выделением перечислений (enum), схем данных и проставлением ссылок на них. Учитывая, что только для одного DTO с описанием тела ответа число атрибутов у нас может быть более 50, процесс формирования такого YAML-файла – достаточно затратная по времени задача.
В какой-то момент мне пришла идея возложить этот не очень благодарный труд по преобразованию текста из модели данных на плечи искусственного интеллекта, а именно ChatGPT. На различных этапах эксперимента я использовала модели ChatGPT-3.5 и ChatGPT-4.0 (далее по тексту, когда я упоминаю использование модели искусственного интеллекта, я имею в виду ChatGPT). И тут мы плавно подошли к этапу номер три.
Третий этап: привлекаем ChatGPT к формированию спецификации OpenAPI
Различные эксперименты по привлечению искусственного интеллекта для решения задач системного анализа я проводила и ранее. На мой взгляд, ИИ оказывается максимально полезен для преобразования текста из одного формата в другой по определенным правилам, особенно когда речь идет про изменение текста в/из формата, который хорошо знаком ChatGPT. А именно эту задачу мы и решали на этапе 2 – преобразовывали текст из модели данных в Confluence (один формат) в YAML (другой формат, известный ChatGPT). Результат получился достаточно интересным, поэтому захотелось им поделиться.
На третьем этапе мы используем подход, аналогичный подходу два, с той лишь разницей, что:
-
задачу формирования YAML-файла теперь аналитик решает с помощью ChatGPT,
-
полученный черновик YAML-файла проверяется и дорабатывается аналитиком (как правило, требуется внесение незначительных правок). На удивление, с этой задачей ChatGPT справляется достаточно хорошо.
Использование нейросети на третьем этапе позволило нам частично решить основную проблему второго этапа ― снизить трудозатраты аналитика. Теперь мы тратим на описание API примерно столько же времени, сколько на первом этапе, но в итоге получаем все преимущества второго этапа ― жесткий единый контракт взаимодействия посредством API и снижение трудозатрат разработки.
Пишем спецификацию OpenAPI с помощью ChatGPT
Спецификация OpenAPI представляет собой один из способов описания REST API и в версии 3.0 на верхнем уровне включает следующие объекты:
-
openapi,
-
info,
-
servers,
-
paths,
-
components,
-
security,
-
tags,
-
externalDocs.
Самой полезной для нас оказалась помощь нейросети при генерации текста для объекта components. В статье мы будем описывать формирование именно этой части спецификации OpenAPI. Объект components, в свою очередь, включает объект schemas – описание моделей данных (фактически это и есть наши DTO), которые могут быть переиспользованы в различных местах в документе спецификации. ChatGPT не только помогает в формировании этого раздела спецификации в части преобразования нашей модели данных в формат спецификации OpenAPI, но и формировании вложенных структур данных с автоматической расстановкой ссылок на связанные объекты. Посмотрим, как это все работает на примере.
Допустим, у нас есть описание DTO тела ответа нашего API (см. рисунок 1), есть массив oList и вложенные в него массив tList, объекты docInfo и additInfo.
Декомпозируем задачу. Для начала отправим следующий промпт (запрос) в ChatGPT (просим нейросеть преобразовать в формат спецификации OpenAPI текстовое описание объекта additInfo):
Перепиши нижеприведенный текст в формате схемы описания OpenAPI. additInfo / Object (Дополнительная информация) creationTS / date-time (Дата и время формирования записи) = document / O_list / CreationDataTime currencyCode / string (Код валюты) = document / O_list / Currency_code enteredAmount/ number (Сумма в коде валюты) = document / O_list / Entered_amount operationName/ string (Наименование операции) = document / O_list / Operation_name effectiveDate / date (Дата вступления в силу учетных данных) = document / O_list / Effective_Date
Обращаю внимание, что описание объекта из Confluence я взяла как есть ― ИИ получил на вход текст без дополнительных пояснений и форматирования. Есть только требование, в какой формат требуется преобразовать текст. Плюс во входных данных присутствует лишняя информация, которая для формирования нашего YAML не требуется.
Получаем следующий ответ от ChatGPT (все ответы модели привожу без правок):
components:
schemas:
AdditInfo:
type: object
description: "Дополнительная информация"
properties:
creationTS:
type: string
format: date-time
description: "Дата и время формирования записи"
currencyCode:
type: string
description: "Код валюты"
enteredAmount:
type: number
description: "Сумма в коде валюты"
operationName:
type: string
description: "Наименование операции"
effectiveDate:
type: string
format: date
description: "Дата вступления в силу учетных данных"
required:
- creationTS
- currencyCode
- enteredAmount
- operationName
- effectiveDate
На мой взгляд, выглядит неплохо. ИИ правильно извлек из входящего текста наименования атрибутов, их тип, описание и верно составил описание в требуемом формате. Можно было бы придраться, что был добавлен блок required с обязательными атрибутами, хотя явных указаний из входящего задания на это не было, но при необходимости этот блок можно убрать или скорректировать.
Если попросить нейросеть сделать то же самое, но с приведением примеров, модель добавит их в результат, что также может быть полезно. Вот вариант с добавленными примерами для пары атрибутов:
currencyCode:
type: string
description: "Код валюты"
example: "USD"
enteredAmount:
type: number
description: "Сумма в коде валюты"
example: 12345.67
Теперь отправим аналогичный промпт для объекта docInfo. Получаем следующий ответ:
components:
schemas:
DocInfo:
type: object
description: "Данные о документах"
properties:
formCode:
type: string
description: "Тип входящего формуляра"
example: "FORM001"
docTypeCode:
type: string
nullable: true
description: "Тип первичного учетного документа"
example: "INV"
docName:
type: string
nullable: true
description: "Наименование первичного учетного документа"
example: "Invoice"
docNumber:
type: string
description: "Номер первичного учетного документа"
example: "12345-INV"
docDate:
type: string
format: date
description: "Дата первичного учетного документа"
example: "2024-03-18"
required:
- formCode
- docNumber
- docDate
Можно отметить, что модель учла мои пожелания из предыдущего запроса ― добавила примеры. Интересно, что для атрибутов, для которых в исходном DTO было указано: «если не нашли, то null», модель добавила «nullable: true» в результирующем описании схемы. Также в этот раз модель правильнее составила блок required: вместо добавления всех атрибутов добавлены только те, для которых не указано «nullable: true».
Далее аналогичным образом отправляем на обработку ИИ описание массива tList.
Получим следующий результат:
components:
schemas:
TList:
type: array
description: "Проводки"
items:
$ref: '#/components/schemas/Transaction'
Transaction:
type: object
description: "Информация о проводке"
properties:
id:
type: integer
nullable: true
description: "Идентификатор проводки"
example: 12345
account:
type: string
nullable: true
description: "Счет учета"
example: "03224"
valCode:
type: string
nullable: true
description: "Код валюты"
example: "USD"
В ответе мы видим, что модель отдельно выделила запись массива Transaction, описала для этой записи схему данных, а только потом дала описание для массива TList, где для элементов массива уже вставлены ссылки $ref на схему элементов массива. Отмечу, что каких-то дополнительных указаний в промпте для этого я не давала, исходный промпт по-прежнему выглядел как «Перепиши нижеприведенный текст в формате схемы описания OpenAPI» с указанием текста DTO.
Завершающим шагом попросим модель объединить эти результаты в итоговую схему. Отправляем промпт «Перепиши нижеприведенный текст в формате схемы описания OpenAPI. tList сделай массивом, для элементов массива сделай ссылки на схему. docInfo и additInfo – элементы oList. docInfo и additInfo сделай объектами с указанием ссылки на их схемы. Все эти схемы ты составлял в предыдущих ответах» и приводим описание DTO для массива oList (описание для docInfo и additInfo в промпт не включаем).
Получим следующий результат (описание схем для Transaction, DocInfo и AdditInfo для краткости не привожу, т.к. результат по ним совпадает с тем, что приводила ранее):
components:
schemas:
OList:
type: array
description: "Список записей"
items:
$ref: '#/components/schemas/OListItem'
OListItem:
type: object
description: "Запись учета"
properties:
exKey:
type: string
description: "Ключ логической ссылки"
example: "d3f4c5b7-1a2b-3c4d-5e6f-7g8h9i0j1k2l"
status:
type: string
nullable: true
description: "Статус записи"
enum:
- COMPLETED
- CANCELED
example: "COMPLETED"
amount:
type: number
description: "Сумма в рублях"
example: 12345.67
tList:
type: array
description: "Проводки"
items:
$ref: '#/components/schemas/Transaction'
docInfo:
$ref: '#/components/schemas/DocInfo'
additInfo:
$ref: '#/components/schemas/AdditInfo'
Модель правильно сформировала схему, расставила ссылки на описания отдельных элементов, как это и требовалось. Также были переиспользованы результаты предыдущих запросов. Из них были извлечены описания для Transaction, DocInfo и AdditInfo. Для атрибута «status», где в исходном DTO было определено множество возможных значений, модель добавила в описание enum.
Если попросить модель привести описание блока paths: с результатом oList, получим следующий результат.
paths:
/o-list:
get:
summary: Получение списка записей учета
description: Возвращает список записей учета (oList) с детализацией проводок, документа и дополнительной информации.
operationId: getOList
tags:
- Records
responses:
'200':
description: Успешный ответ, содержащий список записей учета.
content:
application/json:
schema:
$ref: '#/components/schemas/OList'
'400':
description: Неверный запрос.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Описание ошибки
example: "Некорректный формат запроса"
'500':
description: Внутренняя ошибка сервера.
content:
application/json:
schema:
type: object
properties:
error:
type: string
description: Описание ошибки
example: "Внутренняя ошибка сервера"
Таким образом, мы получили черновик нашего YAML. Понятно, что это только набросок, но модель сделала за нас большую часть рутинной работы, и нам потребуется немного времени, чтобы довести это описание до финального состояния.
Допустим, у нас модель данных описывается иначе, например, тело ответа нашего API описано в виде таблицы:

Если отправить в ChatGPT промпт: «Вот таблица. Перепиши текст в формате схемы описания OpenAPI» и вставить таблицу (простым копированием без каких-либо дополнительных пояснений). Получаем такой ответ от модели:
components:
schemas:
EntityRecord:
type: object
description: Запись сущности с уникальным идентификатором, ссылками на версии и метаинформацией.
properties:
gid:
type: string
format: uuid
description: Уникальный идентификатор записи.
example: "123e4567-e89b-12d3-a456-426614174000"
ex_key:
type: string
format: uuid
description: Ключ логической ссылки на запись сущности.
example: "123e4567-e89b-12d3-a456-426614174001"
ex_version_key:
type: string
format: uuid
nullable: true
description: Ключ логической ссылки на группу набора версий сущности.
example: "123e4567-e89b-12d3-a456-426614174002"
entity_version:
type: string
maxLength: 63
nullable: true
description: Версия сущности.
example: "v1.0.0"
created_ts:
type: string
format: date-time
description: Дата и время создания записи (служебное поле).
example: "2024-11-12T14:37:00Z"
updated_ts:
type: string
format: date-time
description: Дата и время изменения записи (служебное поле).
example: "2024-11-12T15:00:00Z"
required:
- gid
- ex_key
- created_ts
- updated_ts
Дальнейшие эксперименты с передачей нейросети на вход данных в виде табличного описания я не проводила. Но, судя по результатам, мы получили неплохой результат.
Выводы
-
ChatGPT может значительно сократить трудозатраты системного аналитика по формированию спецификации OpenAPI, взяв на себя наиболее рутинную часть задачи.
-
ChatGPT максимально полезен для задач преобразования текста из одного формата в другой по определенным правилам. Особенно когда речь идет про преобразование в/из формата, который хорошо знаком ChatGPT. Абсолютно согласна с Юрием Куприяновым: «Вообще выглядит так, что нейросеть сильно облегчает именно часть работы по складыванию букв в слова и слов в фразы, а вот образ результата вам нужно заранее себе представлять хоть в каком-то виде (или в детальном, смотря насколько в детали вы собираетесь уходить)».
-
Наилучший результат от нейросети удается получить, если декомпозировать исходную задачу и давать языковой модели задания последовательно, начиная с простых и постепенно усложняя задачу. Если сразу дать в одном промпте сложную большую задачу, ChatGPT начинает отлынивать, может прервать ответ и написать что-то из серии «далее аналогично», пропускать строки и т.п.
-
Нужно обязательно проверять результат работы ChatGPT. Относимся к нему как к младшему, достаточно смышленому специалисту, которому можно поручить рутинную часть задач. За ним проверяем? Конечно! Результат он выдает приемлемый, очень похожий на правду, но, вполне возможно, потребуется немного ваших правок. В любом случае времени на эти правки вы потратите значительно меньше, чем если бы сами делали эту задачу самостоятельно. Плюс большая часть рутины будет не на ваших плечах.
Порой слышу рассуждения о том, заменит ли нас ИИ. Я согласна с Теренсом Тао, математиком, обладателем Филдсовской премии, что ИИ стоит рассматривать не как конкурента, а как помощника, так как «у людей и машин очень разные сильные стороны, которые следует рассматривать как дополняющие друг друга, а не конкурирующие», и «лучше рассматривать ИИ как дополнительный способ выполнения задач». ИИ может быть хорошим помощником и взять часть рутины на себя, чтобы у нас было больше времени заниматься тем, что мы любим – интересными задачами и творчеством. Я верю, что со временем ИИ не заменит нас, а позволит выйти на новый уровень, решать более сложные и масштабные задачи.
Всем добра.
*Статья написана в рамках ХабраЧелленджа 3.0, который прошел в ЛАНИТ осенью 2024 года. О том, что такое ХабраЧеллендж, читайте здесь.
Автор: ivanna_natalia