Cloud Functions for Firebase を使用してフローをデプロイする

Genkit には、Cloud Functions for Firebase にフローをデプロイするのに役立つプラグインが含まれています。デプロイされたフローは HTTPS エンドポイントとして使用でき、Cloud Functions クライアント ライブラリを使用して呼び出し可能な関数としてアクセスできます。

始める前に

  • Firebase CLI をインストールします。
  • Genkit のフローの概念とフローを作成する方法を理解している必要があります。このページの手順では、デプロイするフローがあらかじめ定義されていることを前提としています。
  • Firebase 用 Cloud Functions をすでに使用している場合は役立ちますが、必須ではありません。

1. Firebase プロジェクトを設定します

TypeScript Cloud Functions が設定された Firebase プロジェクトがまだない場合は、次の手順を行います。

  1. Firebase コンソールを使用して新しい Firebase プロジェクトを作成するか、既存のプロジェクトを選択します。

  2. プロジェクトを Blaze プランにアップグレードします。これは、Cloud Functions のデプロイに必要です。

  3. Firebase CLI でログインします。

    firebase login
    firebase login --reauth # alternative, if necessary
    firebase login --no-localhost # if running in a remote shell
  4. 新しいプロジェクト ディレクトリを作成します。

    export PROJECT_ROOT=~/tmp/genkit-firebase-project1
    mkdir -p $PROJECT_ROOT
  5. ディレクトリ内で Firebase プロジェクトを初期化します。

    cd $PROJECT_ROOT
    firebase init genkit

    このページの残りの部分では、関数を TypeScript で記述することを前提としていますが、JavaScript を使用している場合は Genkit フローをデプロイすることもできます。

2. フロー定義を更新する

Cloud Functions を使用して Firebase プロジェクトを設定したら、プロジェクトの functions/src ディレクトリにフロー定義をコピーまたは書き込み、index.ts にエクスポートできます。

フローをデプロイできるようにするには、フローの定義方法に少し変更を加える必要があります。コアロジックは同じですが、デプロイをスムーズに行い、デプロイ後にセキュリティを強化するために、追加情報を追加します。

次のようなフローがあるとします。

const generatePoemFlow = ai.defineFlow(
  {
    name: "generatePoem",
    inputSchema: z.string(),
    outputSchema: z.string(),
  },
  async (subject: string) => {
    const { text } = await ai.generate(`Compose a poem about ${subject}.`);
    return text;
  }
);

以降のセクションでは、デプロイする前に行う必要がある変更について説明します。

onFlow でフローを定義する

Genkit.defineFlow() でフローを定義する代わりに、Firebase プラグインの onFlow() 関数を使用します。この関数を使用すると、onCall と同様に、フロー ロジックを Cloud Functions リクエスト ハンドラでラップできます。

import { onFlow } from "@genkit-ai/firebase/functions";

export const generatePoem = onFlow(
  ai,
  {
    // ...
  },
  async (subject: string) => {
    // ...
  }
);

onFlowGenkit のメソッドではなく、Genkit インスタンスを最初のパラメータとして受け取る関数です。それ以外の場合、構文は defineFlow に似ています。

認可ポリシーを定義する

デプロイされたすべてのフロー(Firebase にデプロイされているかどうかにかかわらず)には、認可ポリシーが必要です。ポリシーがないと、費用のかかる生成 AI フローを誰でも呼び出せるようになります。認可ポリシーを定義するには、フロー定義で authPolicy パラメータを使用します。

import { firebaseAuth } from "@genkit-ai/firebase/auth";

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    // ...
    authPolicy: firebaseAuth((user, input) => {
      if (!user.email_verified) {
        throw new Error("Verified email required to run flow");
      }
    }),
  },
  async (subject: string) => {
    // ...
  }
);

このポリシーは、firebaseAuth() ヘルパーを使用して、確認済みのメールアドレスを持つアプリの登録ユーザーのみにアクセスを許可します。クライアント側では、Authorization: Bearer ヘッダーをポリシーを満たす Firebase ID トークンに設定する必要があります。Cloud Functions クライアント SDK には、これを自動化する呼び出し可能関数メソッドが用意されています。例については、デプロイされたフローを試すをご覧ください。

デプロイされたフローに対して API 認証情報を使用できるようにする

デプロイされたフローには、依存するリモート サービスで認証を行う方法が必要です。ほとんどのフローでは、使用するモデル API サービスにアクセスするための認証情報が最低限必要になります。

この例では、選択したモデル プロバイダに応じて、次のいずれかを行います。

Gemini(Google AI)

  1. Google AI がお住まいの地域で利用可能であることを確認します。

  2. Google AI Studio を使用して Gemini API の API キーを生成します。

  3. API キーを Cloud Secret Manager に保存します。

    firebase functions:secrets:set GOOGLE_GENAI_API_KEY

    このステップは、課金対象となる可能性のあるサービスへのアクセスを許可する API キーが誤って漏洩するのを防ぐために重要です。

    シークレットの管理の詳細については、機密性の高い構成情報の保存とアクセスをご覧ください。

  4. src/index.ts を編集し、既存のインポートの後に次を追加します。

    import {defineSecret} from "firebase-functions/params";
    const googleAIapiKey = defineSecret("GOOGLE_GENAI_API_KEY");
    

    次に、フロー定義で、Cloud Functions の関数にこのシークレット値へのアクセスが必要であることを宣言します。

    export const generatePoem = onFlow(
      {
        name: "generatePoem",
        // ...
        httpsOptions: {
          secrets: [googleAIapiKey],  // Add this line.
        },
      },
      async (subject) => {
        // ...
      }
    );
    

この関数をデプロイすると、API キーが Cloud Secret Manager に保存され、Cloud Functions 環境から使用できるようになります。

Gemini(Vertex AI)

  1. Cloud コンソールで、Firebase プロジェクトの Vertex AI API を有効にする

  2. [IAM] ページで、デフォルトのコンピューティング サービス アカウントVertex AI ユーザーロールが付与されていることを確認します。

このチュートリアルで設定する必要があるシークレットはモデル プロバイダ用のものだけですが、一般的には、フローで使用する各サービスに対して同様の設定を行う必要があります。

CORS ポリシーを設定する

ウェブアプリからフロー(デプロイされたフローを試すで説明します)にアクセスする場合は、httpsOptions パラメータで CORS ポリシーを設定します。

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    // ...
    httpsOptions: {
      cors: '*',
    },
  },
  async (subject: string) => {
    // ...
  }
);

本番環境アプリには、より制限の厳しいポリシーが必要になる可能性がありますが、このチュートリアルではこれで十分です。

サンプルコードの全文

上記の変更をすべて行った後、デプロイ可能なフローは次のようになります。

const googleAIapiKey = defineSecret("GOOGLE_GENAI_API_KEY");

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    inputSchema: z.string(),
    outputSchema: z.string(),
    authPolicy: firebaseAuth((user, input) => {
      if (!user.email_verified) {
        throw new Error("Verified email required to run flow");
      }
    }),
    httpsOptions: {
      secrets: [googleAIapiKey],
      cors: '*',
    },
  },
  async (subject: string) => {
    const { text } = await ai.generate(`Compose a poem about ${subject}.`);
    return text;
  }
);

3. Firebase にフローをデプロイする

onFlow を使用してフローを定義したら、他の Cloud Functions の関数をデプロイする場合と同様にデプロイできます。

cd $PROJECT_ROOT
firebase deploy --only functions

これで、フローを Cloud Functions の関数としてデプロイできました。ただし、フロー承認ポリシーにより、curl などでデプロイされたエンドポイントにアクセスすることはできません。フローへの安全なアクセス方法については、次のセクションをご覧ください。

省略可: デプロイされたフローを試す

フロー エンドポイントを試すには、次の最小限の例のウェブアプリをデプロイします。

  1. Firebase コンソールの [プロジェクト設定] セクションで、新しいウェブアプリを追加し、Hosting も設定するオプションを選択します。

  2. Firebase コンソールの [Authentication] セクションで、この例で使用する Google プロバイダを有効にします。

  3. プロジェクト ディレクトリで Firebase Hosting を設定し、サンプルアプリをデプロイします。

    cd $PROJECT_ROOT
    firebase init hosting

    すべてのプロンプトでデフォルト値を受け入れます。

  4. public/index.html は、次のように置き換えます。

    <!DOCTYPE html>
    <html>
      <head>
        <title>Genkit demo</title>
      </head>
      <body>
        <div id="signin" hidden>
          <button id="signinBtn">Sign in with Google</button>
        </div>
        <div id="callGenkit" hidden>
          Subject: <input type="text" id="subject" />
          <button id="generatePoem">Compose a poem on this subject</button>
          <p id="generatedPoem"></p>
        </div>
        <script type="module">
          import { initializeApp } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-app.js";
          import {
            getAuth,
            onAuthStateChanged,
            GoogleAuthProvider,
            signInWithPopup,
          } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-auth.js";
          import {
            getFunctions,
            httpsCallable,
          } from "https://www.gstatic.com/firebasejs/11.0.1/firebase-functions.js";
    
          const firebaseConfig = await fetch("/__/firebase/init.json");
          initializeApp(await firebaseConfig.json());
    
          async function generatePoem() {
            const poemFlow = httpsCallable(getFunctions(), "generatePoem");
            const subject = document.querySelector("#subject").value;
            const response = await poemFlow(subject);
            document.querySelector("#generatedPoem").innerText = response.data;
          }
    
          function signIn() {
            signInWithPopup(getAuth(), new GoogleAuthProvider());
          }
    
          document.querySelector("#signinBtn").addEventListener("click", signIn);
          document
            .querySelector("#generatePoem")
            .addEventListener("click", generatePoem);
    
          const signinEl = document.querySelector("#signin");
          const genkitEl = document.querySelector("#callGenkit");
    
          onAuthStateChanged(getAuth(), (user) => {
            if (!user) {
              signinEl.hidden = false;
              genkitEl.hidden = true;
            } else {
              signinEl.hidden = true;
              genkitEl.hidden = false;
            }
          });
        </script>
      </body>
    </html>
    
  5. ウェブアプリと Cloud Functions 関数をデプロイします。

    cd $PROJECT_ROOT
    firebase deploy

deploy コマンドで出力された URL にアクセスして、ウェブアプリを開きます。このアプリでは、Google アカウントでログインする必要があります。ログインすると、エンドポイント リクエストを開始できます。

省略可: デベロッパー UI でフローを実行する

onFlow を使用して定義されたフローは、defineFlow を使用して定義されたフローを実行する場合とまったく同じ方法でデベロッパー UI で実行できるため、デプロイと開発の間で切り替える必要はありません。

cd $PROJECT_ROOT/functions
npx genkit start -- npx tsx --watch src/index.ts

または

cd $PROJECT_ROOT/functions
npm run genkit:start

これで、genkit start コマンドで出力された URL に移動してアクセスできるようになりました。

省略可: Firebase Local Emulator Suite を使用した開発

Firebase には、Genkit で使用できるローカル開発用のエミュレータ スイートが用意されています。

Firebase Emulator Suite で Genkit Dev UI を使用するには、次のように Firebase エミュレータを起動します。

npx genkit start -- firebase emulators:start --inspect-functions

これにより、エミュレータでコードが実行され、Genkit フレームワークが開発モードで実行されます。これにより、Genkit リフレクション API が起動して公開されます(ただし、デベロッパー UI は公開されません)。

デベロッパー UI で Firestore のトレースを表示するには、[Inspect] タブに移動して [Dev/Prod] スイッチを切り替えます。[prod] に切り替えると、Firestore からトレースが読み込まれます。