リクエストの前後にカスタム サーバーサイド スクリプトを実行する


アプリが Firebase AI Logic を介して Gemini API に送信するすべてのリクエストの前後に、独自のカスタム サーバーサイド スクリプトを実行できます。クライアント コードを変更する必要はありません。これらのスクリプトは、Cloud Functions for Firebase にデプロイされたコールバック スタイルの関数として実装します。

この機能を使用すると、プロンプトの管理、トークン使用量の上限設定、分析用の生成のロギング、レスポンス コンテンツの削除などを行うことができます。

次の 2 つのイベントタイプを使用できます。

  • beforeGenerateContent: リクエストが Gemini API に到達する前に実行されます。この関数は、リクエストを検査または変更できます。また、エラーをスローしてリクエストを完全にブロックすることもできます。

  • afterGenerateContent: Gemini API からレスポンスが返された後、クライアント アプリに返される前に実行されます。この関数は、レスポンスの検査や変更、レスポンスの完全なブロック、レスポンスの監視(ロギングや監査など)を行うことができます。

スクリプトが関数として Cloud Functions for Firebase にデプロイされると、Firebase AI Logic トリガーとして登録されます。つまり、Firebase AI Logic を介して Gemini API に送信されるプロジェクト内のすべての generateContent リクエスト(サーバー プロンプト テンプレートで作成されたリクエストを含む)に対して実行されます。

これらの関数は、Firebase AI Logic を介さない Gemini API へのリクエストではトリガーされません。

前提条件

ステップ 1: Cloud Functions for Firebase 用にプロジェクトを設定する

Firebase プロジェクトで Cloud Functions for Firebase を使用したことがない場合は、次の設定を完了します。

  1. Firebase プロジェクトが従量課金制の Blaze のお支払いプランに登録されていることを確認します(Cloud Functions for Firebase を使用するために必要)。

  2. コマンドライン インターフェース(CLI)をインストールします。gcloud CLI と Firebase CLI

  3. デフォルトのコンピューティング サービス アカウントに、関数のビルドに必要な Cloud Build サービス アカウントのロール(roles/cloudbuild.builds.builder)を付与します。次の gcloud CLI コマンドを実行します。

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
      --role="roles/cloudbuild.builds.builder"
    
  4. Firebase プロジェクトで Cloud Functions for Firebase を初期化します。

    1. 次の Firebase CLI コマンドを実行します。

      firebase init functions
      
    2. プロンプトが表示されたら、[TypeScript] を選択します。

    3. functions/package.json の firebase-functions がバージョン 6.3.0 以降であることを確認します。バージョンを確認する方法は次のとおりです。

      npm --prefix functions list firebase-functions
      

ステップ 2: 関数を記述する

リクエスト前関数(beforeGenerateContent)を作成する リクエスト後関数(afterGenerateContent)を作成する

リクエスト前関数(beforeGenerateContent)を作成する

beforeGenerateContent イベントタイプを使用すると、Firebase AI Logic プロキシが generateContent リクエストを受信したときに関数がトリガーされます。この関数は、 リクエストが Gemini API に送信される前に、リクエストに対して実行されます。 この関数は、リクエストを変更することも、リクエストを完全にブロックすることもできます。

関数を作成する前に、次の情報を確認してください。

例

以下は、次の処理を行うリクエスト前関数の例です。

  • リクエストが特定の Gemini API プロバイダに対するものである場合にのみ関数を実行するように指定します。

  • ブロックされたトピックのプロンプトを検査し、エラーをスローしてリクエストを拒否します。

  • テキスト生成モデルの最大出力トークン数を制限します。

import { logger } from "firebase-functions";
import {
  beforeGenerateContent,
  HttpsError,
  vertexV1Beta1,
  type VertexV1Beta1GenerateContentRequest,
} from "firebase-functions/v2/ai";

const BLOCKED_TOPICS = ["weapon", "explosive", "self-harm"];
const MAX_OUTPUT_TOKENS = 4000;

export const guardPrompts = beforeGenerateContent((event) => {
  // 1. Optional: If you want the function to only run for a specific Gemini API provider, specify it here.
  if (event.data.api !== vertexV1Beta1) return;
  const request = event.data.request as VertexV1Beta1GenerateContentRequest;

  // 2. Read the prompt: contents[] -> parts[] -> text
  const prompt = (request.contents ?? [])
    .flatMap((c) => c.parts ?? [])
    .map((p) => ("text" in p ? p.text : "") ?? "")
    .join(" ")
    .toLowerCase();

  // 3. Throwing rejects the request. The request is never sent to the Gemini API.
  const blocked = BLOCKED_TOPICS.find((t) => prompt.includes(t));
  if (blocked) {
    logger.warn("Blocked a prompt", { topic: blocked });
    throw new HttpsError("invalid-argument", `We don't return content about ${blocked}.`);
  }

  logger.info("Allowing generation", {
    model: event.data.model,
    authType: event.authType,
    authId: event.authId,
    appId: event.appId,
  });

  // 4. The next step truncates the response, but that will break images.
  if (event.data.model.includes("image")) return;

  // 5. Return the WHOLE request, edited. Returning nothing leaves it untouched.
  return {
    ...request,
    generationConfig: {
      ...request.generationConfig,
      maxOutputTokens: Math.min(
        request.generationConfig?.maxOutputTokens ?? MAX_OUTPUT_TOKENS,
        MAX_OUTPUT_TOKENS,
      ),
    },
  };
});

リクエスト前関数の主な考慮すべきポイント

  • Gemini API プロバイダを指定する: event.data.request は、Gemini Developer API または Agent Platform Gemini API (formerly Vertex AI) のいずれかにできます。これらの異なる API のリクエスト オブジェクトは、異なる形式になっています。リクエスト オブジェクトを安全に操作するには、event.data.api を確認する必要があります(たとえば、それぞれ geminiV1Beta または vertexV1Beta1 と比較します)。

  • スローするとリクエストがブロックされる: HttpsError をスローすると、リクエストは拒否されます。

  • リクエスト全体を返す: 関数がリクエストを変更する場合は、変更されたリクエスト オブジェクト全体を返す必要があります。何も返さない(または undefined を返す)場合、リクエストは変更されません。

  • レイテンシをテストする: 関数の処理内容によっては、レイテンシが増加し、ユーザー エクスペリエンスに影響する可能性があります。

リクエスト後の関数(afterGenerateContent)を作成する

afterGenerateContent イベントタイプを使用すると、Firebase AI Logic プロキシが generateContent リクエストからレスポンスを受信したときに関数がトリガーされます。この関数は、レスポンスがクライアント アプリに返される前に、レスポンスに対して実行されます。この関数は、使用状況のログを記録したり、レスポンスを変更したり、レスポンスを完全にブロックしたりできます。

関数を作成する前に、次の情報を確認してください。

例

トークンの使用状況と終了理由をログに記録する post-request 関数の例を次に示します。

import { logger } from "firebase-functions";
import {
  afterGenerateContent,
  vertexV1Beta1,
  type VertexV1Beta1GenerateContentResponse,
} from "firebase-functions/v2/ai";

export const recordGenerationUsage = afterGenerateContent((event) => {
  // Optional: If you want the function to only run for a specific Gemini API provider, specify it here.
  if (event.data.api !== vertexV1Beta1) return;
  const response = event.data.response as VertexV1Beta1GenerateContentResponse;

  logger.info("Generation finished", {
    model: event.data.model,
    promptTokens: response.usageMetadata?.promptTokenCount,
    totalTokens: response.usageMetadata?.totalTokenCount,
    finishReason: response.candidates?.[0]?.finishReason,
  });

  // To leave the response untouched, return nothing.
  // To modify the response, return a modified response object here.
});

リクエスト後の関数に関する考慮すべきポイント

  • Gemini API プロバイダを指定する: event.data.response は、Gemini Developer API または Agent Platform Gemini API (formerly Vertex AI) のいずれかにできます。リクエスト オブジェクトを安全にキャストして操作するには、event.data.api を確認する必要があります(たとえば、それぞれ geminiV1Beta または vertexV1Beta1 と比較します)。

  • レイテンシをテストする: 関数の処理内容によっては、レイテンシが増加し、ユーザー エクスペリエンスに影響する可能性があります。

ステップ 3: 関数をデプロイする

関数を Firebase にデプロイすると、Firebase AI Logic サービス エージェントにこれらの関数を呼び出す権限が付与され、各関数が Firebase AI Logic トリガーとして登録されます。

  1. Firebase CLI を使用して次のように関数をデプロイします。

    firebase deploy --only functions
    
  2. デプロイ後、関数が Firebase にデプロイされていることを確認します。

    firebase functions:list
    
  3. 関数を反復処理する必要がある場合:

    プロジェクト ディレクトリ内の関数を更新してから、firebase deploy --only functions を再度実行します。

関数の実行を停止する

これらの関数のいずれかの実行を停止するには、サーバーから削除し、 Firebase AI Logic トリガーとしての登録を解除する必要があります。これを行うには、次のいずれかのオプションを指定して Firebase CLI を使用します。

  • オプション 1: 関数を暗黙的に削除する

    1. プロジェクト ディレクトリのコードベースから関数を削除します。

    2. 次の Firebase CLI コマンドを実行します。

      firebase deploy --only functions
      
  • オプション 2: 関数を明示的に削除する

    1. プロジェクト ディレクトリのコードベースから関数を削除します。

    2. 次の Firebase CLI コマンドを実行します。

      firebase functions:delete FUNCTION_NAME
      



イベントデータ リファレンス

beforeGenerateContent と afterGenerateContent の両方が、リクエストに関するコンテキストとメタデータを含む AIBlockingEvent オブジェクトを受け取ります。

最上位のリクエスト メタデータ(AIBlockingEvent)

最上位の AIBlockingEvent オブジェクトは、呼び出し元とトリガー環境に関する情報を提供します。

  • event.authType: 発信者の認証状態("app_user"、"unauthenticated"、"unknown")。
  • event.authId: 呼び出し元の Firebase Authentication UID(ログインしている場合)。
  • event.authClaims: 呼び出し元のカスタム認証クレーム(存在する場合)。
  • event.appId: リクエストを行った Firebase アプリ ID。
  • event.androidPackageName / event.iosBundleId: 呼び出し元アプリのパッケージ名またはバンドル ID(それぞれ Android プラットフォームまたは Apple プラットフォームに適用)。
  • event.data: イベント ペイロード。リクエスト前関数とリクエスト後関数で異なります。

リクエスト前のイベントデータ(beforeGenerateContent)

beforeGenerateContent 関数では、event.data に BeforeGenerateContentData オブジェクトが入力されます。

  • event.data.api: Gemini API プロバイダ: geminiV1Beta(Gemini Developer API)または vertexV1Beta1(Agent Platform Gemini API (formerly Vertex AI))。
  • event.data.model: モデルリソースの完全なパス(例: projects/{PROJECT_ID}/locations/global/publishers/google/models/gemini-3.8-flash)。
  • event.data.template: 使用されたサーバー プロンプト テンプレート(PromptTemplateInfo)に関するメタデータ(該当する場合)。
  • event.data.request: 送信リクエストのペイロード。オブジェクトのタイプとプロパティは、Gemini API プロバイダによって異なります。

リクエスト後のイベントデータ(afterGenerateContent)

afterGenerateContent 関数では、event.data に AfterGenerateContentData オブジェクトが入力されます。このオブジェクトは BeforeGenerateContentData を拡張し(api、model、template、request を提供)、モデルのレスポンスを追加します。

  • event.data.response: モデルのレスポンス ペイロード。オブジェクト タイプとプロパティは、Gemini API プロバイダによって異なります。



制限事項と動作

これらの関数を実装する際は、次の動作と制限事項に注意してください。

  • generateContent リクエストのみ: これらの関数は、Firebase AI Logic を介した Gemini API への generateContent リクエストによってのみトリガーできます。

    以下の場合、これらの関数はトリガーされず、そのリクエストに対して関数はサイレントでバイパスされます。

    • generateContentStream へのリクエストでは、これらの関数はトリガーされません。

    • Gemini Live API へのリクエストでは、これらの関数はトリガーされません。

  • クライアントサイドのコード変更が不要: これらの関数を実行するときに generateContent リクエストを使用することを確認する以外に、クライアントサイドのコードベースを変更する必要はありません。

    これらの関数はサーバーにデプロイされ、Firebase AI Logic トリガーとして登録されます。これにより、Firebase AI Logic プロキシはサーバーサイドでリクエストとレスポンスをインターセプトできます。

  • プロジェクト レベルのスコープ: Firebase プロジェクトごとに、最大 1 つの beforeGenerateContent 関数と 1 つの afterGenerateContent 関数をデプロイできます。

  • デフォルトのロケーション: これらの関数は、デフォルトで us-central1 にデプロイされます(関数のロケーションをご覧ください)。ただし、関数をデプロイする場所に関係なく、関数は global リージョンの Firebase AI Logic トリガーとして登録されます。