Triển khai luồng bằng Cloud Functions cho Firebase

Genkit bao gồm một trình bổ trợ giúp bạn triển khai luồng đến Cloud Functions cho Firebase. Sau khi triển khai, luồng sẽ có sẵn dưới dạng điểm cuối HTTPS và có thể truy cập dưới dạng hàm có thể gọi bằng cách sử dụng thư viện máy khách của Cloud Functions.

Trước khi bắt đầu

  • Cài đặt Giao diện dòng lệnh (CLI) của Firebase.
  • Bạn nên nắm rõ khái niệm về luồng của Genkit và cách viết các luồng đó. Hướng dẫn trên trang này giả định rằng bạn đã xác định một số luồng mà bạn muốn triển khai.
  • Việc này sẽ hữu ích nhưng không bắt buộc nếu bạn đã từng sử dụng Hàm trên đám mây cho Firebase.

1. Thiết lập dự án Firebase

Nếu bạn chưa có dự án Firebase đã thiết lập Hàm trên đám mây TypeScript, hãy làm theo các bước sau:

  1. Tạo dự án Firebase mới bằng bảng điều khiển Firebase hoặc chọn một dự án hiện có.

  2. Nâng cấp dự án lên gói Blaze (bắt buộc để triển khai Hàm trên đám mây).

  3. Đăng nhập bằng Firebase CLI:

    firebase login
    firebase login --reauth # alternative, if necessary
    firebase login --no-localhost # if running in a remote shell
  4. Tạo thư mục dự án mới:

    export PROJECT_ROOT=~/tmp/genkit-firebase-project1
    mkdir -p $PROJECT_ROOT
  5. Khởi chạy dự án Firebase trong thư mục:

    cd $PROJECT_ROOT
    firebase init genkit

    Phần còn lại của trang này giả định rằng bạn đã chọn viết các hàm trong TypeScript, nhưng bạn cũng có thể triển khai các luồng Genkit nếu đang sử dụng JavaScript.

2. Cập nhật định nghĩa về quy trình

Sau khi thiết lập dự án Firebase bằng Cloud Functions, bạn có thể sao chép hoặc viết định nghĩa luồng trong thư mục functions/src của dự án và xuất các định nghĩa đó trong index.ts.

Để có thể triển khai luồng, bạn cần thực hiện một số thay đổi nhỏ đối với cách xác định luồng. Logic cốt lõi sẽ vẫn như vậy, nhưng bạn sẽ thêm một số thông tin bổ sung để có thể triển khai các logic đó một cách suôn sẻ và an toàn hơn sau khi triển khai.

Giả sử bạn có quy trình sau:

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

Các phần sau đây mô tả những thay đổi bạn cần thực hiện trước khi có thể triển khai ứng dụng.

Xác định luồng bằng onFlow

Thay vì xác định luồng bằng Genkit.defineFlow(), hãy sử dụng hàm onFlow() của trình bổ trợ Firebase. Việc sử dụng hàm này sẽ gói logic flow của bạn trong trình xử lý yêu cầu của Cloud Functions, tương tự như onCall.

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

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

Xin lưu ý rằng onFlow không phải là phương thức của Genkit, mà là một hàm lấy bản sao Genkit làm tham số đầu tiên. Nếu không, cú pháp sẽ tương tự như defineFlow.

Xác định chính sách uỷ quyền

Tất cả các luồng đã triển khai, dù có được triển khai cho Firebase hay không, đều phải có chính sách uỷ quyền; nếu không, bất kỳ ai cũng có thể gọi các luồng AI tạo sinh có thể tốn kém của bạn. Để xác định chính sách uỷ quyền, hãy sử dụng tham số authPolicy trong định nghĩa luồng:

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) => {
    // ...
  }
);

Chính sách này sử dụng trình trợ giúp firebaseAuth() để chỉ cho phép những người dùng đã đăng ký ứng dụng của bạn có địa chỉ email đã xác minh mới có quyền truy cập. Ở phía máy khách, bạn cần đặt tiêu đề Authorization: Bearer thành mã thông báo mã nhận dạng Firebase đáp ứng chính sách của bạn. SDK ứng dụng của Cloud Functions cung cấp các phương thức hàm có thể gọi để tự động hoá việc này; hãy xem phần Thử quy trình đã triển khai để biết ví dụ.

Cung cấp thông tin xác thực API cho các flow đã triển khai

Sau khi triển khai, các luồng của bạn cần có một số cách để xác thực với mọi dịch vụ từ xa mà chúng dựa vào. Hầu hết các luồng sẽ cần ít nhất thông tin xác thực để truy cập vào dịch vụ API mô hình mà chúng sử dụng.

Đối với ví dụ này, hãy làm theo một trong những cách sau, tuỳ thuộc vào nhà cung cấp mô hình mà bạn chọn:

Gemini (AI của Google)

  1. Đảm bảo rằng công nghệ AI của Google có ở khu vực của bạn.

  2. Tạo khoá API cho Gemini API bằng Google AI Studio.

  3. Lưu trữ khoá API trong Trình quản lý bí mật trên đám mây:

    firebase functions:secrets:set GOOGLE_GENAI_API_KEY

    Bước này rất quan trọng để tránh vô tình rò rỉ khoá API cấp quyền truy cập vào một dịch vụ có thể tính phí.

    Hãy xem phần Lưu trữ và truy cập thông tin cấu hình nhạy cảm để biết thêm thông tin về cách quản lý thông tin bảo mật.

  4. Chỉnh sửa src/index.ts và thêm nội dung sau vào phần nhập hiện có:

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

    Sau đó, trong phần khai báo flow, hãy khai báo rằng hàm trên đám mây cần quyền truy cập vào giá trị bí mật này:

    export const generatePoem = onFlow(
      {
        name: "generatePoem",
        // ...
        httpsOptions: {
          secrets: [googleAIapiKey],  // Add this line.
        },
      },
      async (subject) => {
        // ...
      }
    );
    

Giờ đây, khi bạn triển khai hàm này, khoá API sẽ được lưu trữ trong Trình quản lý khoá trên đám mây và có sẵn trong môi trường của Cloud Functions.

Gemini (Vertex AI)

  1. Trong Cloud Console, hãy Bật API Vertex AI cho dự án Firebase của bạn.

  2. Trên trang IAM, hãy đảm bảo rằng Tài khoản dịch vụ điện toán mặc định được cấp vai trò Người dùng Vertex AI.

Bí mật duy nhất bạn cần thiết lập cho hướng dẫn này là dành cho nhà cung cấp mô hình, nhưng nói chung, bạn phải làm tương tự cho từng dịch vụ mà luồng của bạn sử dụng.

Đặt chính sách CORS

Nếu bạn sẽ truy cập vào flow từ một ứng dụng web (bạn sẽ thực hiện việc này trong phần Thử flow đã triển khai), trong tham số httpsOptions, hãy đặt chính sách CORS:

export const generatePoem = onFlow(
  ai,
  {
    name: "generatePoem",
    // ...
    httpsOptions: {
      cors: '*',
    },
  },
  async (subject: string) => {
    // ...
  }
);

Bạn có thể muốn áp dụng chính sách hạn chế hơn cho các ứng dụng phát hành công khai, nhưng chính sách này sẽ phù hợp với hướng dẫn này.

Ví dụ hoàn chỉnh

Sau khi bạn thực hiện tất cả các thay đổi được mô tả ở trên, quy trình triển khai của bạn sẽ có dạng như ví dụ sau:

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. Triển khai flow đến Firebase

Sau khi xác định các luồng bằng onFlow, bạn có thể triển khai các luồng đó như cách triển khai các Hàm trên đám mây khác:

cd $PROJECT_ROOT
firebase deploy --only functions

Bây giờ, bạn đã triển khai flow dưới dạng một Hàm trên đám mây! Tuy nhiên, bạn sẽ không thể truy cập vào điểm cuối đã triển khai bằng curl hoặc tương tự, do chính sách uỷ quyền của luồng. Hãy chuyển sang phần tiếp theo để tìm hiểu cách truy cập vào luồng một cách an toàn.

Không bắt buộc: Thử quy trình đã triển khai

Để thử điểm cuối của flow, bạn có thể triển khai ứng dụng web ví dụ tối thiểu sau:

  1. Trong phần Cài đặt dự án của bảng điều khiển Firebase, hãy thêm một ứng dụng web mới, chọn tuỳ chọn để thiết lập tính năng Lưu trữ.

  2. Trong phần Xác thực của bảng điều khiển Firebase, hãy bật nhà cung cấp Google mà bạn sẽ sử dụng trong ví dụ này.

  3. Trong thư mục dự án, hãy thiết lập dịch vụ Lưu trữ Firebase, nơi bạn sẽ triển khai ứng dụng mẫu:

    cd $PROJECT_ROOT
    firebase init hosting

    Chấp nhận các giá trị mặc định cho tất cả lời nhắc.

  4. Thay thế public/index.html bằng nội dung sau:

    <!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. Triển khai ứng dụng web và Hàm trên đám mây:

    cd $PROJECT_ROOT
    firebase deploy

Mở ứng dụng web bằng cách truy cập vào URL do lệnh deploy in ra. Ứng dụng yêu cầu bạn đăng nhập bằng Tài khoản Google, sau đó bạn có thể bắt đầu các yêu cầu điểm cuối.

Không bắt buộc: Chạy luồng trong giao diện người dùng dành cho nhà phát triển

Bạn có thể chạy các luồng được xác định bằng onFlow trong giao diện người dùng dành cho nhà phát triển, giống hệt như cách bạn chạy các luồng được xác định bằng defineFlow, vì vậy, bạn không cần phải chuyển đổi giữa hai luồng này trong quá trình triển khai và phát triển.

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

hoặc

cd $PROJECT_ROOT/functions
npm run genkit:start

Bây giờ, bạn có thể chuyển đến URL do lệnh genkit start in ra để truy cập.

Không bắt buộc: Phát triển bằng Bộ công cụ mô phỏng trên thiết bị của Firebase

Firebase cung cấp một bộ trình mô phỏng để phát triển cục bộ mà bạn có thể sử dụng với Genkit.

Để sử dụng giao diện người dùng Genkit Dev với Bộ trình mô phỏng Firebase, hãy khởi động trình mô phỏng Firebase như sau:

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

Thao tác này sẽ chạy mã của bạn trong trình mô phỏng và chạy khung Genkit ở chế độ phát triển, chế độ này sẽ khởi chạy và hiển thị API phản chiếu Genkit (nhưng không phải giao diện người dùng Dev).

Để xem dấu vết từ Firestore trong Giao diện người dùng dành cho nhà phát triển, bạn có thể chuyển đến thẻ Kiểm tra và bật/tắt nút chuyển "Dev/Prod" (Phát triển/Sản xuất). Khi chuyển sang "prod", chế độ này sẽ tải dấu vết từ firestore.