Chạy tập lệnh tuỳ chỉnh phía máy chủ trước và sau các yêu cầu


Bạn có thể chạy tập lệnh tuỳ chỉnh phía máy chủ của riêng mình trước và sau mỗi yêu cầu mà ứng dụng của bạn gửi đến Gemini API thông qua Firebase AI Logicmà không cần thay đổi mã máy khách. Bạn triển khai các tập lệnh này dưới dạng các hàm kiểu lệnh gọi lại được triển khai cho Cloud Functions for Firebase.

Với khả năng này, bạn có thể làm những việc như kiểm duyệt câu lệnh, giới hạn mức sử dụng mã thông báo, ghi lại các nội dung được tạo để phân tích hoặc biên tập nội dung phản hồi.

Có 2 loại sự kiện:

  • beforeGenerateContent: Chạy trước khi yêu cầu đến Gemini API. Hàm này có thể kiểm tra hoặc sửa đổi yêu cầu, hoặc chặn hoàn toàn yêu cầu bằng cách gửi lỗi.

  • afterGenerateContent: Chạy sau khi phản hồi được gửi lại từ Gemini API và trước khi phản hồi được trả về cho ứng dụng khách. Hàm này có thể kiểm tra hoặc sửa đổi phản hồi, chặn hoàn toàn phản hồi hoặc chỉ quan sát phản hồi (chẳng hạn như để ghi nhật ký hoặc kiểm tra).

Sau khi tập lệnh được triển khai dưới dạng các hàm cho Cloud Functions for Firebase, chúng sẽ được đăng ký dưới dạng Firebase AI Logic trình kích hoạt. Điều này có nghĩa là chúng sẽ chạy cho mọi yêu cầu generateContent trong dự án của bạn đến Gemini API thông qua Firebase AI Logic (bao gồm cả các yêu cầu được thực hiện bằng mẫu lời nhắc của máy chủ).

Các hàm này không được kích hoạt bởi các yêu cầu gửi đến Gemini API không thông qua Firebase AI Logic.

Điều kiện tiên quyết

Bước 1: Thiết lập dự án cho Cloud Functions for Firebase

Nếu bạn chưa từng sử dụng Cloud Functions for Firebase trong dự án Firebase, hãy hoàn tất quy trình thiết lập sau.

  1. Đảm bảo dự án Firebase của bạn sử dụng gói giá Blaze (trả tiền theo mức dùng) (bắt buộc để sử dụng Cloud Functions for Firebase).

  2. Cài đặt giao diện dòng lệnh (CLI): gcloud CLICLI Firebase

  3. Cấp cho tài khoản dịch vụ mặc định của Compute Engine vai trò Tài khoản dịch vụ Cloud Build (roles/cloudbuild.builds.builder) cần thiết để tạo hàm. Chạy lệnh gcloud CLI sau đây:

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
      --role="roles/cloudbuild.builds.builder"
    
  4. Khởi động Cloud Functions for Firebase trong dự án Firebase của bạn:

    1. Chạy lệnh CLI Firebase sau:

      firebase init functions
      
    2. Khi được nhắc, hãy chọn TypeScript.

    3. Đảm bảo rằng firebase-functions trong functions/package.json là phiên bản 6.3.0 trở lên. Sau đây là cách kiểm tra phiên bản:

      npm --prefix functions list firebase-functions
      

Bước 2: Viết các hàm

Viết một hàm tiền yêu cầu (beforeGenerateContent) Viết một hàm hậu yêu cầu (afterGenerateContent)

Viết hàm tiền yêu cầu (beforeGenerateContent)

Với loại sự kiện beforeGenerateContent, hàm sẽ được kích hoạt khi proxy Firebase AI Logic nhận được một yêu cầu generateContent. Hàm này chạy theo yêu cầu trước khi yêu cầu được gửi đến Gemini API. Hàm này có thể sửa đổi hoặc chặn hoàn toàn yêu cầu.

Đảm bảo bạn xem xét thông tin sau đây trước khi viết hàm:

Ví dụ

Dưới đây là ví dụ về hàmtiền yêu cầu thực hiện những việc sau:

  • Chỉ định rằng hàm chỉ chạy khi yêu cầu là dành cho một nhà cung cấp Gemini API cụ thể.

  • Kiểm tra câu lệnh để tìm các chủ đề bị chặn và từ chối yêu cầu bằng cách đưa ra lỗi.

  • Giới hạn số lượng mã thông báo đầu ra tối đa cho các mô hình tạo văn bản.

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

Những điểm chính cần cân nhắc đối với các hàm trước yêu cầu

  • Chỉ định nhà cung cấp Gemini API: event.data.request có thể dành cho Gemini Developer API hoặc Agent Platform Gemini API (formerly Vertex AI). Các đối tượng yêu cầu cho những API khác nhau này có hình dạng khác nhau. Để làm việc an toàn với đối tượng yêu cầu, bạn phải kiểm tra event.data.api (ví dụ: so sánh với geminiV1Beta hoặc vertexV1Beta1, tương ứng).

  • Thao tác ném sẽ chặn yêu cầu: Nếu bạn ném HttpsError, yêu cầu sẽ bị từ chối.

  • Trả về toàn bộ yêu cầu: Nếu hàm của bạn sửa đổi yêu cầu, thì bạn phải trả về đối tượng yêu cầu đã sửa đổi hoàn chỉnh. Nếu không trả về giá trị nào (hoặc undefined), yêu cầu sẽ không thay đổi.

  • Kiểm tra độ trễ: Tuỳ thuộc vào chức năng của bạn, độ trễ có thể tăng lên và ảnh hưởng đến trải nghiệm người dùng.

Viết hàm sau yêu cầu (afterGenerateContent)

Với loại sự kiện afterGenerateContent, hàm sẽ được kích hoạt khi proxy Firebase AI Logic nhận được phản hồi từ một yêu cầu generateContent. Hàm chạy theo phản hồi trước khi phản hồi được trả về cho ứng dụng khách. Hàm này có thể ghi nhật ký việc sử dụng, sửa đổi phản hồi hoặc chặn hoàn toàn phản hồi.

Đảm bảo bạn xem xét thông tin sau đây trước khi viết hàm:

Ví dụ

Sau đây là ví dụ về hàm post-request (sau yêu cầu) ghi nhật ký việc sử dụng mã thông báo và lý do kết thúc:

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

Những điểm chính cần lưu ý đối với các hàm sau yêu cầu

  • Chỉ định nhà cung cấp Gemini API: event.data.response có thể dành cho Gemini Developer API hoặc Agent Platform Gemini API (formerly Vertex AI). Để truyền và làm việc với đối tượng yêu cầu một cách an toàn, bạn phải kiểm tra event.data.api (ví dụ: so sánh với geminiV1Beta hoặc vertexV1Beta1, tương ứng).

  • Kiểm tra độ trễ: Tuỳ thuộc vào chức năng của bạn, độ trễ có thể tăng lên và ảnh hưởng đến trải nghiệm người dùng.

Bước 3: Triển khai các hàm

Việc triển khai các hàm của bạn cho Firebase sẽ cấp cho tác nhân dịch vụ Firebase AI Logic quyền gọi các hàm này và đăng ký từng hàm dưới dạng một trình kích hoạt Firebase AI Logic.

  1. Triển khai các hàm bằng giao diện dòng lệnh Firebase:

    firebase deploy --only functions
    
  2. Sau khi triển khai, hãy xác nhận rằng các hàm của bạn đã được triển khai đến Firebase:

    firebase functions:list
    
  3. Nếu bạn cần lặp lại hàm:

    Cập nhật hàm trong thư mục dự án của bạn, sau đó chạy lại firebase deploy --only functions.

Dừng chạy một hàm

Để ngăn một trong các hàm này chạy, bạn phải xoá hàm đó khỏi máy chủ của chúng tôi huỷ đăng ký hàm đó dưới dạng một trình kích hoạt Firebase AI Logic. Bạn có thể thực hiện việc này bằng cách sử dụng Firebase CLI với một trong hai lựa chọn sau:

  • Lựa chọn 1: Xoá hàm một cách gián tiếp

    1. Xoá hàm khỏi toàn bộ mã nguồn của thư mục dự án.

    2. Chạy lệnh CLI Firebase sau:

      firebase deploy --only functions
      
  • Lựa chọn 2: Xoá hàm một cách rõ ràng

    1. Xoá hàm khỏi toàn bộ mã nguồn của thư mục dự án.

    2. Chạy lệnh CLI Firebase sau:

      firebase functions:delete FUNCTION_NAME
      



Tài liệu tham khảo về dữ liệu sự kiện

Cả beforeGenerateContentafterGenerateContent đều nhận được một đối tượng AIBlockingEvent chứa bối cảnh và siêu dữ liệu về yêu cầu.

Siêu dữ liệu yêu cầu cấp cao nhất (AIBlockingEvent)

Đối tượng AIBlockingEvent cấp cao nhất cung cấp thông tin về người gọi và môi trường kích hoạt:

  • event.authType: Trạng thái xác thực cho người gọi: "app_user", "unauthenticated" hoặc "unknown".
  • event.authId: UID Xác thực Firebase của phương thức gọi, nếu đã đăng nhập.
  • event.authClaims: Các xác nhận quyền sở hữu uỷ quyền tuỳ chỉnh của người gọi (nếu có).
  • event.appId: Mã ứng dụng Firebase đã đưa ra yêu cầu.
  • event.androidPackageName / event.iosBundleId: Tên gói hoặc mã nhận dạng gói của ứng dụng gọi điện (tương ứng áp dụng cho nền tảng Android hoặc Apple).
  • event.data: Tải trọng sự kiện, khác nhau giữa các hàm trước yêu cầu và sau yêu cầu:

Dữ liệu sự kiện trước yêu cầu (beforeGenerateContent)

Trong hàm beforeGenerateContent, event.data được điền bằng một đối tượng BeforeGenerateContentData:

  • event.data.api: Nhà cung cấp Gemini API: geminiV1Beta (Gemini Developer API) hoặc vertexV1Beta1 (Agent Platform Gemini API (formerly Vertex AI)).
  • event.data.model: Đường dẫn đầy đủ đến tài nguyên mô hình (ví dụ: projects/{PROJECT_ID}/locations/global/publishers/google/models/gemini-3.8-flash).
  • event.data.template: Siêu dữ liệu về mẫu câu lệnh phía máy chủ được dùng (PromptTemplateInfo), nếu có.
  • event.data.request: Tải trọng yêu cầu đi. Loại đối tượng và các thuộc tính phụ thuộc vào nhà cung cấp Gemini API:

Dữ liệu sự kiện sau yêu cầu (afterGenerateContent)

Trong hàm afterGenerateContent, event.data được điền bằng một đối tượng AfterGenerateContentData. Đối tượng này mở rộng BeforeGenerateContentData (cung cấp api, model, templaterequest) và thêm phản hồi của mô hình:



Hạn chế và hành vi

Khi triển khai các hàm này, hãy lưu ý những hành vi và hạn chế sau:

  • Chỉ yêu cầu generateContent: Các hàm này chỉ có thể được kích hoạt bằng các yêu cầu generateContent đến Gemini API thông qua Firebase AI Logic.

    Sau đây sẽ không kích hoạt các hàm này và các hàm sẽ được bỏ qua một cách âm thầm cho yêu cầu đó:

    • Các yêu cầu đến generateContentStream sẽ không kích hoạt các hàm này.

    • Các yêu cầu đối với Gemini Live API sẽ không kích hoạt các hàm này.

  • Không cần thay đổi mã phía máy khách: Ngoài việc đảm bảo rằng bạn sử dụng các yêu cầu generateContent khi muốn chạy các hàm này, bạn không cần thay đổi gì trong toàn bộ mã nguồn phía máy khách.

    Các hàm này được triển khai đến máy chủ của chúng tôi và được đăng ký dưới dạng trình kích hoạt Firebase AI Logic để proxy Firebase AI Logic có thể chặn các yêu cầu và phản hồi phía máy chủ.

  • Phạm vi ở cấp dự án: Bạn có thể triển khai tối đa một hàm beforeGenerateContent và một hàm afterGenerateContent cho mỗi dự án Firebase.

  • Vị trí mặc định: Theo mặc định, các hàm này sẽ được triển khai đến us-central1 (tìm hiểu về vị trí cho các hàm). Tuy nhiên, hàm sẽ được đăng ký làm một trình kích hoạt Firebase AI Logic trong khu vực global bất kể bạn triển khai hàm ở đâu.