AI モデルによるコンテンツの生成

生成 AI の中核をなすのは AI モデルです。現在、生成モデルの最も顕著な例は、大規模言語モデル(LLM)と画像生成モデルの 2 つです。これらのモデルは、プロンプトと呼ばれる入力(ほとんどの場合、テキスト、画像、またはその両方の組み合わせ)を受け取り、そこからテキスト、画像、音声、動画を出力します。

これらのモデルの出力は驚くほど説得力があります。LLM は人間が書いたように見えるテキストを生成します。画像生成モデルは、実際の写真や人間が作成したアートワークに非常に近い画像を生成できます。

さらに、LLM は単純なテキスト生成以外のタスクにも対応できることが実証されています。

  • コンピュータ プログラムの作成
  • 大きなタスクを完了するために必要なサブタスクを計画する
  • 整理されていないデータの整理
  • テキストのコーパスから情報データを理解して抽出する
  • アクティビティのテキスト記述に基づいて自動アクティビティのフォローと実行

利用可能なモデルは、複数のプロバイダから提供されています。各モデルには独自の長所と短所があり、あるモデルは特定のタスクでは優れているが、他のタスクではパフォーマンスが低い場合があります。生成 AI を使用するアプリでは、多くの場合、現在のタスクに応じて複数の異なるモデルを使用すると効果的です。

通常、アプリ デベロッパーは生成 AI モデルを直接操作するのではなく、ウェブ API として利用可能なサービスを介して操作します。これらのサービスは類似した機能を備えていることがよくありますが、互換性のない異なる API を介して提供されます。複数のモデルサービスを利用する場合は、それぞれの独自の SDK を使用する必要があります。これらの SDK は互換性がない可能性があります。また、あるモデルから最新の最も機能が豊富なモデルにアップグレードする場合、その統合を最初から構築し直す必要がある場合があります。

Genkit は、任意の生成 AI モデル サービスにアクセスする詳細を抽象化した単一のインターフェースを提供し、いくつかのビルド済み実装をすでに利用できるようにすることで、この課題に対処します。Genkit を中心に AI を活用したアプリを構築すると、最初の生成 AI 呼び出しのプロセスが簡素化され、新しいモデルが登場したときに複数のモデルを組み合わせたり、モデルを交換したりすることも同様に簡単になります。

始める前に

このページのコードサンプルを実行する場合は、まずスタートガイドの手順を完了してください。以下の例では、Genkit がプロジェクトの依存関係としてすでにインストールされていることを前提としています。

Genkit でサポートされているモデル

Genkit は、任意の生成 AI モデル サービスを使用できるほど柔軟に設計されています。コア ライブラリはモデルを操作するための共通インターフェースを定義し、モデル プラグインは特定のモデルとその API を操作するための実装の詳細を定義します。

Genkit チームは、Vertex AI、Google 生成 AI、Ollama が提供するモデルを操作するためのプラグインを維持しています。

  • Google Cloud Vertex AI プラグインを介した LLM の Gemini ファミリー
  • Google AI プラグインを介した Gemini LLM ファミリー
  • Google Cloud Vertex AI を介した Imagen2 と Imagen3 の画像生成モデル
  • Google Cloud Vertex AI のモデル ガーデンを介した Anthropic の Claude 3 LLM ファミリー
  • Ollama プラグインを介して Gemma 2、Llama 3 などの多くのオープンモデル(Ollama サーバーを自分でホストする必要があります)

また、これらのモデルへのインターフェースを提供する、コミュニティ サポートのプラグインもいくつかあります。

詳しくは、npmjs.org で genkit-model のタグが付けられたパッケージを検索してください。

モデル プラグインの読み込みと構成

Genkit を使用してコンテンツの生成を開始するには、モデル プラグインを読み込んで構成する必要があります。スタートガイドからこのページにアクセスしている場合は、すでにこの手順を完了しています。そうでない場合は、スタートガイドまたは個々のプラグインのドキュメントで手順を確認し、続行してください。

generate() メソッド

Genkit では、生成 AI モデルを操作するための主なインターフェースは generate() メソッドです。

最も単純な generate() 呼び出しでは、使用するモデルとテキスト プロンプトを指定します。

import { gemini15Flash, googleAI } from '@genkit-ai/googleai';
import { genkit } from 'genkit';

const ai = genkit({
  plugins: [googleAI()],
  model: gemini15Flash,
});

(async () => {
  const { text } = await ai.generate(
    'Invent a menu item for a pirate themed restaurant.'
  );
  console.log(text);
})();

この簡単な例を実行すると、デバッグ情報が出力され、その後に 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 インスタンスの構成時に指定したデフォルトモデルに生成リクエストを送信しています。

1 回の generate() 呼び出しにモデルを指定することもできます。

const { text } = await ai.generate({
  model: gemini15Pro,
  prompt: 'Invent a menu item for a pirate themed restaurant.',
});

この例では、モデル プラグインによってエクスポートされたモデル参照を使用します。別の方法として、文字列識別子を使用してモデルを指定することもできます。

const { text } = await ai.generate({
  model: 'googleai/gemini-1.5-pro-latest',
  prompt: 'Invent a menu item for a pirate themed restaurant.',
});

モデルの文字列識別子は providerid/modelid のようになります。プロバイダ ID(この場合は googleai)はプラグインを識別し、モデル ID は特定のバージョンのモデルのプラグイン固有の文字列識別子です。

Ollama プラグインなどの一部のモデル プラグインは、数十種類のモデルにアクセスできるため、個々のモデル参照をエクスポートしません。このような場合は、文字列 ID を使用して generate() にモデルを指定できます。

これらの例は、重要なポイントも示しています。generate() を使用して生成 AI モデルを呼び出す場合、使用するモデルを変更するには、モデル パラメータに別の値を渡すだけです。ネイティブ モデル SDK ではなく generate() を使用すると、アプリで複数のモデルを簡単に使用したり、将来的にモデルを変更したりできます。

ここまでに説明したのは、最も単純な generate() 呼び出しの例のみです。ただし、generate() には、生成モデルとの高度なインタラクション用のインターフェースも用意されています。このインターフェースについては、後のセクションで説明します。

システム プロンプト

一部のモデルでは、システム プロンプトの提供がサポートされています。これは、ユーザーからのメッセージにどのように応答するかをモデルに指示します。システム プロンプトを使用して、モデルに採用するペルソナ、回答のトーン、回答の形式などを指定できます。

使用しているモデルがシステム プロンプトをサポートしている場合は、system パラメータでプロンプトを指定できます。

const { text } = await ai.generate({
  system: 'You are a food industry marketing consultant.',
  prompt: 'Invent a menu item for a pirate themed restaurant.',
});

モデル パラメータ

generate() 関数は config パラメータを取ります。このパラメータを使用して、モデルがコンテンツを生成する方法を制御するオプションの設定を指定できます。

const { text } = await ai.generate({
  prompt: 'Invent a menu item for a pirate themed restaurant.',
  config: {
    maxOutputTokens: 400,
    stopSequences: ['<end>', '<fin>'],
    temperature: 1.2,
    topP: 0.4,
    topK: 50,
  },
});

サポートされるパラメータは、個々のモデルとモデル API によって異なります。ただし、前の例のパラメータはほとんどのモデルに共通しています。これらのパラメータの説明は次のとおりです。

出力の長さを制御するパラメータ

maxOutputTokens

LLM は、トークンと呼ばれる単位で動作します。トークンは通常、特定の文字列にマッピングされますが、必ずしもそうとは限りません。プロンプトをモデルに渡す際の最初のステップの 1 つは、プロンプト文字列をトークンのシーケンスにトークン化することです。次に、LLM はトークン化された入力からトークンのシーケンスを生成します。最後に、トークンのシーケンスがテキストに変換され、これが出力になります。

maximum output tokens パラメータは、LLM を使用して生成するトークンの数に上限を設定します。モデルごとに異なるトークン化ツールを使用する可能性がありますが、1 つの英語の単語が 2 ~ 4 つのトークンで構成されていると見なすのが一般的です。

前述のとおり、一部のトークンは文字列にマッピングされない場合があります。たとえば、シーケンスの終了を示すトークンがよくあります。LLM がこのトークンを生成すると、生成が停止します。したがって、LLM が「停止」トークンを生成したために、最大数よりも少ないトークンを生成することがあります。

stopSequences

このパラメータを使用すると、生成時に LLM 出力の終了を示すトークンまたはトークン シーケンスを設定できます。ここで使用する正しい値は通常、モデルのトレーニング方法によって異なり、通常はモデル プラグインによって設定されます。ただし、別の停止シーケンスを生成するようにモデルに指示した場合は、ここで指定できます。

トークン自体ではなく、文字列を指定してください。ほとんどの場合、モデルのトークン化ツールが 1 つのトークンにマッピングする文字列を指定します。

「クリエイティビティ」を制御するパラメータ

temperaturetop-ptop-k パラメータは、モデルの「創造性」を制御します。以下に、これらのパラメータの意味について簡単に説明します。ただし、これらのパラメータは LLM の出力の文字を調整するために使用されることを覚えておくことが重要です。最適な値は目標と設定内容によって異なり、テストを実施して見極める必要があります。

温度

LLM は基本的にトークンを予測するマシンです。LLM は、特定のトークン列(プロンプトなど)について、その語彙内の各トークンが列内で次に出現する可能性を予測します。温度は、これらの予測を分割して 0 ~ 1 の確率に正規化するスケーリング係数です。

温度の値が低い(0.0 ~ 1.0)と、トークン間の確率の差が拡大します。その結果、モデルは、すでに評価して確率が低いと判断したトークンを生成する可能性がさらに低くなります。これは、クリエイティブ性が低い出力と見なされることがよくあります。技術的には 0.0 は有効な値ではありませんが、多くのモデルでは、モデルが確定的に動作し、最も可能性の高いトークンのみを考慮することを示す値として扱います。

温度値が高い(1.0 より大きい)と、トークン間の確率の差が圧縮され、以前に評価された可能性の低いトークンが生成される可能性が高くなります。これは、より創造的な出力と見なされることがよくあります。一部のモデル API には、最大温度(多くの場合 2.0)が適用されます。

topP

トップ p は 0.0 ~ 1.0 の値で、トークンの累積確率を指定して、モデルで検討する可能性のあるトークンの数を制御します。たとえば、値が 1.0 の場合、考えられるすべてのトークンを考慮しますが、各トークンの確率も考慮します。値が 0.4 の場合、確率の合計が 0.4 になる最も可能性の高いトークンのみを考慮し、残りのトークンは考慮から除外されます。

topK

Top-k は整数値で、モデルで考慮する可能性のあるトークンの数も制御します。ただし、この場合はトークンの最大数を明示的に指定します。値 1 を指定すると、モデルは確定的に動作します。

モデル パラメータをテストする

デベロッパー UI を使用して、さまざまなモデルとプロンプトの組み合わせによって生成される出力に対するこれらのパラメータの影響をテストできます。genkit start コマンドでデベロッパー UI を起動すると、プロジェクトで構成されたプラグインによって定義されたすべてのモデルが自動的に読み込まれます。コードでこれらの変更を繰り返し行う必要はなく、さまざまなプロンプトと構成値をすばやく試すことができます。

構造化出力

生成 AI をアプリケーションのコンポーネントとして使用する場合は、多くの場合、書式なしテキスト以外の形式で出力する必要があります。ユーザーに表示するコンテンツを生成する場合でも、ユーザーに魅力的に表示するために構造化出力を利用できます。ただし、モデルの出力をプログラムで使用したり、あるモデルの出力を別のモデルにフィードしたりするなど、生成 AI の高度なアプリケーションでは、構造化された出力が必要です。

Genkit では、generate() を呼び出すときにスキーマを指定して、モデルから構造化出力をリクエストできます。

import { z } from 'genkit'; // Import Zod, which is re-exported by Genkit.
const MenuItemSchema = z.object({
  name: z.string(),
  description: z.string(),
  calories: z.number(),
  allergens: z.array(z.string()),
});

const { output } = await ai.generate({
  prompt: 'Invent a menu item for a pirate themed restaurant.',
  output: { schema: MenuItemSchema },
});

モデル出力スキーマは、Zod ライブラリを使用して指定します。Zod はスキーマ定義言語に加えて、ランタイム型チェックも提供します。これにより、静的 TypeScript 型と生成 AI モデルの予測不可能な出力のギャップを埋めることができます。Zod を使用すると、生成呼び出しが成功すると常に TypeScript 型に準拠した出力が返されるという事実に依存できるコードを記述できます。

generate() でスキーマを指定すると、Genkit は次のことを行います。

  • 目的の出力形式に関する追加のガイダンスをプロンプトに追加します。また、生成するコンテンツをモデルに正確に指定する副作用もあります(メニュー アイテムを提案するだけでなく、説明やアレルゲンのリストも生成するなど)。
  • モデル出力を解析して JavaScript オブジェクトに変換します。
  • 出力がスキーマに準拠していることを確認します。

生成呼び出しが成功したときに構造化出力を取得するには、レスポンス オブジェクトの output プロパティを使用します。

if (output) {
  const { name, description, calories, allergens } = output;
}

エラー処理

上記の例では、output プロパティは null にできます。これは、モデルがスキーマに準拠した出力を生成できない場合に発生することがあります。このようなエラーに対処するための最善の戦略は、具体的なユースケースによって異なりますが、ここでは一般的なヒントをいくつかご紹介します。

  • 別のモデルを試す。構造化出力を成功させるには、モデルが JSON で出力を生成できる必要があります。Gemini や Claude などの最も強力な LLM は、この処理を行うのに十分な汎用性があります。ただし、Ollama で使用する一部のローカルモデルなど、小規模なモデルでは、そのために特別にトレーニングされていない限り、構造化出力を確実に生成できない場合があります。

  • Zod の型変換機能を使用する: Zod が準拠していない型をスキーマで指定された型に型変換しようとすることをスキーマで指定できます。スキーマに文字列以外のプリミティブ タイプが含まれている場合は、Zod 型変換を使用すると、発生する generate() エラーの数を減らすことができます。次のバージョンの MenuItemSchema では、型変換を使用して、モデルがカロリー情報を数値ではなく文字列として生成する状況を自動的に修正します。

    const MenuItemSchema = z.object({
      name: z.string(),
      description: z.string(),
      calories: z.coerce.number(),
      allergens: z.array(z.string()),
    });
    
  • generate() 呼び出しを再試行します。選択したモデルが準拠した出力を生成できないのはまれな場合、ネットワーク エラーと同様にエラーを処理し、なんらかの増分バックオフ戦略を使用してリクエストを再試行します。

ストリーミング

大量のテキストを生成する場合は、出力が生成されるときに出力を表示することで、ユーザー エクスペリエンスを向上させることができます。つまり、出力をストリーミングします。ストリーミングの動作のよくある例は、ほとんどの LLM チャットアプリで見られます。ユーザーは、メッセージに対するモデルのレスポンスが生成されるときにそれを確認できます。これにより、アプリケーションの応答性が向上し、インテリジェントな相手とチャットしているような錯覚をユーザーに与えることができます。

Genkit では、generateStream() メソッドを使用して出力をストリーミングできます。構文は generate() メソッドに似ています。

const { response, stream } = await ai.generateStream(
  'Suggest a complete menu for a pirate themed restaurant.'
);

レスポンス オブジェクトには stream プロパティがあります。このプロパティを使用すると、生成されたリクエストのストリーミング出力を反復処理できます。

for await (const chunk of stream) {
  console.log(chunk.text);
}

ストリーミング以外のリクエストと同様に、リクエストの完全な出力を取得することもできます。

const completeText = (await response).text;

ストリーミングは構造化出力でも機能します。

const MenuSchema = z.object({
  starters: z.array(MenuItemSchema),
  mains: z.array(MenuItemSchema),
  desserts: z.array(MenuItemSchema),
});

const { response, stream } = await ai.generateStream({
  prompt: 'Suggest a complete menu for a pirate themed restaurant.',
  output: { schema: MenuSchema },
});

for await (const chunk of stream) {
  // `output` is an object representing the entire output so far.
  console.log(chunk.output);
}

// Get the completed output.
const { output } = await response;

構造化出力のストリーミングは、テキストのストリーミングとは少し異なります。レスポンス チャンクの output プロパティは、単一のチャンクを表すオブジェクトではなく、これまでに生成されたチャンクの集合から構築されたオブジェクトです(単独では有効ではない場合があります)。構造化出力の各チャンクは、ある意味では前のチャンクを置き換えます

たとえば、前の例の最初の 5 つの出力は次のようになります。

null

{ starters: [ {} ] }

{
  starters: [ { name: "Captain's Treasure Chest", description: 'A' } ]
}

{
  starters: [
    {
      name: "Captain's Treasure Chest",
      description: 'A mix of spiced nuts, olives, and marinated cheese served in a treasure chest.',
      calories: 350
    }
  ]
}

{
  starters: [
    {
      name: "Captain's Treasure Chest",
      description: 'A mix of spiced nuts, olives, and marinated cheese served in a treasure chest.',
      calories: 350,
      allergens: [Array]
    },
    { name: 'Shipwreck Salad', description: 'Fresh' }
  ]
}

マルチモーダル入力

これまでの例では、モデル プロンプトとしてテキスト文字列を使用していました。これは生成 AI モデルにプロンプトを送信する最も一般的な方法ですが、多くのモデルでは他のメディアをプロンプトとして使用することもできます。メディア プロンプトは、画像にキャプションを追加する、音声録音を文字に変換するなど、メディアに対してなんらかのオペレーションを実行するようモデルに指示するテキスト プロンプトと組み合わせて使用されることがほとんどです。

メディア入力を受け入れる機能と使用できるメディアの種類は、モデルとその API に完全に依存します。たとえば、Gemini 1.5 シリーズのモデルは、画像、動画、音声をプロンプトとして受け入れることができます。

メディア プロンプトをサポートするモデルにメディア プロンプトを提供するには、単純なテキスト プロンプトを generate に渡すのではなく、メディア部分とテキスト部分で構成される配列を渡します。

const { text } = await ai.generate([
  { media: { url: 'https://example.com/photo.jpg' } },
  { text: 'Compose a poem about this image.' },
]);

上記の例では、一般公開されている HTTPS URL を使用してイメージを指定しました。メディアデータをデータ URL としてエンコードして直接渡すこともできます。次に例を示します。

import { readFile } from 'node:fs/promises';
const b64Data = await readFile('photo.jpg', { encoding: 'base64url' });
const dataUrl = `data:image/jpeg;base64,${b64Data}`;

const { text } = await ai.generate([
  { media: { url: dataUrl } },
  { text: 'Compose a poem about this image.' },
]);

メディア入力をサポートするすべてのモデルは、データ URL と HTTPS URL の両方をサポートしています。一部のモデル プラグインでは、他のメディアソースのサポートが追加されています。たとえば、Vertex AI プラグインでは、Cloud Storage(gs://)URL を使用することもできます。

メディアの生成

ここまでのこのページの例のほとんどは、LLM を使用してテキストを生成する方法について説明してきました。ただし、Genkit は画像生成モデルでも使用できます。画像生成モデルで generate() を使用することは、LLM を使用する場合と似ています。たとえば、Vertex AI で Imagen2 モデルを使用して画像を生成するには:

  1. Genkit は、生成されたメディアの標準出力形式として data: URL を使用します。これは標準形式であり、多くのライブラリで処理できます。この例では、jsdomdata-urls パッケージを使用します。

    npm i --save data-urls
    npm i --save-dev @types/data-urls
  2. 画像を生成してファイルに保存するには、画像生成モデルと出力形式のメディアタイプを指定して generate() を呼び出します。

    import { imagen3Fast, vertexAI } from '@genkit-ai/vertexai';
    import parseDataURL from 'data-urls';
    import { genkit } from 'genkit';
    
    import { writeFile } from 'node:fs/promises';
    
    const ai = genkit({
      plugins: [vertexAI({ location: 'us-central1' })],
    });
    
    (async () => {
      const { media } = await ai.generate({
        model: imagen3Fast,
        prompt: 'photo of a meal fit for a pirate',
        output: { format: 'media' },
      });
    
      if (media === null) throw new Error('No media generated.');
    
      const data = parseDataURL(media.url);
      if (data === null) throw new Error('Invalid "data:" URL.');
    
      await writeFile(`output.${data.mimeType.subtype}`, data.body);
    })();
    

次のステップ

Genkit の詳細

  • アプリ デベロッパーが生成 AI モデルの出力に影響を与える主な方法は、プロンプトを使用することです。Genkit を使用して効果的なプロンプトを開発し、コードベースで管理する方法については、プロンプトの管理をご覧ください。
  • generate() は、生成 AI を活用したすべてのアプリケーションの中核ですが、実際のアプリケーションでは、通常、生成 AI モデルの呼び出しの前後に追加の作業が必要になります。これを反映するために、Genkit ではフローのコンセプトが導入されています。フローの関数としての定義に加えて、オブザーバビリティやデプロイの簡素化などの機能が追加されています。詳細については、ワークフローの定義をご覧ください。

LLM の高度な使用

  • LLM の機能を強化する 1 つの方法は、ユーザーに詳細情報をリクエストする方法や、ユーザーになんらかのアクションをリクエストする方法のリストをプロンプトとして表示することです。これはツール呼び出しまたは関数呼び出しと呼ばれます。この機能をサポートするようにトレーニングされたモデルは、特別な形式のレスポンスでプロンプトに応答できます。これは、呼び出し元のアプリケーションに、なんらかのアクションを実行し、元のプロンプトとともに結果を LLM に送り返す必要があることを示します。Genkit には、プロンプトの生成と、ツール呼び出しの実装の呼び出し / レスポンス ループ要素の両方を自動化するライブラリ関数があります。詳細については、ツールの呼び出しをご覧ください。
  • 検索拡張生成(RAG)は、ドメイン固有の情報をモデルの出力に導入するために使用される手法です。これは、関連する情報をプロンプトに挿入してから言語モデルに渡すことで実現されます。完全な RAG を実装するには、テキスト エンベディング生成モデル、ベクトル データベース、大規模言語モデルなど、複数のテクノロジーを組み合わせる必要があります。Genkit がこれらのさまざまな要素を調整するプロセスを簡素化する方法については、検索拡張生成(RAG)をご覧ください。

モデル出力のテスト

ソフトウェア エンジニアは、同じ入力に対して常に同じ出力が生成される確定的システムに慣れています。ただし、AI モデルは確率的であるため、出力は入力の微妙な違い、モデルのトレーニング データ、さらには温度などのパラメータによって意図的に導入されたランダム性によって異なる場合があります。

Genkit の評価ツールは、さまざまな戦略を使用して LLM のレスポンスの品質を評価するための構造化された方法です。詳しくは、評価のページをご覧ください。