איך אפשר לראות את הזיכרון של העוזר הדיגיטלי מבוסס-AI באמצעות Gemini ו-Cloud SQL pgvector

1. מבוא

בשיעור Codelab הזה נסביר איך ליצור את הדמו של Living Memory, עוזר וירטואלי מבוסס-AI שעוקב אחרי 'זיכרונות' מהשיחה כדי לספק חוויה בהתאמה אישית.

ממשק המשתמש של הזיכרון הפעיל

האפליקציה משתמשת ב-Gemini להבנת שפה טבעית וב-Cloud SQL ל-PostgreSQL עם התוסף pgvector כדי לאחסן את הזיכרונות האלה ולאחזר אותם על סמך דמיון סמנטי.

ה-Codelab הזה מיועד למפתחים בכל רמות המיומנות שמתעניינים ב-AI ובמסדי נתונים, והוא אמור להימשך כ-60 דקות. העלות של המשאבים שנוצרו צריכה להיות פחות מ-5$.

הפעולות שתבצעו:

  • איך מגדירים מכונת Cloud SQL ל-PostgreSQL עם תמיכה ב-pgvector.
  • איך משתמשים ב-Gemini כדי לחלץ באופן אינטראקטיבי 'זיכרונות' מהודעות של משתמשים.
  • איך מבצעים חיפושי וקטורים ב-PostgreSQL כדי לאחזר הקשר רלוונטי לתשובות מבוססות-AI.

ארכיטקטורה של זיכרון פעיל

הדרישות

  • פרויקט ב-Google Cloud שהחיוב בו מופעל.
  • ידע בסיסי בשורת הפקודה וב-Node.js.

‫2. לפני שמתחילים

הגדרת הפרויקט

יצירת פרויקט ב-Google Cloud

  1. במסוף Google Cloud, בדף לבחירת הפרויקט, בוחרים פרויקט ב-Google Cloud או יוצרים פרויקט.
  2. הקפידו לוודא שהחיוב מופעל בפרויקט שלכם ב-Cloud. כך בודקים אם החיוב מופעל בפרויקט

הפעלת Cloud Shell

Cloud Shell היא סביבת שורת פקודה שפועלת ב-Google Cloud, וכוללת מראש את הכלים הדרושים.

  1. לוחצים על Activate Cloud Shell בחלק העליון של מסוף Google Cloud.
  2. אחרי שמתחברים ל-Cloud Shell, מאמתים את האימות:
    gcloud auth list
    
  3. מוודאים שהפרויקט מוגדר:
    gcloud config get project
    
  4. אם הפרויקט לא מוגדר כמו שציפיתם, מגדירים אותו:
    export PROJECT_ID=<YOUR_PROJECT_ID>
    gcloud config set project $PROJECT_ID
    

הפעלת ממשקי ה-API

מריצים את הפקודה הבאה ב-Cloud Shell כדי להפעיל את ממשקי ה-API הנדרשים:

gcloud services enable sqladmin.googleapis.com \
                       aiplatform.googleapis.com

3. שכפול מאגר ההדגמה

עכשיו, מעתיקים את הקוד של הדמו של הזיכרון החי.

  1. משכפלים את המאגר לסביבת Cloud Shell:
    git clone https://github.com/GoogleCloudPlatform/devrel-demos.git
    cd devrel-demos/codelabs/visual-memory-postgres-demo
    
  2. מתקינים את יחסי התלות:
    npm install
    

4. יצירה והגדרה של מסד הנתונים ב-Cloud SQL

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

  1. האפליקציה משתמשת במשתני סביבה לצורך הגדרה. מריצים את הבלוק הבא במסוף Cloud Shell כדי להגדיר את המשתנים הנדרשים לסשן הזה:
    export REGION="us-central1"
    export INSTANCE_NAME="living-memory-db"
    export DB_HOST=127.0.0.1
    export DB_PORT=5432
    export DB_USER=memory_app
    export DB_PASS=memory_app_password
    export DB_NAME=living_memory
    export PGPASSWORD=$DB_PASS
    
  2. יוצרים את המופע. השלב הזה נמשך בדרך כלל 5-10 דקות.
    gcloud sql instances create $INSTANCE_NAME \
        --database-version=POSTGRES_16 \
        --cpu=1 \
        --memory=3840MB \
        --region=$REGION \
        --root-password=$DB_PASS \
        --edition=ENTERPRISE
    
    בזמן יצירת המופע, כדאי להבין את סכימת מסד הנתונים שבה תשתמשו. הסקריפט מפעיל את התוסף vector ויוצר כמה טבלאות לתמיכה באפליקציה:דיאגרמת סכימה של זיכרון חזותי
    • users, conversations, messages: טבלאות סטנדרטיות לאחסון פרופילי משתמשים והיסטוריית שיחות.
    • memories: זהו טבלת הליבה של Retrieval-Augmented Generation (יצירה משולבת-אחזור, RAG). כל שורה מייצגת פיסת מידע שחולצה מהשיחה (לדוגמה, 'המשתמש אוהב טיולים'). בהיסטוריית הפעילות נשמרים:
      • content: הטקסט של הזיכרון.
      • memory_type: סוג הזיכרון (FACT,‏ PREF או IMPLICIT).
      • embedding: עמודה vector עם 768 ממדים שמכילה את הייצוג הסמנטי של התוכן, שנוצר על ידי Gemini.
    • pgvector אינדקס: נוצר אינדקס HNSW (Hierarchical Navigable Small World) בעמודה embedding. הפעולה הזו חיונית לאופטימיזציה של חיפושים לפי k-Nearest Neighbor ‏ (k-NN), ומאפשרת ל-pgvector למצוא במהירות את הזיכרונות הכי דומים מבחינה סמנטית באמצעות האופרטור של מרחק הקוסינוס (<=>).
  3. יצירת מסד הנתונים
    gcloud sql databases create $DB_NAME --instance=$INSTANCE_NAME
    
  4. יצירת משתמש האפליקציה
    gcloud sql users create $DB_USER --instance=$INSTANCE_NAME --password=$DB_PASS
    
  5. מפעילים את שרת ה-proxy ל-Cloud SQL Auth. הפרוקסי מספק גישה מאובטחת למופע בלי צורך בהגדרת רשימת כתובות IP שאפשר לגשת אליהן.
    (cloud-sql-proxy ${GOOGLE_CLOUD_PROJECT}:us-central1:living-memory-db &) && sleep 2 && echo ""
    
    אמורה להופיע הודעה כמו: The proxy has started successfully and is ready for new connections!.
  6. מפעילים את התוסף vector באמצעות הפקודה schema.sql ויוצרים את הטבלאות הנדרשות. שרת ה-proxy פועל, ולכן עכשיו אפשר להתחבר למופע בכתובת 127.0.0.1.
    psql -h 127.0.0.1 -U $DB_USER -d $DB_NAME < schema.sql
    
  7. מוודאים שהסכמה נוצרה בהצלחה.
    psql -h 127.0.0.1 -U $DB_USER -d $DB_NAME -c "\dt"
    
    אמור להופיע פלט עם רשימה של הטבלאות conversations, memories, messages ו-users.
                      List of relations
    Schema |     Name      | Type  |   Owner    
    --------+---------------+-------+------------
    public | conversations | table | memory_app
    public | memories      | table | memory_app
    public | messages      | table | memory_app
    public | queries_log   | table | memory_app
    public | users         | table | memory_app
    (5 rows)
    

5. הסבר על אחזור סמנטי באמצעות pgvector

בקטע הזה נבדוק איך האפליקציה מאחזרת הקשר רלוונטי ל-AI לפני יצירת תשובה. קטע הקוד הבא מתוך server.js מציג את הקוד שאחראי לכך בנקודת הקצה /api/chat:

// Retrieve Similar Memories for Context (Using pgvector)
const promptEmbeddingRes = await ai.models.embedContent({
  model: 'gemini-embedding-001',
  contents: message,
  config: { outputDimensionality: 768 },
});

const promptEmbedding = promptEmbeddingRes.embeddings[0].values;
const embeddingStr = `[${promptEmbedding.join(',')}]`;

// Query DB for top 5 closest memories
const relevantMemories = await pool.query(
  `SELECT id, content, memory_type, category 
   FROM memories 
   WHERE user_id = $1 
   ORDER BY embedding <=> $2::vector 
   LIMIT 5`,
  [userId, embeddingStr]
);

איך זה עובד

  • AI גנרטיבי (הטמעה): האפליקציה מקבלת את ההודעה הנכנסת של המשתמש ומשתמשת במודל gemini-embedding-001 כדי להמיר את הטקסט לווקטור עם 768 ממדים. הווקטור הזה מייצג את המשמעות הסמנטית של ההודעה.
  • Cloud SQL‏ (pgvector): האפליקציה מעבירה את הווקטור הזה ל-Cloud SQL. באמצעות האופרטור <=> (מרחק קוסינוס) שסופק על ידי התוסף pgvector, ‏ Cloud SQL מוצא את 5 הזיכרונות שהכי דומים סמנטית להנחיה.
  • התוצאה: זהו תהליך של Retrieval-Augmented Generation (יצירה משולבת-אחזור, RAG). ה-AI מקבל גישה לזיכרונות ספציפיים ורלוונטיים ממסד הנתונים כדי להתאים אישית את התשובה שלו, בלי שהוא צריך לטעון את כל ההיסטוריה.

6. הסבר על חילוץ זיכרון

בשלב הבא, נראה איך האפליקציה לומדת מהשיחה. קטע הקוד הבא הוא מהפונקציה extractMemoriesAsync ב-server.js:

// MEMORY EXTRACTION LOGIC
async function extractMemoriesAsync(userMessage, userId, messageId) {
  const extractionPrompt = `
    Analyze the following user message. A memory profile is being built for this user.
    Extract ANY explicit facts (Facts), preferences (Pref), or implicit behavioral traits/styles (Implicit).
    Return the result as a raw JSON array of objects (NO Markdown blocks, just the JSON array).
    Format: [{"content": "string fact/sentence", "type": "FACT|PREF|IMPLICIT", "category": "General|Travel|Hobby|Persona"}]
    If nothing is found, return [].
    Message: "${userMessage}"
    `;

  const result = await ai.models.generateContent({
    model: 'gemini-2.5-flash',
    contents: extractionPrompt
  });
  let rawJson = result.text.replace(/^```json/g, '').replace(/```$/g, '').trim();

  let extracted;
  try {
    extracted = JSON.parse(rawJson);
  } catch (e) {
    console.warn("Could not parse extracted JSON:", rawJson);
    return;
  }

  if (Array.isArray(extracted) && extracted.length > 0) {
    // Compute embeddings and save each to the DB
    for (const memory of extracted) {
      const embedRes = await ai.models.embedContent({
        model: 'gemini-embedding-001',
        contents: memory.content,
        config: { outputDimensionality: 768 },
      });
      const vectorData = `[${embedRes.embeddings[0].values.join(',')}]`;

      await pool.query(
        `INSERT INTO memories (user_id, content, memory_type, category, embedding, source_message_id)
                 VALUES ($1, $2, $3, $4, $5, $6)`,
        [userId, memory.content, memory.type.toUpperCase(), memory.category, vectorData, messageId]
      );
      console.log(`Saved new memory: ${memory.content}`);
    }
  }
}

איך זה עובד

  • AI גנרטיבי (פלט מובנה): האפליקציה משתמשת במודל gemini-2.5-flash המהיר במיוחד כדי לנתח את ההודעה של המשתמש ולחלץ עובדות והעדפות מובנות כמערך JSON.
  • Cloud SQL (אחסון היברידי): אחרי יצירת הטמעות לעובדות החדשות האלה, הן מאוחסנות ב-Cloud SQL. שימו לב שנתונים רלציוניים רגילים (מזהה משתמש, תוכן טקסט, קטגוריות) מאוחסנים לצד נתוני הווקטורים הרב-ממדיים בשורה אחת.
  • התוצאה: האפליקציה יוצרת פרופיל זיכרון שמתעדכן בעצמו בזמן אמת, תוך שימוש ביכולות הניתוח של Gemini וביכולות האחסון של Cloud SQL.

7. הפעלת אפליקציית הצ'אט

  1. הוספת כמה משתמשים לדוגמה למסד הנתונים
    npm run seed
    
    זיכרון של ערך הבסיס
  2. ואז מריצים את השרת.
    node server.js
    
  3. ב-Cloud Shell, לוחצים על Web Preview (תצוגה מקדימה של אתר) בפינה השמאלית העליונה של סרגל הכלים של הטרמינל, ובוחרים באפשרות Change Port (שינוי יציאה). מזינים 3000 בשדה של מספר הניוד ולוחצים על שינוי ותצוגה מקדימה.

איך משתמשים בעוזר הדיגיטלי

כשהאפליקציה נפתחת בדפדפן, מוצג ממשק הצ'אט של Living Memory. בצד שמאל, כלי ההמחשה של נתוני AI Cortex מציג זיכרונות כצמתים במרחב וקטורי, עם קידוד צבעים לפי סוג (עובדה, העדפה, מאפיין מרומז). הטקסט בצמתי הזיכרון עשוי להיות קטן, בהתאם לרזולוציית המסך. כדי לראות אותו מקרוב, אפשר להשתמש בעכבר או במשטח המגע כדי להתקרב ולהתרחק ולזוז בתצוגה.

ממשק המשתמש של הזיכרון הפעיל

שאילתות על זיכרונות קיימים

הסקריפט seed שהרצתם קודם יצר שני משתמשים לדוגמה עם זיכרונות מסוימים שאוכלסו מראש.

  1. בוחרים משתמש מהתפריט הנפתח של המשתמשים בפינה הימנית העליונה.
  2. לוחצים על אחד מהלחצנים של הצ'אט המהיר או מקלידים Give me restaurant recommendations in New York City בשדה הקלט של הצ'אט ומקישים על שליחה.
  3. כשהעוזר הדיגיטלי משיב, אפשר ללחוץ על ההודעה שלו כדי לראות באילו זיכרונות הוא השתמש. הם יסומנו בירוק ותוכלו להגדיל אותם ולראות איך הם עזרו ליצור את התשובה.

יצירת משתמש חדש

עכשיו ניצור משתמש חדש.

  1. לוחצים על הלחצן + לצד התפריט הנפתח של המשתמש כדי להתחיל שיחת צ'אט חדשה.
  2. אפשר להשתמש בשם ובתיאור שנוצרו או לערוך אותם כדי לתאר את עצמכם.
  3. לוחצים על יצירה והאפליקציה מתחילה לחלץ זיכרונות. אחרי כ-30 שניות, אמורים להופיע צמתים חדשים בכלי ההמחשה בצד שמאל. הם מייצגים את העובדות וההעדפות ש-Gemini חילץ מההודעה שלכם ושמר במסד הנתונים של Cloud SQL.
  4. כדי לראות את ה-Assistant משתמש בזיכרונות החדשים שלו בשיחה, אפשר לשאול שאלת המשך כמו What food do I like?.

8. הסרת המשאבים

כדי להימנע מחיובים שוטפים בחשבון Google Cloud על המשאבים שבהם השתמשתם ב-Codelab הזה, מומלץ למחוק את המשאבים שיצרתם.

  1. מחיקת המכונה של Cloud SQL:
    gcloud sql instances delete $INSTANCE_NAME --quiet
    
  2. מסירים את מאגר ההדגמה:
    rm -rf ~/devrel-demos
    

9. מזל טוב

יצרת והטמעת בהצלחה עוזר AI עם זיכרון דינמי.

מה למדתם

  • איך משתמשים ב-Cloud SQL pgvector לחיפוש סמנטי.
  • איך משתמשים ב-Gemini כדי לחלץ זיכרון דינמי.

השלבים הבאים

תהנו מהיצירה עם הזיכרון החי!