הרצה של סקריפטים מותאמים אישית בצד השרת לפני ואחרי בקשות


אתם יכולים להריץ סקריפטים מותאמים אישית בצד השרת לפני ואחרי כל בקשה שהאפליקציה שולחת אל Gemini API דרך Firebase AI Logicבלי לשנות את קוד הלקוח. מטמיעים את הסקריפטים האלה כפונקציות בסגנון קריאה חוזרת (callback) שמוצבות ב-Cloud Functions for Firebase.

היכולת הזו מאפשרת לכם לבצע פעולות כמו ניהול הנחיות, הגבלת השימוש באסימונים, רישום של יצירות לצורך ניתוח או צנזורה של תוכן בתשובות.

יש שני סוגים של אירועים:

  • beforeGenerateContent: מופעל לפני שבקשה מגיעה אל Gemini API. הפונקציה יכולה לבדוק או לשנות את הבקשה, או לחסום את הבקשה לחלוטין על ידי הפקת שגיאה.

  • afterGenerateContent: הפונקציה מופעלת אחרי שהתגובה נשלחת חזרה מ-Gemini API ולפני שהיא מוחזרת לאפליקציית הלקוח. הפונקציה יכולה לבדוק או לשנות את התגובה, לחסום את התגובה לחלוטין או רק לצפות בה (למשל לצורך רישום ביומן או ביקורת).

אחרי שפורסים את הסקריפטים כפונקציות ב-Cloud Functions for Firebase, הם נרשמים כטריגרים של Firebase AI Logic, כלומר הם יפעלו לכל בקשה של generateContent בפרויקט אל Gemini API דרך Firebase AI Logic (כולל בקשות שנוצרו באמצעות תבניות פרומפט של השרת).

הפונקציות האלה לא מופעלות על ידי בקשות שנשלחות אל Gemini API שלא דרך Firebase AI Logic.

דרישות מוקדמות

שלב 1: הגדרת הפרויקט ל-Cloud Functions for Firebase

אם אף פעם לא השתמשתם ב-Cloud Functions for Firebase בפרויקט Firebase, צריך להשלים את ההגדרה הבאה.

  1. חשוב לוודא שפרויקט Firebase שלכם מוגדר למינוי Blaze בתשלום לפי שימוש (נדרש כדי להשתמש ב-Cloud Functions for Firebase).

  2. מתקינים ממשקי שורת פקודה (CLI): ‫gcloud CLI ו- ‫Firebase CLI

  3. נותנים לחשבון השירות שמוגדר כברירת מחדל ב-Compute את התפקיד Cloud Build Service Account‏ (roles/cloudbuild.builds.builder) שנדרש כדי לבנות את הפונקציה. מריצים את הפקודה הבאה:gcloud CLI

    gcloud projects add-iam-policy-binding PROJECT_ID \
      --member="serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com" \
      --role="roles/cloudbuild.builds.builder"
    
  4. מאתחלים את Cloud Functions for Firebase בפרויקט Firebase:

    1. מריצים את הפקודה הבאה ב-CLI:Firebase

      firebase init functions
      
    2. כשמופיעה בקשה, בוחרים באפשרות TypeScript.

    3. מוודאים שגרסת firebase-functions ב-functions/package.json היא 6.3.0 ואילך. כך בודקים את הגרסה:

      npm --prefix functions list firebase-functions
      

שלב 2: כותבים את הפונקציות

כתיבת פונקציה לפני בקשה (beforeGenerateContent) כתיבת פונקציה אחרי בקשה (afterGenerateContent)

כתיבת פונקציה לפני בקשה (beforeGenerateContent)

עם סוג האירוע beforeGenerateContent, הפונקציה מופעלת כשה-proxy של Firebase AI Logic מקבל בקשת generateContent. הפונקציה מופעלת על הבקשה לפני שהבקשה נשלחת אל Gemini API. הפונקציה יכולה לשנות את הבקשה או לחסום אותה לחלוטין.

לפני שכותבים את הפונקציה, חשוב לעיין במידע הבא:

דוגמה

הנה דוגמה לפונקציית pre-request שמבצעת את הפעולות הבאות:

  • מציינים שהפונקציה תפעל רק אם הבקשה היא לספק Gemini API ספציפי.

  • בודק את ההנחיה כדי לראות אם יש בה נושאים חסומים, ודוחה את הבקשה על ידי הצגת שגיאה.

  • הגבלת מספר הטוקנים המקסימלי בפלט של מודלים ליצירת טקסט.

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

שיקולים חשובים לגבי פונקציות לפני בקשה

  • מציינים את ספק Gemini API: event.data.request יכול להיות Gemini Developer API או Agent Platform Gemini API (formerly Vertex AI). לאובייקטים של הבקשות בממשקי ה-API השונים האלה יש צורות שונות. כדי לעבוד בבטחה עם אובייקט הבקשה, צריך לבדוק את event.data.api (למשל, להשוות ל-geminiV1Beta או ל-vertexV1Beta1, בהתאמה).

  • הוספת throw חוסמת את הבקשה: אם מוסיפים throw של HttpsError, הבקשה תידחה.

  • החזרת הבקשה כולה: אם הפונקציה משנה את הבקשה, צריך להחזיר את אובייקט הבקשה המלא והמשונה. אם לא מחזירים כלום (או undefined), הבקשה לא משתנה.

  • בדיקת זמן האחזור: בהתאם לפעולה של הפונקציה, היא עלולה להוסיף זמן אחזור ולהשפיע על חוויית המשתמש.

כתיבת פונקציה של בקשה (afterGenerateContent)

בסוג האירוע afterGenerateContent, הפונקציה מופעלת כשה-proxy של Firebase AI Logic מקבל תגובה מבקשת generateContent. הפונקציה מופעלת על התשובה לפני שהתשובה מוחזרת לאפליקציית הלקוח. הפונקציה יכולה לרשום את השימוש, לשנות את התשובה או לחסום אותה לחלוטין.

לפני שכותבים את הפונקציה, חשוב לעיין במידע הבא:

דוגמה

הנה דוגמה לפונקציה post-request שמתעדת את השימוש בטוקן ואת הסיבה לסיום:

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

שיקולים חשובים לגבי פונקציות אחרי בקשה

  • מציינים את ספק Gemini API: event.data.response יכול להיות Gemini Developer API או Agent Platform Gemini API (formerly Vertex AI). כדי להשתמש בבטחה באובייקט הבקשה, צריך לבדוק את event.data.api (למשל, להשוות ל-geminiV1Beta או ל-vertexV1Beta1, בהתאמה).

  • בדיקת זמן האחזור: בהתאם לפעולה של הפונקציה, היא עלולה להוסיף זמן אחזור ולהשפיע על חוויית המשתמש.

שלב 3: פריסת הפונקציות

פריסת הפונקציות ב-Firebase מעניקה לסוכן השירות Firebase AI Logic הרשאה להפעיל את הפונקציות האלה, ורושמת כל פונקציה כטריגר Firebase AI Logic.

  1. פורסים את הפונקציות באמצעות Firebase CLI:

    firebase deploy --only functions
    
  2. אחרי הפריסה, מוודאים שהפונקציות נפרסו ב-Firebase:

    firebase functions:list
    
  3. אם אתם צריכים לבצע איטרציה בפונקציה:

    מעדכנים את הפונקציה בספריית הפרויקט ומריצים את firebase deploy --only functions שוב.

הפסקת ההרצה של פונקציה

כדי להפסיק את ההרצה של אחת מהפונקציות האלה, צריך למחוק אותה מהשרתים שלנו ולבטל את הרישום שלה כFirebase AI Logic טריגר. אפשר לעשות את זה באמצעות Firebase CLI באחת מהדרכים הבאות:

  • אפשרות 1: מחיקה משתמעת של הפונקציה

    1. מסירים את הפונקציה מבסיס הקוד של ספריית הפרויקט.

    2. מריצים את הפקודה הבאה ב-CLI:Firebase

      firebase deploy --only functions
      
  • אפשרות 2: מחיקה מפורשת של הפונקציה

    1. מסירים את הפונקציה מבסיס הקוד של ספריית הפרויקט.

    2. מריצים את הפקודה הבאה Firebaseב-CLI:

      firebase functions:delete FUNCTION_NAME
      



מידע על נתוני אירועים

גם beforeGenerateContent וגם afterGenerateContent מקבלים אובייקט AIBlockingEvent שמכיל הקשר ומטא-נתונים לגבי הבקשה.

מטא-נתונים של בקשה ברמה העליונה (AIBlockingEvent)

אובייקט AIBlockingEvent ברמה העליונה מספק מידע על המתקשר ועל סביבת ההפעלה:

  • event.authType: מצב האימות של המתקשר: "app_user", ‏"unauthenticated" או "unknown".
  • event.authId: ה-UID של המתקשר באימות ב-Firebase, אם הוא מחובר לחשבון.
  • event.authClaims: טענות אימות מותאמות אישית של המתקשר, אם יש כאלה.
  • event.appId: מזהה האפליקציה ב-Firebase ששלחה את הבקשה.
  • event.androidPackageName / event.iosBundleId: שם החבילה או מזהה החבילה של האפליקציה שקוראת (רלוונטי לפלטפורמות Android או Apple, בהתאמה).
  • event.data: מטען הייעודי (payload) של האירוע, ששונה בין פונקציות שלפני הבקשה לבין פונקציות שאחרי הבקשה:

נתוני אירועים לפני בקשה (beforeGenerateContent)

בפונקציה beforeGenerateContent, הערך event.data מאוכלס באובייקט BeforeGenerateContentData:

  • event.data.api: הספק Gemini API: geminiV1Beta (Gemini Developer API) או vertexV1Beta1 (Agent Platform Gemini API (formerly Vertex AI)).
  • event.data.model: נתיב המשאב המלא של המודל (לדוגמה, projects/{PROJECT_ID}/locations/global/publishers/google/models/gemini-3.8-flash).
  • event.data.template: מטא-נתונים על תבנית לפרומפט של השרת שנעשה בה שימוש (PromptTemplateInfo), אם רלוונטי.
  • event.data.request: מטען הייעודי (payload) של הבקשה היוצאת. סוג האובייקט והמאפיינים תלויים בספק Gemini API:

נתוני אירועים אחרי בקשה (afterGenerateContent)

בפונקציה afterGenerateContent, הפרמטר event.data מאוכלס באובייקט AfterGenerateContentData. האובייקט הזה מרחיב את BeforeGenerateContentData (מספק api,‏ model,‏ template ו-request) ומוסיף את התגובה של המודל:



מגבלות והתנהגויות

כשמטמיעים את הפונקציות האלה, חשוב לזכור את ההתנהגויות והמגבלות הבאות:

  • בקשות generateContent בלבד: אפשר להפעיל את הפונקציות האלה רק באמצעות בקשות generateContent אל Gemini API דרך Firebase AI Logic.

    הפעולות הבאות לא יפעילו את הפונקציות האלה, והפונקציות יעקפו את הבקשה בשקט:

    • בקשות ל-generateContentStream לא יפעילו את הפונקציות האלה.

    • בקשות אל Gemini Live API לא יפעילו את הפונקציות האלה.

  • אין צורך לבצע שינויים בקוד בצד הלקוח: כדי להריץ את הפונקציות האלה, צריך לוודא שאתם משתמשים בבקשות מסוג generateContent, אבל לא נדרשים שינויים אחרים בבסיס הקוד בצד הלקוח.

    הפונקציות האלה נפרסות בשרתים שלנו ונרשמות כטריגרים של Firebase AI Logic, כדי ששרת ה-proxy של Firebase AI Logic יוכל ליירט בקשות ותגובות בצד השרת.

  • היקף ברמת הפרויקט: אפשר לפרוס לכל היותר פונקציה אחת של beforeGenerateContent ופונקציה אחת של afterGenerateContent לכל פרויקט Firebase.

  • מיקומי ברירת מחדל: הפונקציות האלה יופעלו ב-us-central1 כברירת מחדל (מידע על מיקומים של פונקציות). עם זאת, הפונקציה תירשם כטריגר Firebase AI Logic באזור global, בלי קשר למיקום שבו תפרסו את הפונקציה.