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

Cloud Functions cho Firebase có một phương thức onCallGenkit cho phép bạn nhanh chóng tạo một hàm có thể gọi bằng một hành động Genkit (ví dụ: Flow). Bạn có thể gọi các hàm này bằng cách sử dụng genkit/beta/client hoặc SDK ứng dụng chức năng. Các hàm này sẽ tự động thêm thông tin xác thực.

Trước khi bắt đầu

  • 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 của 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. Cài đặt Giao diện dòng lệnh (CLI) của Firebase.

  4. Đă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
  5. Tạo thư mục dự án mới:

    export PROJECT_ROOT=~/tmp/genkit-firebase-project1
    mkdir -p $PROJECT_ROOT
  6. 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 đã quyết định viết các hàm bằng 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. Gói Flow trong onCallGenkit

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 gói các luồng đó trong onCallGenkit. Phương thức này có tất cả các tính năng của onCall thông thường. API này tự động hỗ trợ cả phản hồi truyền trực tuyến và JSON.

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

Bạn có thể hiển thị luồng này dưới dạng một hàm có thể gọi bằng onCallGenkit:

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

export generatePoem = onCallGenkit(generatePoemFlow);

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

Tất cả các luồng đã triển khai, cho dù đã triển khai cho Firebase hay chưa, đề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 thông số authPolicy của onCallGenkit:

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

Mẫu này sử dụng một hàm thủ công làm chính sách xác thực. Ngoài ra, thư viện https sẽ xuất các trình trợ giúp signedIn()hasClaim(). Dưới đây là cùng một mã sử dụng một trong các trình trợ giúp đó:

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

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

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 đều 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, khoá này 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 = onCallGenkit({
      secrets: [googleAIapiKey]
    }, generatePoemFlow);
    

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.

Thêm tính năng thực thi Kiểm tra ứng dụng

Firebase App Check sử dụng cơ chế chứng thực tích hợp sẵn để xác minh rằng API của bạn chỉ được ứng dụng gọi. onCallGenkit hỗ trợ việc thực thi quy trình Kiểm tra ứng dụng theo cách khai báo.

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

Đặt chính sách CORS

Theo mặc định, các hàm có thể gọi cho phép mọi miền gọi hàm của bạn. Nếu bạn muốn tuỳ chỉnh các miền có thể thực hiện việc này, hãy sử dụng tuỳ chọn cors. Với phương thức xác thực thích hợp (đặc biệt là tính năng Kiểm tra ứng dụng), bạn thường không cần sử dụng CORS.

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

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 có thể triển khai của bạn sẽ trông giống như ví dụ sau:

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. Triển khai quy trình cho Firebase

Sau khi xác định các luồng bằng onCallGenkit, bạn có thể triển khai các luồng đó giố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 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. Phần tiếp theo giải thích 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ử nghiệm điểm cuối của flow, bạn có thể triển khai ứng dụng web mẫu tối giản 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 trình cung cấp Google được sử dụng trong ví dụ này.

  3. Trong thư mục dự án, hãy thiết lập tính năng 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 onCallGenkit 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

Lệnh này chạy mã của bạn trong trình mô phỏng và chạy khung Genkit ở chế độ phát triển. Thao tác 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 dành cho nhà phát triển).

Để 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ẻ Inspect (Kiểm tra) rồi bật/tắt nút chuyển Dev/Prod (Nhà phát triển/Sản phẩm). Khi chuyển sang prod, tuỳ chọn này sẽ tải các dấu vết từ firestore.