В этом гайде я покажу, как интегрировать возможности YandexGPT (в частности, генерацию текста и картинок) в ваш ASP.NET Core проект.

Получение доступа к YandexGPT API

  1. Перейдите в Консоль управления Яндекс Облака

  2. Создайте или выберите каталог

  3. Перейдите в раздел Сервисные аккаунты

  4. Создайте новый сервисный аккаунт

  5. Назначьте ему роль ai.languageModels.user для генерации текста и ai.imageGeneration.user для генерации Изображений

  6. Создайте API ключ для этого аккаунта

  7. Сохраните полученный ключ

Создание и написание проекта

Создаем консольное приложение .NET Core(я писал его на .NET 10)

добавим в проект класс который будет отвечать за генерацию текста MyGPTClient:

internal class MyGPTClient
{
    private readonly string _apiKey;
    private readonly HttpClient _client;
    private readonly string _textModelUri;
    private readonly string _imageModelUri;

    public MyGPTClient(string apiKey, string textModelUri, string imageModelUri = null)
    {
        _apiKey = apiKey;
        _client = new HttpClient();
        _client.DefaultRequestHeaders.Add("Authorization", $"Api-Key {apiKey}");
        _textModelUri = textModelUri;
        _imageModelUri = imageModelUri;
    }

Метод GenerateArticleAsync генерация статьи:

 public async Task<string> GenerateArticleAsync(string title)
 {
     var request = new
     {
         modelUri = _textModelUri,
         completionOptions = new
         {
             stream = false,
             temperature = 0.6,
             maxTokens = 2000
         },
         messages = new[]
         {
             new { role = "system", text = "Ты — умный ассистент, который пишет SEO-оптимизированные статьи в формате Markdown." },
             new { role = "user", text = $"Напиши развернутую статью на тему: {title}." }
         }
     };

     var jsonRequest = JsonConvert.SerializeObject(request);
     var response = await _client.PostAsync("https://llm.api.cloud.yandex.net/foundationModels/v1/completionAsync",
         new StringContent(jsonRequest, System.Text.Encoding.UTF8, "application/json"));

     response.EnsureSuccessStatusCode();
     var responseBody = await response.Content.ReadAsStringAsync();
     var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

     if (result == null || result.id == null)
     {
         throw new Exception("Не удалось получить идентификатор задачи");
     }

     string taskId = result.id;
     var taskResult = await WaitForTaskCompletionAsync(taskId);

     if (taskResult != null && taskResult.alternatives != null && taskResult.alternatives.Count > 0)
     {
         return taskResult.alternatives[0].message.text;
     }
     else
     {
         throw new Exception("Не удалось получить ответ от API");
     }
 }

код основан на Документация AI Studio

По умолчанию модель возвращает ответ, отформатированный с помощью разметки Markdown.

в role = "system" задаем роль для ИИ

в role = "user" задаём тему статьи

Я столкнулся с проблемой когда проект не усп��вает получить данные с API поэтому написал вспомогательный метод WaitForTaskCompletionAsync для ожидания завершения задачи его я также использую при генерации изображения

private async Task<dynamic> WaitForTaskCompletionAsync(string taskId)
{
    while (true)
    {
        var response = await _client.GetAsync($"https://llm.api.cloud.yandex.net/operations/{taskId}");
        response.EnsureSuccessStatusCode();

        var responseBody = await response.Content.ReadAsStringAsync();
        var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

        if (result == null)
        {
            throw new Exception("Не удалось получить статус задачи");
        }

        if (result.done == true)
        {
            return result.response;
        }

        await Task.Delay(1000);
    }
}

по умолчанию текст статьи содержит в себе лишние символы типа "****", "////" и другие, убираем все лишнее:

public static string ConvertToHtml(string input)
{
    string result = TextEdit(input);
    // Заменяем переносы строк на <br>
    string html = result.Replace("\n", "<br>");

    // Заменяем **текст** на <strong>текст</strong>
    html = System.Text.RegularExpressions.Regex.Replace(
        html,
        @"\*\*(.*?)\*\*",
        "<strong>$1</strong>"
    );

    // Заменяем *текст* на <em>текст</em> (курсив)
    html = System.Text.RegularExpressions.Regex.Replace(
        html,
        @"\*(.*?)\*",
        "<em>$1</em>"
    );
    return html;
}
private static string TextEdit(string input)
{
    var lines = input.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None)
        .SkipWhile(line => string.IsNullOrWhiteSpace(line) || line.TrimStart().StartsWith("#"))
        .Select(line =>
        {
            var trimmed = line.TrimStart();
            return trimmed.StartsWith("#")
                ? $"<strong>{trimmed.TrimStart('#').TrimStart()}</strong>"
                : line;
        });

    return string.Join(Environment.NewLine, lines);
}

таким образом мы получили чистый текст без лишних символов и без темы статьи она нам тут не нужна так как мы знали её заранее передавая тему статьи в метод GenerateArticleAsync.

Далее нам нужно сгенерировать изображение создаём метод GenerateImageAsync:

 public async Task<string> GenerateImageAsync(string prompt)
 {
     if (string.IsNullOrEmpty(_imageModelUri))
         throw new InvalidOperationException("URI модели изображения не настроен");

     var request = new
     {
         modelUri = _imageModelUri,
         generationOptions = new
         {
             seed = new Random().Next(1, 10000),
             aspectRatio = new
             {
                 widthRatio = "2",
                 heightRatio = "1"
             }
         },
         messages = new[]
         {
             new
             {
                 weight = "1",
                 text = prompt
             }
         }
     };

     var response = await _client.PostAsync(
         "https://llm.api.cloud.yandex.net/foundationModels/v1/imageGenerationAsync",
         new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json")
     );

     response.EnsureSuccessStatusCode();
     var responseBody = await response.Content.ReadAsStringAsync();
     var result = JsonConvert.DeserializeObject<dynamic>(responseBody);

     if (result?.id == null)
         throw new Exception("Не удалось получить идентификатор задачи");

     var taskResult = await WaitForTaskCompletionAsync(result.id.ToString());

     if (taskResult?.image == null)
         throw new Exception("Нет данных изображения в ответе");

     var fileName = $"{Guid.NewGuid()}.jpg";
     var directoryPath = Path.Combine(Directory.GetCurrentDirectory(), "Resources", "ArticleImg");
     var fullPath = Path.Combine(directoryPath, fileName);

     Directory.CreateDirectory(directoryPath);

     try
     {
         var base64String = taskResult.image.ToString();
         if (base64String.StartsWith("data:image/jpeg;base64,"))
         {
             base64String = base64String.Substring("data:image/jpeg;base64,".Length);
         }

         if (!IsValidBase64String(base64String))
             throw new Exception("Некорректные данные base64");

         var imageBytes = Convert.FromBase64String(base64String);
         await File.WriteAllBytesAsync(fullPath, imageBytes);
     }
     catch (FormatException ex)
     {
         throw new Exception($"Ошибка при декодировании base64: {ex.Message}");
     }
     catch (Exception ex)
     {
         throw new Exception($"Ошибка при обработке изображения: {ex.Message}");
     }

     return fileName;
 }

в данном методе я задаю имя изображения гуидом и сохраняю в папку Resources/ArticleImg

Вспомогательный метод для проверки base64

 private bool IsValidBase64String(string base64String)
 {
     try
     {
         Convert.FromBase64String(base64String);
         return true;
     }
     catch (FormatException)
     {
         return false;
     }
     catch (ArgumentNullException)
     {
         return false;
     }
 }

если вам нужно генерировать темы для статей то вот мой вариант как это делаю я:

public static async Task<(string result, string articleType)> CreateRandomArticleTopic()
{
    string[] articleTypes = {
        "Технологии и IT",
        "Наука",
        "Экономика",
        "Гейминг",
        "Обучение",
        "Новости"
    };

    var apiKey = _apiKey;
    var modelUri = _textModelUri;
    var client = new MyGPTClient(apiKey, modelUri);

    Random rnd = new Random();
    string randomArticleType = articleTypes[rnd.Next(0, articleTypes.Length)];

    try
    {
        return await client.GenerateArticleTopicAsync(randomArticleType);
    }
    catch (HttpRequestException ex)
    {
        throw new Exception($"Ошибка при обращении к API: {ex.Message}");
    }
    catch (Exception ex)
    {
        throw new Exception($"Произошла ошибка: {ex.Message}");
    }
}

Ну вот и всё осталось это вызвать в вашем Program.cs

 var apiKey = "{{API ключ}}";
 var textModelUri = "gpt://{{тут id каталога}}/yandexgpt";
 var imageModelUri = "art://{{тут id каталога}}/yandex-art/latest"; // Если есть доступ
 var gptClient = new MyGPTClient(apiKey, textModelUri, imageModelUri);


 //генерируем 5 тем для статей
 var articles = new (string title, string articleType)[5];
 for (int i = 0; i < 5; i++)
 {
     var (title, articleType) = await rrrrGPTClient.CreateRandomArticleTopic();
     articles[i] = (title, articleType);
 }

  for (int i = 0; i < 5; i++) 
  {
     //генерация статьи
     var articleContent = await gptClient.GenerateArticleAsync(articles[i].title);
     //генерация изображения
     var imageUrl = await gptClient.GenerateImageAsync(articles[i].title);
  }

Спасибо за внимание!