使用 Cloud Functions for Firebase 部署数据流

Cloud Functions for Firebase 提供了 onCallGenkit 方法,可让您使用 Genkit 操作(例如流程)快速创建可调用函数。您可以使用 genkit/beta/clientFunctions 客户端 SDK 调用这些函数,后者会自动添加身份验证信息。

准备工作

  • 您应该熟悉 Genkit 的流程概念以及如何编写流程。本页中的说明假定您已定义要部署的一些流程。
  • 如果您之前使用过适用于 Firebase 的 Cloud Functions,将会很有帮助,但不是必需的。

1. 设置 Firebase 项目

如果您还没有设置 TypeScript Cloud Functions 的 Firebase 项目,请按以下步骤操作:

  1. 使用 Firebase 控制台创建新的 Firebase 项目,或选择现有项目。

  2. 将项目升级为 Blaze 方案,这是部署 Cloud Functions 函数的必备条件。

  3. 安装 Firebase CLI

  4. 使用 Firebase CLI 登录:

    firebase login
    firebase login --reauth # alternative, if necessary
    firebase login --no-localhost # if running in a remote shell
  5. 创建新的项目目录:

    export PROJECT_ROOT=~/tmp/genkit-firebase-project1
    mkdir -p $PROJECT_ROOT
  6. 在目录中初始化 Firebase 项目:

    cd $PROJECT_ROOT
    firebase init genkit

    本页面的其余部分假定您已决定使用 TypeScript 编写函数,但如果您使用的是 JavaScript,也可以部署 Genkit 流程。

2. 在 onCallGenkit 中封装流

使用 Cloud Functions 设置 Firebase 项目后,您可以在项目的 functions/src 目录中复制或编写流程定义,并在 index.ts 中导出这些定义。

为了能够部署流程,您需要将其封装在 onCallGenkit 中。此方法具有普通 onCall 的所有功能。它自动支持流式传输和 JSON 响应。

假设您有以下流程:

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;
  }
);

您可以使用 onCallGenkit 将此流程公开为可调用函数:

import { onCallGenkit } from 'firebase-functions/https';

export generatePoem = onCallGenkit(generatePoemFlow);

定义授权政策

所有已部署的流程(无论是否部署到 Firebase)都应具有授权政策;如果没有授权政策,任何人都可以调用可能非常昂贵的生成式 AI 流程。如需定义授权政策,请使用 onCallGenkitauthPolicy 参数:

export const generatePoem = onCallGenkit({
  authPolicy: (auth) => auth?.token?.email_verified,
}, generatePoemFlow);

此示例使用手动函数作为其身份验证政策。此外,https 库还会导出 signedIn()hasClaim() 帮助程序。下面是使用其中一个帮助程序的相同代码:

import { hasClaim } from 'firebase-functions/https';

export const generatePoem = onCallGenkit({
  authPolicy: hasClaim('email_verified'),
}, generatePoemFlow);

向已部署的流程提供 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 密钥,该密钥可授予对可能按量计费的服务的访问权限。

    如需详细了解如何管理 Secret,请参阅存储和访问敏感的配置信息

  4. 修改 src/index.ts,并将以下代码添加到现有 import 之后:

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

    然后,在流程定义中声明 Cloud Functions 函数需要访问此 Secret 值:

    export const generatePoem = onCallGenkit({
      secrets: [googleAIapiKey]
    }, generatePoemFlow);
    

现在,当您部署此函数时,您的 API 密钥会存储在 Cloud Secret Manager 中,并可从 Cloud Functions 环境中使用。

Gemini (Vertex AI)

  1. 在 Cloud 控制台中,为您的 Firebase 项目启用 Vertex AI API

  2. IAM 页面上,确保为默认计算服务账号授予 Vertex AI User 角色。

在本教程中,您只需要为模型提供方设置 Secret,但一般来说,您必须为流水线使用的每项服务执行类似的操作。

添加 App Check 强制执行

Firebase App Check 使用内置的认证机制来验证您的 API 是否仅由您的应用调用。onCallGenkit 支持以声明方式强制执行 App Check 政策。

export const generatePoem = onCallGenkit({
  enforceAppCheck: true,
  // Optional. Makes App Check tokens only usable once. This adds extra security
  // at the expense of slowing down your app to generate a token for every API
  // call
  consumeAppCheckToken: true,
}, generatePoemFlow);

设置 CORS 政策

默认情况下,Callable 函数允许任何网域调用您的函数。如果您想自定义可以执行此操作的网域,请使用 cors 选项。通过适当的身份验证(尤其是 App Check),通常不需要 CORS。

export const generatePoem = onCallGenkit({
  cors: 'mydomain.com',
}, generatePoemFlow);

完整示例

完成上述所有更改后,您的可部署流程应如下例所示:

import { genkit } from 'genkit';
import { onCallGenkit, hasClaim } from 'firebase-functions/https';
import { defineSecret } from 'firebase-functions/params';

const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");

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;
});

export const generateFlow = onCallGenkit({
  secrets: [apiKey],
  authPolicy: hasClaim("email_verified"),
  enforceAppCheck: true,
}, generatePoemFlow);

3. 将流程部署到 Firebase

使用 onCallGenkit 定义流程后,您可以像部署其他 Cloud Functions 函数一样部署这些流程:

cd $PROJECT_ROOT
firebase deploy --only functions

现在,您已将该流程部署为 Cloud Functions 函数!但是,由于该流程的授权政策,您无法使用 curl 或类似工具访问已部署的端点。下一部分将介绍如何安全地访问该流程。

可选:试用已部署的流程

如需试用流程端点,您可以部署以下最小示例 Web 应用:

  1. 在 Firebase 控制台的项目设置部分中,添加一个新的 Web 应用,并选择同时设置 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. 部署 Web 应用和 Cloud Functions 函数:

    cd $PROJECT_ROOT
    firebase deploy

访问 deploy 命令输出的网址,打开 Web 应用。该应用要求您使用 Google 账号登录,然后您才能发起端点请求。

可选:在开发者界面中运行流程

您可以在开发者界面中运行使用 onCallGenkit 定义的流程,就像运行使用 defineFlow 定义的流程一样,因此在部署和开发之间无需在二者之间切换。

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

cd $PROJECT_ROOT/functions
npm run genkit:start

现在,您可以导航到 genkit start 命令输出的网址进行访问。

可选:使用 Firebase Local Emulator Suite 进行开发

Firebase 提供了一套适用于本地开发的模拟器,您可以将其与 Genkit 搭配使用。

如需将 Genkit 开发者界面与 Firebase Emulator Suite 搭配使用,请按如下方式启动 Firebase 模拟器:

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

此命令会在模拟器中运行您的代码,并在开发模式下运行 Genkit 框架。这会启动并公开 Genkit 反射 API(但不会公开开发者界面)。

如需在开发者界面中查看 Firestore 中的轨迹,您可以前往 Inspect 标签页,然后切换 Dev/Prod 开关。切换到prod 后,它会从 Firestore 加载轨迹。