Генерация контента с помощью моделей искусственного интеллекта

В основе генеративного ИИ лежат модели ИИ. Двумя наиболее яркими примерами генеративных моделей являются модели большого языка (LLM) и модели генерации изображений. Эти модели принимают входные данные, называемые подсказками (чаще всего текст, изображение или их комбинацию), и на выходе выдают текст, изображение или даже аудио или видео.

Результаты этих моделей могут быть на удивление убедительными: LLM генерируют текст, который выглядит так, как если бы он был написан человеком, а модели генерации изображений могут создавать изображения, очень близкие к реальным фотографиям или произведениям искусства, созданным людьми.

Кроме того, LLM доказали свою способность решать задачи, выходящие за рамки простой генерации текста:

  • Написание компьютерных программ.
  • Планирование подзадач, необходимых для выполнения более крупной задачи.
  • Организация неорганизованных данных.
  • Понимание и извлечение информационных данных из корпуса текста.
  • Отслеживание и выполнение автоматизированных действий на основе текстового описания действия.

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

Как разработчик приложений, вы обычно не взаимодействуете с генеративными моделями ИИ напрямую, а скорее через сервисы, доступные в виде веб-API. Хотя эти службы часто имеют схожие функциональные возможности, все они предоставляют их через разные и несовместимые API. Если вы хотите использовать несколько сервисов моделей, вам придется использовать каждый из их собственных SDK, потенциально несовместимых друг с другом. И если вы хотите перейти с одной модели на новейшую и наиболее функциональную, вам, возможно, придется создавать эту интеграцию заново.

Genkit решает эту проблему, предоставляя единый интерфейс, который абстрагирует детали доступа к потенциально любому сервису генеративной модели ИИ, при этом уже доступно несколько готовых реализаций. Создание приложения на основе искусственного интеллекта на основе Genkit упрощает процесс первого вызова генеративного искусственного интеллекта и делает одинаково простым объединение нескольких моделей или замену одной модели на другую по мере появления новых моделей.

Прежде чем начать

Если вы хотите запустить примеры кода на этой странице, сначала выполните действия, описанные в руководстве по началу работы . Во всех примерах предполагается, что вы уже установили Genkit в качестве зависимости в своем проекте.

Модели, поддерживаемые Genkit

Genkit спроектирован так, чтобы быть достаточно гибким, чтобы потенциально использовать любой сервис генеративных моделей искусственного интеллекта. Его основные библиотеки определяют общий интерфейс для работы с моделями, а плагины моделей определяют детали реализации для работы с конкретной моделью и ее API.

Команда Genkit поддерживает плагины для работы с моделями, предоставляемыми Vertex AI, Google Generative AI и Ollama:

Загрузка и настройка плагинов модели

Прежде чем вы сможете использовать Genkit для создания контента, вам необходимо загрузить и настроить плагин модели. Если вы приступили к работе с руководством по началу работы, вы уже это сделали. В противном случае ознакомьтесь с руководством по началу работы или документацией отдельного плагина и следуйте инструкциям, прежде чем продолжить.

Функция genkit.Generate()

В Genkit основным интерфейсом, через который вы взаимодействуете с генеративными моделями ИИ, является функция genkit.Generate() .

Самый простой вызов genkit.Generate() указывает модель, которую вы хотите использовать, и текстовое приглашение:

import (
    "context"
    "log"

    "github.com/firebase/genkit/go/ai"
    "github.com/firebase/genkit/go/genkit"
    "github.com/firebase/genkit/go/plugins/googlegenai"
)

func main() {
    ctx := context.Background()

    g, err := genkit.Init(ctx,
        genkit.WithPlugins(&googlegenai.GoogleAI{}),
        genkit.WithDefaultModel("googleai/gemini-2.0-flash"),
    )
    if err != nil {
        log.Fatal(err)
    }

    resp, err := genkit.Generate(ctx, g,
        ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    )
    if err != nil {
        log.Fatal(err)
    }

    log.Println(resp.Text())
}

Когда вы запустите этот краткий пример, он распечатает некоторую отладочную информацию, за которой последует вывод вызова genkit.Generate() , который обычно представляет собой текст Markdown, как в следующем примере:

## The Blackheart's Bounty

**A hearty stew of slow-cooked beef, spiced with rum and molasses, served in a
hollowed-out cannonball with a side of crusty bread and a dollop of tangy
pineapple salsa.**

**Description:** This dish is a tribute to the hearty meals enjoyed by pirates
on the high seas. The beef is tender and flavorful, infused with the warm spices
of rum and molasses. The pineapple salsa adds a touch of sweetness and acidity,
balancing the richness of the stew. The cannonball serving vessel adds a fun and
thematic touch, making this dish a perfect choice for any pirate-themed
adventure.

Запустите сценарий еще раз, и вы получите другой результат.

В предыдущем примере кода запрос на создание был отправлен в модель по умолчанию, которую вы указали при настройке экземпляра Genkit.

Вы также можете указать модель для одного вызова genkit.Generate() :

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.5-pro"),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)

Идентификатор строки модели выглядит как providerid/modelid , где идентификатор поставщика (в данном случае googleai ) идентифицирует плагин, а идентификатор модели — это строковый идентификатор, специфичный для плагина, для конкретной версии модели.

Эти примеры также иллюстрируют важный момент: когда вы используете genkit.Generate() для вызова генеративной модели ИИ, изменение модели, которую вы хотите использовать, — это вопрос передачи другого значения в параметр модели. Используя genkit.Generate() вместо собственных SDK моделей, вы получаете возможность более легко использовать несколько различных моделей в своем приложении и изменять модели в будущем.

До сих пор вы видели только примеры простейших вызовов genkit.Generate() . Однако genkit.Generate() также предоставляет интерфейс для более сложного взаимодействия с генеративными моделями, который вы увидите в следующих разделах.

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

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

Если используемая вами модель поддерживает системные подсказки, вы можете предоставить ее с помощью опции WithSystem() :

resp, err := genkit.Generate(ctx, g,
    ai.WithSystem("You are a food industry marketing consultant."),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)

Для моделей, которые не поддерживают системные приглашения, WithSystem() имитирует их, изменяя запрос так, чтобы он выглядел как системное приглашение.

Параметры модели

Функция genkit.Generate() принимает опцию WithConfig() , с помощью которой вы можете указать дополнительные настройки, управляющие тем, как модель генерирует контент:

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    ai.WithConfig(&googlegenai.GeminiConfig{
        MaxOutputTokens: 500,
        StopSequences:   ["<end>", "<fin>"],
        Temperature:     0.5,
        TopP:            0.4,
        TopK:            50,
    }),
)

Точные поддерживаемые параметры зависят от конкретной модели и API модели. Однако параметры в предыдущем примере являются общими практически для каждой модели. Ниже приводится объяснение этих параметров:

Параметры, управляющие длиной вывода

Максаутпуттокенс

LLM работают с единицами, называемыми токенами . Токен обычно (но не обязательно) соответствует определенной последовательности символов. Когда вы передаете приглашение модели, одним из первых шагов является преобразование строки приглашения в последовательность токенов. Затем LLM генерирует последовательность токенов из токенизированных входных данных. Наконец, последовательность токенов преобразуется обратно в текст, который и является вашим результатом.

Параметр максимального количества токенов устанавливает ограничение на количество токенов, которые можно сгенерировать с помощью LLM. В каждой модели потенциально используется свой токенизатор, но хорошее практическое правило — считать, что одно английское слово состоит из 2–4 токенов.

Как говорилось ранее, некоторые токены могут не соответствовать последовательностям символов. Одним из таких примеров является то, что часто существует токен, указывающий конец последовательности: когда LLM генерирует этот токен, он перестает генерировать новые. Следовательно, возможно и часто бывает так, что LLM генерирует меньше токенов, чем максимальное, потому что он сгенерировал «стоповый» токен.

Стоп-последовательности

Этот параметр можно использовать для установки токенов или последовательностей токенов, которые при создании указывают на конец вывода LLM. Правильные значения, которые следует использовать здесь, обычно зависят от того, как была обучена модель, и обычно устанавливаются плагином модели. Однако если вы предложили модели создать другую последовательность остановок, вы можете указать ее здесь.

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

Параметры, управляющие «креативностью»

Параметры температуры , top-p и top-k вместе определяют, насколько «креативной» должна быть модель. В этом разделе представлены очень краткие объяснения того, что означают эти параметры, но более важным моментом является то, что эти параметры используются для настройки характера выходного сигнала LLM. Оптимальные значения для них зависят от ваших целей и предпочтений и, скорее всего, будут найдены только экспериментальным путем.

Температура

LLM — это, по сути, машины прогнозирования токенов. Для заданной последовательности токенов (например, приглашения) LLM прогнозирует для каждого токена в своем словаре вероятность того, что этот токен будет следующим в последовательности. Температура — это коэффициент масштабирования, на который делятся эти прогнозы перед нормализацией на вероятность от 0 до 1.

Низкие значения температуры — от 0,0 до 1,0 — усиливают разницу в вероятности между токенами, в результате чего модель с еще меньшей вероятностью создаст токен, который она уже оценила как маловероятный. Это часто воспринимается как менее творческий результат. Хотя 0,0 технически не является допустимым значением, многие модели рассматривают его как указание на то, что модель должна вести себя детерминировано и учитывать только один наиболее вероятный токен.

Значения высокой температуры — те, которые больше 1,0 — сжимают различия в вероятности между токенами, в результате чего модель с большей вероятностью будет производить токены, которые она ранее считала маловероятными. Это часто воспринимается как более творческий результат. API некоторых моделей устанавливают максимальную температуру, часто 2,0.

ТопП

Top-p — это значение от 0,0 до 1,0, которое контролирует количество возможных токенов, которые должна учитывать модель, путем указания совокупной вероятности токенов. Например, значение 1,0 означает, что нужно учитывать все возможные токены (но при этом учитывать вероятность каждого токена). Значение 0,4 означает, что следует учитывать только наиболее вероятные токены, сумма вероятностей которых равна 0,4, и исключать из рассмотрения остальные токены.

ТопК

Top-k — это целочисленное значение, которое также контролирует количество возможных токенов, которые должна учитывать модель, но на этот раз путем явного указания максимального количества токенов. Указание значения 1 означает, что модель должна вести себя детерминировано.

Экспериментируйте с параметрами модели

Вы можете поэкспериментировать с влиянием этих параметров на выходные данные, генерируемые различными комбинациями моделей и подсказок, с помощью пользовательского интерфейса разработчика. Запустите пользовательский интерфейс разработчика с помощью команды genkit start , и он автоматически загрузит все модели, определенные плагинами, настроенными в вашем проекте. Вы можете быстро попробовать различные запросы и значения конфигурации без необходимости многократного внесения этих изменений в код.

Сопряжение модели с ее конфигурацией

Учитывая, что каждый поставщик или даже конкретная модель может иметь свою собственную схему конфигурации или требовать определенных настроек, установка отдельных параметров с помощью WithModelName() и WithConfig() может привести к ошибкам, поскольку последний не является строго типизированным для первого.

Чтобы связать модель с ее конфигурацией, вы можете создать ссылку на модель, которую вместо этого можно передать в вызов генерации:

model := googlegenai.GoogleAIModelRef("gemini-2.0-flash", &googlegenai.GeminiConfig{
    MaxOutputTokens: 500,
    StopSequences:   ["<end>", "<fin>"],
    Temperature:     0.5,
    TopP:            0.4,
    TopK:            50,
})

resp, err := genkit.Generate(ctx, g,
    ai.WithModel(model),
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)
if err != nil {
    log.Fatal(err)
}

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

Структурированный вывод

При использовании генеративного ИИ в качестве компонента вашего приложения часто требуется вывод в формате, отличном от обычного текста. Даже если вы просто создаете контент для отображения пользователю, вы можете извлечь выгоду из структурированного вывода просто с целью представить его более привлекательно для пользователя. Но для более продвинутых приложений генеративного ИИ, таких как программное использование результатов модели или передача результатов одной модели в другую, структурированный вывод просто необходим.

В Genkit вы можете запросить структурированный вывод из модели, указав тип вывода при вызове genkit.Generate() :

type MenuItem struct {
    Name        string   `json:"name"`
    Description string   `json:"description"`
    Calories    int      `json:"calories"`
    Allergens   []string `json:"allergens"`
}

resp, err := genkit.Generate(ctx, g,
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
    ai.WithOutputType(MenuItem{}),
)
if err != nil {
  log.Fatal(err) // One possible error is that the response does not conform to the type.
}

Типы выходных данных модели указываются в виде схемы JSON с использованием пакета invopop/jsonschema . Это обеспечивает проверку типов во время выполнения, которая устраняет разрыв между статическими типами Go и непредсказуемыми результатами генеративных моделей ИИ. Эта система позволяет вам писать код, который может полагаться на тот факт, что успешный вызов генерации всегда будет возвращать выходные данные, соответствующие вашим типам Go.

Когда вы указываете тип вывода в genkit.Generate() , Genkit незаметно делает несколько вещей:

  • Дополняет подсказку дополнительными указаниями по выбранному выходному формату. Это также имеет побочный эффект: вы указываете модели, какой именно контент вы хотите сгенерировать (например, не только предложить пункт меню, но и сгенерировать описание, список аллергенов и т. д.).
  • Проверяет соответствие вывода схеме.
  • Маршалирует выходные данные модели в тип Go.

Чтобы получить структурированный вывод в результате успешного вызова генерации, вызовите Output() для ответа модели с пустым значением типа:

var item MenuItem
if err := resp.Output(&item); err != nil {
    log.Fatalf(err)
}

log.Printf("%s (%d calories, %d allergens): %s\n",
    item.Name, item.Calories, len(item.Allergens), item.Description)

Альтернативно вы можете использовать genkit.GenerateData() для более краткого вызова:

item, resp, err := genkit.GenerateData[MenuItem](ctx, g,
    ai.WithPrompt("Invent a menu item for a pirate themed restaurant."),
)
if err != nil {
  log.Fatal(err)
}

log.Printf("%s (%d calories, %d allergens): %s\n",
    item.Name, item.Calories, len(item.Allergens), item.Description)

Эта функция требует параметра типа вывода, но автоматически устанавливает параметр WithOutputType() и вызывает resp.Output() перед возвратом значения.

Обработка ошибок

Обратите внимание, что в предыдущем примере вызов genkit.Generate() может привести к ошибке. Одна из возможных ошибок может произойти, когда модель не может генерировать выходные данные, соответствующие схеме. Лучшая стратегия борьбы с такими ошибками будет зависеть от вашего конкретного варианта использования, но вот несколько общих советов:

  • Попробуйте другую модель . Для успешного структурированного вывода модель должна иметь возможность генерировать выходные данные в формате JSON. Самые мощные программы LLM, такие как Gemini, достаточно универсальны, чтобы сделать это; однако модели меньшего размера, такие как некоторые из локальных моделей, которые вы будете использовать с Ollama, возможно, не смогут надежно генерировать структурированные выходные данные, если они не были специально обучены этому.

  • Упростите схему . У LLM могут возникнуть проблемы с созданием сложных или глубоко вложенных типов. Попробуйте использовать понятные имена, меньшее количество полей или плоскую структуру, если вы не можете надежно генерировать структурированные данные.

  • Повторите вызов genkit.Generate() . Если выбранная вами модель лишь в редких случаях не генерирует соответствующий вывод, вы можете обработать ошибку так же, как если бы вы обрабатывали сетевую ошибку, и повторить запрос, используя какую-то стратегию постепенной отсрочки.

Потоковое вещание

При создании больших объемов текста вы можете улучшить взаимодействие с пользователями, представляя выходные данные в том виде, в котором они сгенерированы, — потоковую передачу результатов. Знакомый пример потоковой передачи в действии можно увидеть в большинстве чат-приложений LLM: пользователи могут читать ответ модели на свое сообщение по мере его создания, что улучшает восприятие реакции приложения и усиливает иллюзию общения с умным собеседником.

В Genkit вы можете осуществлять потоковую передачу вывода, используя опцию WithStreaming() :

resp, err := genkit.Generate(ctx, g,
    ai.WithPrompt("Suggest a complete menu for a pirate themed restaurant."),
    ai.WithStreaming(func(ctx context.Context, chunk *ai.ModelResponseChunk) error {
        // Do something with the chunk...
        log.Println(chunk.Text())
        return nil
    }),
)
if err != nil {
    log.Fatal(err)
}

log.Println(resp.Text())

Мультимодальный ввод

В примерах, которые вы видели до сих пор, в качестве подсказок модели использовались текстовые строки. Хотя это остается наиболее распространенным способом подсказки генеративным моделям ИИ, многие модели также могут принимать в качестве подсказок другие носители. Подсказки мультимедиа чаще всего используются вместе с текстовыми подсказками, которые инструктируют модель выполнить некоторые операции с мультимедиа, например добавить подпись к изображению или расшифровать аудиозапись.

Возможность принимать медиа-вход и типы мультимедиа, которые вы можете использовать, полностью зависят от модели и ее API. Например, модели серии Gemini 2.0 могут принимать изображения, видео и аудио в качестве подсказок.

Чтобы предоставить мультимедийное приглашение модели, которая его поддерживает, вместо передачи простого текстового приглашения в genkit.Generate() передайте массив, состоящий из медиа-части и текстовой части. В этом примере указывается изображение с использованием общедоступного URL-адреса HTTPS.

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithMessages(
        NewUserMessage(
            NewMediaPart("image/jpeg", "https://example.com/photo.jpg"),
            NewTextPart("Compose a poem about this image."),
        ),
    ),
)

Вы также можете передавать медиаданные напрямую, закодировав их как URL-адрес данных. Например:

image, err := ioutil.ReadFile("photo.jpg")
if err != nil {
    log.Fatal(err)
}

resp, err := genkit.Generate(ctx, g,
    ai.WithModelName("googleai/gemini-2.0-flash"),
    ai.WithMessages(
        NewUserMessage(
            NewMediaPart("image/jpeg", "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(image)),
            NewTextPart("Compose a poem about this image."),
        ),
    ),
)

Все модели, поддерживающие ввод мультимедиа, поддерживают как URL-адреса данных, так и URL-адреса HTTPS. Некоторые плагины модели добавляют поддержку других источников мультимедиа. Например, плагин Vertex AI также позволяет использовать URL-адреса облачного хранилища ( gs:// ).

Следующие шаги

Узнайте больше о Генките

  • Для разработчика приложений основной способ повлиять на результаты генеративных моделей ИИ — это подсказки. Прочтите «Управление подсказками с помощью Dotprompt», чтобы узнать, как Genkit помогает разрабатывать эффективные подсказки и управлять ими в вашей базе кода.
  • Хотя genkit.Generate() является ядром каждого приложения, работающего на основе генеративного ИИ, реальные приложения обычно требуют дополнительной работы до и после вызова генеративной модели ИИ. Чтобы отразить это, Genkit вводит концепцию потоков , которые определяются как функции, но добавляют дополнительные функции, такие как наблюдаемость и упрощенное развертывание. Дополнительные сведения см. в разделе Определение рабочих процессов ИИ .

Продвинутое использование LLM

Существуют методы, которые ваше приложение может использовать, чтобы получить еще большую выгоду от программ LLM.

  • Один из способов расширить возможности LLM — предложить им список способов, которыми они могут запросить у вас дополнительную информацию или попросить вас выполнить какое-либо действие. Это известно как вызов инструмента или вызов функции . Модели, обученные поддерживать эту возможность, могут отвечать на приглашение специально отформатированным ответом, который указывает вызывающему приложению, что оно должно выполнить какое-то действие и отправить результат обратно в LLM вместе с исходным приглашением. В Genkit есть библиотечные функции, которые автоматизируют как генерацию подсказок, так и элементы цикла вызов-ответ в реализации вызова инструмента. См. раздел Вызов инструмента , чтобы узнать больше.
  • Генерация с расширенным поиском (RAG) — это метод, используемый для введения специфичной для предметной области информации в выходные данные модели. Это достигается путем вставки соответствующей информации в подсказку перед ее передачей в языковую модель. Полная реализация RAG требует объединения нескольких технологий: моделей генерации встраивания текста, векторных баз данных и больших языковых моделей. См. раздел Генерация с расширенным поиском (RAG), чтобы узнать, как Genkit упрощает процесс координации этих различных элементов.