使用 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 定义流程

请改用 Firebase 插件的 onFlow() 函数,而不是使用 Genkit.defineFlow() 定义流程。使用此函数可将流程逻辑封装在 Cloud Functions 请求处理程序中,类似于 onCall

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

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

请注意,onFlow 不是 Genkit 的方法,而是一个将 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 密钥,该密钥可授予对可能按量计费的服务的访问权限。

    如需详细了解如何管理 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 = 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 User 角色。

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

设置 CORS 政策

如果您将通过 Web 应用访问您的流程(您将在试用已部署的流程部分中执行此操作),请在 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 或类似工具访问已部署的端点。请继续阅读下一部分,了解如何安全地访问该流程。

可选:试用已部署的流程

如需试用流程端点,您可以部署以下最小示例 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 账号登录,然后您才能发起端点请求。

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

您可以在开发者界面中运行使用 onFlow 定义的流程,方法与运行使用 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 中的轨迹,您可以前往“检查”标签页,然后切换“开发/生产”开关。切换到“prod”后,它将从 Firestore 加载轨迹。