在请求之前和之后运行自定义服务器端脚本


您可以在应用通过 Firebase AI LogicGemini API 发送的每个请求之前和之后运行自己的自定义服务器端脚本而无需更改客户端代码。您将这些脚本实现为部署到 Cloud Functions for Firebase 的回调样式函数。

借助此功能,您可以执行以下操作:审核提示、限制令牌使用量、记录生成的内容以供分析,或编辑回答内容。

有两种类型的活动:

  • beforeGenerateContent:在请求到达 Gemini API 之前运行。该函数可以检查或修改请求,也可以通过抛出错误来完全阻止请求。

  • afterGenerateContent:在 Gemini API 发回响应后,但在将响应返回到客户端应用之前运行。该函数可以检查或修改响应、完全屏蔽响应,或者只是观察响应(例如用于日志记录或审核)。

将脚本作为函数部署到 Cloud Functions for Firebase 后,它们会注册为 Firebase AI Logic 触发器,这意味着它们会针对项目中通过 Firebase AI LogicGemini API 发出的每个 generateContent 请求运行(包括使用服务器提示模板发出的请求)。

这些函数不会由向 Gemini API 发出的非通过 Firebase AI Logic 的请求触发。

前提条件

第 1 步:为 Cloud Functions for Firebase 设置项目

如果您从未在 Firebase 项目中使用过 Cloud Functions for Firebase,请完成以下设置。

  1. 确保您的 Firebase 项目采用的是随用随付 Blaze 定价方案(使用 Cloud Functions for Firebase 的前提条件)。

  2. 安装命令行界面 (CLI):gcloud CLIFirebase CLI

  3. 向默认的 Compute Engine 服务账号授予构建函数所需的 Cloud Build Service Account 角色 (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 提供方时运行。

  • 检查提示中是否存在被屏蔽的主题,并通过抛出错误来拒绝请求。

  • 限制文本生成模型的最大输出 token 数。

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 APIAgent Platform Gemini API (formerly Vertex AI)。 这些不同 API 的请求对象具有不同的形状。为了安全地处理请求对象,您必须检查 event.data.api(例如,分别与 geminiV1BetavertexV1Beta1 进行比较)。

  • 抛出异常会阻止请求:如果您抛出 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.response 可以是 Gemini Developer APIAgent Platform Gemini API (formerly Vertex AI)。 为了安全地转换和处理请求对象,您必须检查 event.data.api(例如,分别与 geminiV1BetavertexV1Beta1 进行比较)。

  • 测试延迟时间:根据函数的功能,它可能会增加延迟时间并影响用户体验。

第 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
      
Firebase AI Logic



事件数据参考文档

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.data 会填充一个 BeforeGenerateContentData 对象:

  • event.data.apiGemini 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(提供 apimodeltemplaterequest),并添加了模型的响应:



限制和行为

实现这些函数时,请注意以下行为和限制:

  • 仅限 generateContent 请求:这些函数只能通过 Firebase AI LogicGemini API 发出的 generateContent 请求来触发。

    以下情况将不会触发这些函数,并且系统会静默绕过相应请求的函数:

    • generateContentStream 的请求将不会触发这些函数。

    • Gemini Live API 的请求将不会触发这些函数。

  • 无需更改客户端代码:除了确保在想要运行这些函数时使用 generateContent 请求之外,您无需更改客户端代码库。

    这些函数会部署到我们的服务器,并注册为 Firebase AI Logic 触发器,以便 Firebase AI Logic 代理可以拦截服务器端的请求和响应。

  • 项目级范围:每个 Firebase 项目最多可以部署一个 beforeGenerateContent 函数和一个 afterGenerateContent 函数。

  • 默认位置:这些函数默认会部署到 us-central1(了解函数的位置)。不过,无论您将函数部署到何处,该函数都将在 global 区域中注册为 Firebase AI Logic 触发器