Как подружить C++ с нейросетями: пишем клиент для ChatGPT API

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему C++ и ChatGPT — это не странно, а мощно

Когда речь заходит об интеграции нейросетей, все почему-то сразу вспоминают Python. Да, у питонистов есть элегантные библиотеки вроде openai и requests. Но что делать, если ваш проект — высоконагруженный сервер на C++, десктопное приложение или игровой движок? Не переписывать же всё на Python?

Хорошая новость: интеграция с ChatGPT API на C++ — это не магия, а вполне решаемая инженерная задача. Вы получаете скорость нативного кода, контроль над памятью и возможность встраивать AI-функции прямо в ядро вашего приложения. Никаких прослоек на Python или Node.js — только чистый C++ и прямая работа с API.

В этой статье мы разберём, как написать клиент для OpenAI API на C++ с нуля. Вы узнаете, как отправлять HTTP-запросы, обрабатывать JSON и получать ответы от ChatGPT. Поехали!

Что нам понадобится: инструментарий

Для работы с ChatGPT API на C++ нам нужно решить две базовые задачи:

  • Отправлять HTTP-запросы (желательно с поддержкой HTTPS)
  • Парсить и формировать JSON

Мы будем использовать две проверенные библиотеки:

  • libcurl — де-факто стандарт для HTTP в C/C++. Есть в любом пакетном менеджере.
  • nlohmann/json — современная, удобная JSON-библиотека. Просто подключите заголовочный файл.

Также нам понадобится API-ключ от OpenAI. Если у вас его ещё нет — зайдите на platform.openai.com, зарегистрируйтесь и создайте ключ в разделе API keys. Храните его в безопасности, лучше всего — в переменной окружения.

Архитектура простого клиента

Наш клиент будет делать одну вещь: отправлять сообщение пользователя в ChatGPT и получать ответ. Всё общение с API происходит через эндпоинт https://api.openai.com/v1/chat/completions методом POST.

Структура будет такой:

  1. Формируем JSON с параметрами запроса (модель, сообщения, температура)
  2. Отправляем POST-запрос через libcurl
  3. Получаем JSON-ответ и парсим его
  4. Извлекаем текст ответа ассистента

Звучит просто? Давайте писать код.

Шаг 1: Подготовка JSON-запроса

Начнём с функции, которая формирует тело запроса. Мы будем использовать библиотеку nlohmann/json. Установить её проще простого: скачайте json.hpp с GitHub и положите в папку с проектом.

#include <string>
#include "json.hpp"

using json = nlohmann::json;

std::string build_chat_request(const std::string& user_message) {
    json request;
    request["model"] = "gpt-3.5-turbo";

    json messages = json::array();
    json user_msg;
    user_msg["role"] = "user";
    user_msg["content"] = user_message;
    messages.push_back(user_msg);

    request["messages"] = messages;
    request["temperature"] = 0.7;
    request["max_tokens"] = 1024;

    return request.dump();
}

Обратите внимание: мы используем модель gpt-3.5-turbo — это оптимальный баланс между качеством и скоростью. Если вам нужно что-то помощнее, можно указать gpt-4, но это дороже.

Шаг 2: Отправка HTTP-запроса с libcurl

Теперь самое интересное — работа с C++ HTTP запросами. libcurl требует некоторой инициализации, но в целом всё прямолинейно.

Напишем функцию, которая принимает тело запроса и API-ключ, а возвращает сырой ответ от сервера.

#include <curl/curl.h>
#include <string>

size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* output) {
    size_t total_size = size * nmemb;
    output->append((char*)contents, total_size);
    return total_size;
}

std::string send_chat_request(const std::string& json_body, const std::string& api_key) {
    CURL* curl = curl_easy_init();
    std::string response;

    if (curl) {
        struct curl_slist* headers = nullptr;
        headers = curl_slist_append(headers, "Content-Type: application/json");

        std::string auth_header = "Authorization: Bearer " + api_key;
        headers = curl_slist_append(headers, auth_header.c_str());

        curl_easy_setopt(curl, CURLOPT_URL, "https://api.openai.com/v1/chat/completions");
        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_body.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);

        // Важно для работы с HTTPS
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);

        CURLcode res = curl_easy_perform(curl);

        if (res != CURLE_OK) {
            std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
        }

        curl_easy_cleanup(curl);
        curl_slist_free_all(headers);
    }

    return response;
}

Разберём ключевые моменты:

  • WriteCallback — это функция обратного вызова, которая накапливает данные ответа в строку. libcurl вызывает её по мере получения данных.
  • Мы передаём API-ключ в заголовке Authorization: Bearer.
  • Обязательно включаем проверку SSL-сертификатов, чтобы не попасть под MITM-атаку.

Шаг 3: Парсинг ответа и извлечение текста

Когда мы получили JSON-ответ от сервера, нужно извлечь из него текст сообщения ассистента. Ответ имеет такую структуру:

{
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "Привет! Чем могу помочь?"
      }
    }
  ]
}

Функция для парсинга:

std::string extract_assistant_reply(const std::string& raw_response) {
    try {
        json response_json = json::parse(raw_response);

        if (response_json.contains("choices") && !response_json["choices"].empty()) {
            return response_json["choices"][0]["message"]["content"];
        } else {
            return "Ошибка: пустой ответ от API";
        }
    } catch (const std::exception& e) {
        return "Ошибка парсинга JSON: " + std::string(e.what());
    }
}

Обратите внимание на обработку ошибок. API может вернуть сообщение об ошибке, превышении лимита или некорректном запросе. В production-коде стоит добавить более детальную проверку.

Блоги

Рекомендаций по книгам