מיליון וקטורים, אפס לולאות: יצירת הטמעות בקנה מידה גדול באמצעות AlloyDB

1. סקירה כללית

בשיעור הזה תלמדו איך לבנות אפליקציה לחיפוש במאגר ידע שניתן להרחבה. במקום לנהל צינור ETL מורכב עם סקריפטים של Python ולולאות כדי ליצור הטבעות וקטוריות, אתם יכולים להשתמש ב-AlloyDB AI כדי לטפל ביצירת ההטבעות באופן מקורי בתוך מסד הנתונים באמצעות פקודת SQL אחת.

d4324260c68d4a70.png

מה תפַתחו

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

מה תלמדו

תלמדו איך:

  • הקצאת משאבים לאשכול AlloyDB והפעלת תוספי AI.
  • יצירת נתונים סינתטיים (50,000 שורות ומעלה) באמצעות SQL.
  • מילוי חוסרים של הטמעות וקטוריות בכל מערך הנתונים באמצעות עיבוד באצווה.
  • כדי להטמיע נתונים חדשים באופן אוטומטי, מגדירים טריגרים מצטברים בזמן אמת.
  • מבצעים חיפוש היברידי (מסנני וקטור + SQL) של 'Flexing Context'.

דרישות

  • דפדפן, כמו Chrome או Firefox.
  • פרויקט ב-Google Cloud שהחיוב בו מופעל.
  • היכרות בסיסית עם SQL.

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

יצירת פרויקט

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

תמונה של לחצן Activate Cloud Shell

  1. אחרי שמתחברים ל-Cloud Shell, אפשר לבדוק שכבר בוצע אימות ושהפרויקט מוגדר למזהה הפרויקט שלכם באמצעות הפקודה הבאה:
gcloud auth list
  1. מריצים את הפקודה הבאה ב-Cloud Shell כדי לוודא שפקודת gcloud מכירה את הפרויקט.
gcloud config list project
  1. אם הפרויקט לא מוגדר, משתמשים בפקודה הבאה כדי להגדיר אותו:
gcloud config set project <YOUR_PROJECT_ID>
  1. מפעילים את ממשקי ה-API הנדרשים: לוחצים על הקישור ומפעילים את ממשקי ה-API.

אפשר גם להשתמש בפקודת gcloud. אפשר לעיין במאמרי העזרה בנושא פקודות gcloud ושימוש בהן.

gcloud services enable \
  alloydb.googleapis.com \
  compute.googleapis.com \
  cloudresourcemanager.googleapis.com \
  servicenetworking.googleapis.com \
  aiplatform.googleapis.com

נקודות חשובות ופתרון בעיות

תסמונת 'פרויקט הרפאים'

הפעלת את הפקודה gcloud config set project, אבל בפועל את מסתכלת על פרויקט אחר בממשק המשתמש של המסוף. צריך לבדוק את מזהה הפרויקט בתפריט הנפתח שבפינה הימנית העליונה.

מחסום בחיוב

הפעלתם את הפרויקט, אבל שכחתם להוסיף חשבון לחיוב. ‫AlloyDB הוא מנוע עם ביצועים גבוהים, והוא לא יופעל אם 'מיכל הדלק' (החיוב) ריק.

השהיה של הפצת API

לחצת על 'הפעלת ממשקי API', אבל בשורת הפקודה עדיין מופיעה ההודעה Service Not Enabled. מחכים 60 שניות. צריך לחכות כמה רגעים עד שהנוירונים בענן יתעוררו.

מכסה

אם אתם משתמשים בחשבון ניסיון חדש לגמרי, יכול להיות שתגיעו למכסה אזורית של מופעי AlloyDB. אם הפעולה us-central1 נכשלת, מנסים את הפעולה us-east1.

סוכן שירות 'מוסתר'

לפעמים סוכן השירות של AlloyDB לא מקבל אוטומטית את התפקיד aiplatform.user. בדרך כלל, אם שאילתות ה-SQL לא יכולות לתקשר עם Gemini בהמשך, זו הסיבה לכך.

3. הגדרת מסד נתונים

בשיעור ה-Lab הזה נשתמש ב-AlloyDB כבסיס הנתונים של נתוני הבדיקה. הוא משתמש באשכולות כדי להכיל את כל המשאבים, כמו מסדי נתונים ויומנים. לכל אשכול יש מופע ראשי שמספק נקודת גישה לנתונים. הטבלאות יכילו את הנתונים בפועל.

ניצור אשכול, מכונה וטבלה של AlloyDB שבהם ייטען מערך הנתונים של הבדיקה.

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

  1. אחרי שהשלב הזה יסתיים, המאגר ישוכפל לעורך המקומי של Cloud Shell ותוכלו להריץ את הפקודה שלמטה מתוך תיקיית הפרויקט (חשוב לוודא שאתם בספריית הפרויקט):
sh run.sh
  1. עכשיו משתמשים בממשק המשתמש (לוחצים על הקישור במסוף או על הקישור 'תצוגה מקדימה באינטרנט' במסוף).
  2. כדי להתחיל, מזינים את הפרטים של מזהה הפרויקט, האשכול ושמות המופעים.
  3. אתם יכולים ללכת לשתות קפה בזמן שהיומנים מתגללים, וכאן תוכלו לקרוא איך זה קורה מאחורי הקלעים. התהליך יימשך כ-10 עד 15 דקות.

נקודות חשובות ופתרון בעיות

הבעיה של 'סבלנות'

אשכולות של מסדי נתונים הם תשתית כבדה. אם תרעננו את הדף או תסיימו את הסשן ב-Cloud Shell כי נראה שהוא נתקע, יכול להיות שתקבלו מופע 'רפאים' שהוקצה באופן חלקי ואי אפשר למחוק אותו בלי התערבות ידנית.

חוסר התאמה באזור

אם הפעלתם את ממשקי ה-API ב-us-central1 אבל ניסיתם להקצות את האשכול ב-asia-south1, יכול להיות שתיתקלו בבעיות שקשורות למכסות או בעיכובים בהרשאות של חשבון השירות. חשוב להשתמש באזור אחד לכל אורך שיעור ה-Lab.

Zombie Clusters

אם השתמשתם בעבר באותו שם לאשכול ולא מחקתם אותו, יכול להיות שהסקריפט יציין שהשם של האשכול כבר קיים. שמות האשכולות חייבים להיות ייחודיים בפרויקט.

פסק זמן ב-Cloud Shell

אם הפסקת הקפה שלכם נמשכת 30 דקות, יכול להיות ש-Cloud Shell יעבור למצב שינה וינתק את התהליך sh run.sh. חשוב להשאיר את הכרטיסייה פעילה.

4. הקצאת הרשאות לסכימה

בשלב הזה נסביר על:

879263c907f3cac6.png

אחרי שמפעילים את האשכול ואת המופע של AlloyDB, עוברים אל עורך ה-SQL של AlloyDB Studio כדי להפעיל את תוספי ה-AI ולספק את הסכימה.

1e3ac974b18a8113.png

יכול להיות שתצטרכו לחכות עד שהמופע שלכם יסיים את תהליך היצירה. אחרי שזה קורה, נכנסים ל-AlloyDB באמצעות פרטי הכניסה שיצרתם כשנוצר האשכול. משתמשים בנתונים הבאים כדי לבצע אימות ל-PostgreSQL:

  • שם משתמש : "postgres"
  • מסד נתונים : "postgres"
  • סיסמה: alloydb (או כל סיסמה אחרת שהגדרתם בזמן היצירה)

אחרי שתעברו בהצלחה את תהליך האימות ב-AlloyDB Studio, תוכלו להזין פקודות SQL בכלי העריכה. אפשר להוסיף כמה חלונות של Editor באמצעות סימן הפלוס משמאל לחלון האחרון.

28cb9a8b6aa0789f.png

מזינים פקודות ל-AlloyDB בחלונות של כלי העריכה, ומשתמשים באפשרויות Run (הפעלה), Format (עיצוב) ו-Clear (ניקוי) לפי הצורך.

הפעלת תוספים

כדי ליצור את האפליקציה הזו, נשתמש בתוספים pgvector ו-google_ml_integration. התוסף pgvector מאפשר לכם לאחסן ולחפש הטמעות של וקטורים. התוסף google_ml_integration מספק פונקציות שמשמשות לגישה לנקודות קצה של חיזוי ב-Vertex AI כדי לקבל חיזויים ב-SQL. מפעילים את התוספים האלה על ידי הפעלת פקודות ה-DDL הבאות:

CREATE EXTENSION IF NOT EXISTS google_ml_integration CASCADE;
CREATE EXTENSION IF NOT EXISTS vector;

צור טבלה

אנחנו צריכים מערך נתונים כדי להדגים את ההיקף. במקום לייבא קובץ CSV, ניצור באופן מיידי 50,000 שורות של 'מאמרי עזרה' סינתטיים באמצעות SQL.

אתם יכולים ליצור טבלה באמצעות הצהרת ה-DDL שבהמשך ב-AlloyDB Studio:

-- 1. Create the table
CREATE TABLE help_articles (
    id SERIAL PRIMARY KEY,
    title TEXT,
    category TEXT,
    product_version TEXT,
    content_body TEXT,
    embedding vector(768) -- Dimension for text-embedding-005
);

-- 2. Generate 50,000 rows of synthetic data
INSERT INTO help_articles (title, category, product_version, content_body)
SELECT
    'Help Article ' || i,
    CASE 
        WHEN i % 3 = 0 THEN 'Billing' 
        WHEN i % 3 = 1 THEN 'Technical' 
        ELSE 'General' 
    END,
    CASE 
        WHEN i % 2 = 0 THEN '2.0' 
        ELSE '1.0' 
    END,
    'This article covers common issues regarding ' || 
    CASE 
        WHEN i % 3 = 0 THEN 'payment failures, invoice disputes, and credit card updates.'
        WHEN i % 3 = 1 THEN 'connection timeouts, latency issues, and API errors.'
        ELSE 'account profile settings, password resets, and user roles.' 
    END
FROM generate_series(1, 50000) AS i;

בעמודה item_vector אפשר לאחסן את ערכי הווקטור של הטקסט.

בודקים את הנתונים:

SELECT count(*) FROM help_articles;
-- Output: 50000

הפעלת דגלים של מסד נתונים

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

  1. מוודאים שהדגל google_ml_integration.enable_model_support מוגדר כ-on:

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

  1. מוודאים שהדגל google_ml_integration.enable_faster_embedding_generation מוגדר למצב on:

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

שלבים להגדרת דגלים של מסד נתונים:

  1. במסוף Google Cloud, עוברים לדף Clusters.

מעבר אל Clusters

  1. לוחצים על אשכול בעמודה שם המשאב.
  2. בדף סקירה כללית, עוברים אל מופעים באשכול, בוחרים מופע ולוחצים על עריכה.
  3. כדי להוסיף, לשנות או למחוק דגל מסד נתונים מהמופע:

הוספת דגל

  1. כדי להוסיף לדוגמה דגל מסד נתונים, לוחצים על 'הוספת דגל'.
  2. בוחרים דגל מהרשימה New database flag (דגל חדש של מסד נתונים).
  3. צריך לספק ערך לדגל.
  4. לוחצים על 'סיום'.
  5. לוחצים על עדכון המופע.
  6. מוודאים שהתוסף google_ml_integration הוא בגרסה 1.5.2 ואילך:

כדי לבדוק את גרסת התוסף באמצעות הפקודה הבאה:

SELECT extversion FROM pg_extension WHERE extname = 'google_ml_integration';

אם אתם צריכים לשדרג את התוסף, אתם יכולים להשתמש בפקודה:

ALTER EXTENSION google_ml_integration UPDATE;

מתן הרשאה

  1. כדי לאפשר למשתמש לנהל את יצירת ההטמעה האוטומטית, צריך להעניק לו הרשאות INSERT,‏ UPDATE ו-DELETE בטבלאות google_ml.embed_gen_progress ו-google_ml.embed_gen_settings:
GRANT INSERT, UPDATE, DELETE ON google_ml.embed_gen_progress TO postgres;

postgres הוא USER_NAME שההרשאות ניתנות לו.

  1. מריצים את ההצהרה הבאה כדי להעניק הרשאת הפעלה לפונקציה embedding:
GRANT EXECUTE ON FUNCTION embedding TO postgres;

נותנים לחשבון השירות של AlloyDB את התפקיד Vertex AI User

במסוף IAM של Google Cloud, מעניקים לחשבון השירות של AlloyDB (שנראה כך: service-<<PROJECT_NUMBER>>@gcp-sa-alloydb.iam.gserviceaccount.com) גישה לתפקיד Vertex AI User. ‫PROJECT_NUMBER יכיל את מספר הפרויקט.

אפשר גם להריץ את הפקודה הבאה מ-Cloud Shell Terminal:

PROJECT_ID=$(gcloud config get-value project)


gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:service-$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")@gcp-sa-alloydb.iam.gserviceaccount.com" \
--role="roles/aiplatform.user"

נקודות חשובות ופתרון בעיות

הלולאה של 'שכחתי את הסיסמה'

אם השתמשתם בהגדרה 'קליק אחד' ואתם לא זוכרים את הסיסמה, עוברים לדף 'פרטים בסיסיים של מופע' במסוף ולוחצים על 'עריכה' כדי לאפס את הסיסמה של postgres.

השגיאה 'התוסף לא נמצא'

אם CREATE EXTENSION נכשל, לרוב זה קורה כי המופע עדיין במצב 'תחזוקה' או 'עדכון' מההקצאה הראשונית. בודקים אם שלב יצירת המופע הושלם וממתינים כמה שניות אם צריך.

5. יצירת וקטורים בשיטת One-Shot

זהו החלק המרכזי של שיעור ה-Lab. במקום לכתוב לולאת Python כדי לעבד את 50,000 השורות האלה, נשתמש בפונקציה ai.initialize_embeddings.

הפקודה הזו מבצעת שתי פעולות:

  1. Backfills כל השורות הקיימות.
  2. יצירת טריגר להטמעה אוטומטית של שורות עתידיות.

מריצים את הצהרת ה-SQL שלמטה מ-AlloyDB Query Editor

CALL ai.initialize_embeddings(
  model_id => 'text-embedding-005',
  table_name => 'help_articles',
  content_column => 'content_body',
  embedding_column => 'embedding',
  incremental_refresh_mode => 'transactional'
);

אימות ההטמעות

בודקים שהעמודה embedding מאוכלסת עכשיו:

SELECT id, left(content_body, 30), substring(embedding::text, 1, 30) as vector_partial 
FROM help_articles;

אמורה להתקבל תוצאה שדומה לזו שמוצגת בהמשך:

a872b8926a164275.png

מה קרה עכשיו?

  1. מילוי חוסרים בהיקף נרחב: המערכת סורקת באופן אוטומטי את 50,000 השורות הקיימות ויוצרת הטמעות באמצעות Vertex AI.
  2. אוטומציה: אם מגדירים את הערך incremental_refresh_mode => 'transactional', ‏ AlloyDB מגדיר אוטומטית את הטריגרים הפנימיים. כל שורה חדשה שתוכנס לטבלה help_articles תעבור מיד הטמעה.
  3. אפשר גם להגדיר את incremental_refresh_mode => 'None' כדי לקבל את ההצהרה רק לצורך ביצוע עדכונים בכמות גדולה, ולהפעיל באופן ידני את ai.refresh_embeddings() כדי לעדכן את כל הטמעות השורות.

החלפתם תור של Kafka,‏ worker של Python וסקריפט העברה ב-6 שורות של SQL. כאן מופיע התיעוד הרשמי המפורט של כל המאפיינים.

בדיקת טריגר בזמן אמת

נבדוק שהאוטומציה 'ללא לולאה' פועלת על נתונים חדשים.

  1. הוספת שורה חדשה:
INSERT INTO help_articles (title, category, product_version, content_body)
VALUES ('New Scaling Guide', 'Technical', '2.0', 'How to scale AlloyDB to millions of transactions.');
  1. בדיקה מיידית:
SELECT embedding FROM help_articles WHERE title = 'New Scaling Guide';

תוצאה:

הווקטור אמור להיווצר באופן מיידי בלי להריץ סקריפט חיצוני.

גודל אצווה של כוונון

כרגע, גודל אצווה ברירת המחדל ב-AlloyDB הוא 50. הגדרות ברירת המחדל פועלות מצוין, אבל AlloyDB עדיין מאפשר למשתמשים לשנות את ההגדרות כדי להגיע להגדרה המושלמת עבור המודל וערכת הנתונים הייחודיים שלהם.

CALL ai.initialize_embeddings(
  model_id => 'text-embedding-005',
  table_name => 'help_articles',
  content_column => 'content_body',
  embedding_column => 'embedding',
  incremental_refresh_mode => 'transactional',
  batch_size => 20
);

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

נקודות חשובות ופתרון בעיות

הפער בהפצת IAM

הפעלתם את פקודת IAM‏ gcloud, אבל פקודת ה-SQL‏ CALL עדיין נכשלת עם שגיאת הרשאה. יכול לעבור זמן מה עד שהשינויים ב-IAM יתעדכנו בכל מערכות Google. קחו נשימה עמוקה.

חוסר התאמה במאפיין וקטור

הטבלה help_articles מוגדרת ל-VECTOR(768)בעמודה content_body. אם תנסו להשתמש במודל אחר (למשל מודל עם 1,536 מימדים) בהמשך, ההוספות שלכם יתפוצצו. כדאי להשתמש ב-text-embedding-005.

6. חיפוש לפי הקשר עם גמישות

עכשיו נבצע חיפוש היברידי. אנחנו משלבים בין הבנה סמנטית (וקטור) לבין לוגיקה עסקית (מסנני SQL).

מריצים את השאילתה הבאה כדי למצוא בעיות בחיוב שקשורות ספציפית לגרסה 2.0 של המוצר:

SELECT
  title,
  left(content_body, 100) as content_snippet,
  1 - (embedding <=> embedding('text-embedding-005', 'Invoice did not go through')::vector) as relevance
FROM help_articles
WHERE category = 'Billing'  -- Hard SQL Filter
  AND product_version = '2.0' -- Hard SQL Filter
ORDER BY relevance DESC
LIMIT 5;

זהו הקשר הגמיש. החיפוש "מתגמש" כדי להבין את כוונת המשתמש ("בעיות בחיוב") תוך שמירה על מגבלות עסקיות נוקשות (גרסה 2.0).

f0fdb50d6195c462.png

למה זה פתרון טוב לסטארטאפים ולהעברות

  1. אפס חובות בתשתית: לא הפעלתם מסד נתונים וקטורי נפרד (Pinecone/Milvus). לא כתבתם משימת ETL נפרדת. הכול נמצא ב-Postgres.
  2. עדכונים בזמן אמת: באמצעות מצב 'טרנזקציונלי', אינדקס החיפוש שלכם אף פעם לא מיושן. ברגע שהנתונים נשמרים, הם מוכנים לשימוש כווקטורים.
  3. התאמה לשינויים: AlloyDB מבוסס על התשתית של Google. הוא יכול להתמודד עם יצירה בכמות גדולה של מיליוני וקטורים מהר יותר ממה שסקריפט Python יכול.

נקודות חשובות ופתרון בעיות

בעיה בביצועים של יצירת תוכן

בעיה: מהיר ל-50,000 שורות. הפעולה איטית מאוד עבור מיליון שורות אם מסנן הקטגוריות לא סלקטיבי מספיק. פתרון:מוסיפים אינדקס וקטורי: כדי להשתמש בפתרון בסביבת ייצור, צריך ליצור אינדקס:CREATE INDEX ON help_articles USING hnsw (embedding vector_cosine_ops);מאמתים את השימוש באינדקס: מריצים את הפקודה EXPLAIN ANALYZE SELECT ... כדי לוודא שהמסד הנתונים משתמש באינדקס ולא מבצע סריקה רציפה.

האסון של חוסר התאמה בין מודלים

בעיה: הפעלתם את העמודה באמצעות text-embedding-005 בהליך CALL. אם משתמשים בטעות במודל אחר (למשל, text-embedding-004 או מודל OSS) בפונקציית ההטמעה של שאילתת ה-SELECT‏ embedding('model-name', ...), יכול להיות שהממדים יהיו זהים (768), אבל מרחב הווקטורים יהיה שונה לחלוטין. השאילתה תפעל ללא שגיאה, אבל התוצאות לא יהיו רלוונטיות בכלל (ציוני רלוונטיות לא הגיוניים). פתרון בעיות:מוודאים שהערך של model_id בפונקציה ai.initialize_embeddings זהה בדיוק לערך של model_id בשאילתת ה-SELECT.

התוצאה Silent Empty (סינון יתר)

בעיה: חיפוש היברידי הוא פעולת AND. נדרשת התאמה סמנטית וגם התאמה ל-SQL.אם משתמש מחפש "עזרה בנושא חיוב" אבל בעמודה product_version מופיע ‘2.0.1' במקום ‘2.0', התוצאה היא אפס שורות, גם אם ההתאמה הווקטורית היא 99%.פתרון בעיות:

  • מריצים את השאילתה בלי המיון הווקטורי כדי לבדוק אם המסננים של SQL‏ (WHERE category...) מחזירים נתונים.
  • בודקים אם יש הבדל בין אותיות רישיות לאותיות קטנות (Billing לעומת billing).

4. שגיאות הרשאה או מכסה (שגיאה 500)

בעיה:הפונקציה embedding() בסעיף SELECT מבצעת קריאה לרשת בזמן אמת אל Vertex AI.אם חשבון השירות של מסד הנתונים מאבד את התפקיד Vertex AI User, או אם חורגים מהמכסה של Vertex AI API ‏ (QPM), כל שאילתת ה-SQL תיכשל.פתרון בעיות:

  • בודקים את Cloud Logging ל-AlloyDB.
  • מוודאים שתפקיד ה-IAM עדיין פעיל.
  • אם נדרשת עמידות גבוהה, צריך להוסיף את הפונקציה לבלוק TRY/CATCH בפרוצדורות מאוחסנות.

5. הטמעות Null

בעיה:אם מוסיפים נתונים לפני שהמודל מאותחל באופן מלא או אם עובד הרקע נכשל, יכול להיות שחלק מהשורות יכילו את הערך NULL בעמודה embedding.NULL <=> Vector מחזירה את הערך NULL. השורות האלה נעלמות מסדר המיון.פתרון בעיות:

  • מריצים את הפקודה SELECT count(*) FROM help_articles WHERE embedding IS NULL; כדי לוודא שהכיסוי הוא מלא.

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

אחרי שמסיימים את ה-Lab הזה, חשוב למחוק את אשכול AlloyDB ואת המכונה.

הוא צריך לנקות את האשכול יחד עם המכונות שלו.

8. מזל טוב

יצרתם בהצלחה אפליקציית חיפוש במאגר ידע שניתנת להרחבה. במקום לנהל צינור ETL מורכב עם סקריפטים של Python ולולאות כדי ליצור הטבעות וקטוריות, השתמשתם ב-AlloyDB AI כדי לטפל ביצירת ההטבעות באופן מקורי בתוך מסד הנתונים באמצעות פקודת SQL אחת.

מה נכלל

  • ביטלנו את Python For-Loop לעיבוד נתונים.
  • יצרנו 50,000 וקטורים באמצעות פקודת SQL אחת.
  • הוספנו טריגרים כדי ליצור וקטורים באופן אוטומטי בעתיד.
  • ביצענו חיפוש היברידי.

השלבים הבאים