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


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

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

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

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

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

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

これらの関数は、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 CLIFirebase CLI をインストールします。

  3. 関数をビルドするために必要な Cloud Build サービス アカウントのロール(roles/cloudbuild.builds.builder)をデフォルトの Compute サービス アカウントに付与します。次の 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.jsonfirebase-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.requestGemini Developer API または Agent Platform Gemini API (formerly Vertex AI) のいずれかにできます。これらの異なる API のリクエスト オブジェクトは形状が異なります。リクエスト オブジェクトを安全に操作するには、event.data.api を確認する必要があります(たとえば、それぞれ geminiV1Beta または vertexV1Beta1 と比較します)。

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

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

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

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

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

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

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

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.responseGemini 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
      



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

beforeGenerateContentafterGenerateContent はどちらも、リクエストに関するコンテキストとメタデータを含む 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.dataBeforeGenerateContentData オブジェクトが設定されます。

  • event.data.api: Gemini API プロバイダ: geminiV1BetaGemini Developer API)または vertexV1Beta1Agent 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.dataAfterGenerateContentData オブジェクトが入力されます。このオブジェクトは BeforeGenerateContentData を拡張し(apimodeltemplaterequest を提供)、モデルのレスポンスを追加します。

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



制限事項と動作

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

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

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

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

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

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

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

  • プロジェクト レベルのスコープ: Firebase プロジェクトごとにデプロイできる beforeGenerateContent 関数と afterGenerateContent 関数はそれぞれ 1 つまでです。

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