שימוש במודל Jev של TypeSafe AI עם פונקציות AI של AlloyDB לשאילתות AI במהירות גבוהה

1. מבוא

ב-Codelab הזה תלמדו איך לחבר את מודל Jev של TypeSafe AI ל-AlloyDB AI באמצעות פונקציות ה-AI המקוריות של AlloyDB‏ (ai.if,‏ ai.analyze_sentiment ו-ml_predict_row). כך תוכלו להריץ הסקה יעילה באצווה באמצעות מודל Jev ישירות על הנתונים התפעוליים שלכם ב-AlloyDB, בלי להעביר את הנתונים או לכתוב קוד מורכב בצד האפליקציה.

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

  • הקצאת גישה לשירותים פרטיים (PSA) ואשכול ומכונה ראשית של AlloyDB ל-PostgreSQL
  • יצירת כלל חומת אש ב-VPC שמאפשר קישוריות פנימית וגישה ל-Cloud Shell
  • טעינת מערך נתונים לדוגמה של מסחר אלקטרוני באמצעות gcloud alloydb clusters import
  • הפעלת Google Secret Manager והתוסף google_ml_integration של AlloyDB
  • איך מאחסנים את מפתח ה-API של TypeSafe AI ב-Secret Manager ומעניקים לו גישה
  • יצירת פונקציות טרנספורמציה של קלט ופלט ב-PL/pgSQL עבור הפרימיטיבים noul (בוליאני) ו-choice (סנטימנט) של Jev
  • רישום של נקודת הקצה של מודל jev-model בהתאמה אישית ב-AlloyDB
  • הרצת שאילתות סמנטיות סקלריות ושאילתות סמנטיות של מערכים (ai.if ו-ai.analyze_sentiment) ישירות ב-SQL
  • מבצעים שאילתה לביקורות אמיתיות של לקוחות על מוצרים כדי לסווג את הסנטימנט באמצעות ai.analyze_sentiment
  • (בונוס) רישום של נקודת קצה כללית (jev-systemone) והערכה של עוצמת שביעות רצון הלקוחות באמצעות הפרימיטיב score של Jev

הדרישות

  • דפדפן אינטרנט כמו Chrome
  • פרויקט ב-Google Cloud שהחיוב בו מופעל
  • מפתח API של TypeSafe AI (מ-TypeSafe AI)

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

בוחרים את הפרויקט ב-Google Cloud

ב-Google Cloud Console, בוחרים את פרויקט Google Cloud שרוצים להשתמש בו או יוצרים פרויקט חדש.

הפעלת Cloud Shell

לוחצים על Activate Cloud Shell (הפעלת Cloud Shell) בפינה השמאלית העליונה של מסוף Google Cloud.

מוודאים שהסשן שלכם ב-Cloud Shell מאומת:

gcloud auth list

מגדירים את מזהה הפרויקט ואת משתני הסביבה:

export PROJECT_ID=$(gcloud config get-value project)
gcloud config set project $PROJECT_ID

export REGION="us-west4"
export ADBCLUSTER="my-alloydb-cluster"
export ADBINSTANCE="my-alloydb-primary"

הפעלה של ממשקי Google Cloud API הנדרשים

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

gcloud services enable \
  alloydb.googleapis.com \
  servicenetworking.googleapis.com \
  secretmanager.googleapis.com \
  compute.googleapis.com

3. הקצאת אשכול AlloyDB ומכונה ראשית

בשלב הזה, תקצו טווח כתובות IP לגישה לשירותים פרטיים ברשת ה-VPC, תיצרו קישור פרטי בין רשתות שכנות ותקצו לאשכול ולמכונה הראשית של AlloyDB גישה לאינטרנט ליציאה.

1. יצירת טווח כתובות IP לגישה לשירותים פרטיים (PSA)

‫AlloyDB דורש הקצאה של טווח כתובות IP פרטיות ברשת של הענן הווירטואלי הפרטי (VPC). בהנחה שאתם משתמשים ברשת default VPC:

יוצרים את הקצאת טווח כתובות ה-IP הפרטיות:

gcloud compute addresses create psa-range \
    --global \
    --purpose=VPC_PEERING \
    --prefix-length=24 \
    --description="VPC private service access" \
    --network=default

יוצרים את חיבור ה-VPC הפרטי:

gcloud services vpc-peerings connect \
    --service=servicenetworking.googleapis.com \
    --ranges=psa-range \
    --network=default

2. יצירת אשכול AlloyDB

יוצרים סיסמה מאובטחת למסד הנתונים עבור משתמש עם הרשאת אדמין postgres הראשוני:

export PGPASSWORD="$(openssl rand -hex 8)@1aA"
echo "Your generated AlloyDB postgres password is: $PGPASSWORD"

יצירת אשכול AlloyDB לתקופת ניסיון בחינם:

gcloud alloydb clusters create $ADBCLUSTER \
    --password=$PGPASSWORD \
    --network=default \
    --region=$REGION 

3. יצירת המופע הראשי עם דגלי AI וקישוריות יוצאת

‫AlloyDB מתחבר ישירות לנקודת קצה מאובטחת של SaaS של TypeSafe AI‏ (https://api.typesafe.ai/v1/systemone) דרך HTTPS יוצא. לכן, צריך לציין --outbound-public-ip. בנוסף, מגדירים את הדגלים של מסד הנתונים AlloyDB AI לסביבה:

gcloud alloydb instances create $ADBINSTANCE \
    --instance-type=PRIMARY \
    --cpu-count=2 \
    --region=$REGION \
    --cluster=$ADBCLUSTER \
    --outbound-public-ip \
    --ssl-mode=ALLOW_UNENCRYPTED_AND_ENCRYPTED \
    --database-flags=\
google_ml_integration.enable_model_support=on,\
google_ml_integration.enable_ai_query_engine=on,\
google_ml_integration.enable_ai_function_acceleration=on,\
google_ml_integration.enable_cost_optimized_ai_functions=on,\
parameterized_views.enabled=on,\
password.enforce_complexity=on,\
password.min_uppercase_letters=1,\
password.min_numerical_chars=1,\
password.min_pass_length=10

4. טעינת נתונים לדוגמה של מסחר אלקטרוני

כדי לבדוק שאילתות סמנטיות ריאליסטיות, מייבאים קבוצת נתונים לדוגמה של מסחר אלקטרוני שמכילה קטלוגים של מוצרים וביקורות של לקוחות ישירות מ-Cloud Storage:

gcloud alloydb clusters import $ADBCLUSTER \
    --region=$REGION \
    --project=$PROJECT_ID \
    --gcs-uri="gs://sample-data-and-media/ecomm-retail/ecom_generic.sql" \
    --sql \
    --database=postgres \
    --user=postgres

4. אחסון מפתח ה-API של TypeSafe AI ב-Secret Manager

ניהול נקודות הקצה של מודלים ב-AlloyDB משולב באופן מקורי עם Google Secret Manager כדי לאמת קריאות חיצוניות ל-API בלי לחשוף פרטי כניסה בטקסט לא מוצפן בקוד של מסד הנתונים או ביומני ההגדרות.

1. יצירת הסוד

ב-Cloud Shell, יוצרים סוד חדש בשם typesafe-jev-api-key:

gcloud secrets create typesafe-jev-api-key \
  --replication-policy="automatic"

מוסיפים את מפתח ה-API של TypeSafe AI כגרסת סוד:

echo -n "<YOUR_TYPESAFE_API_KEY>" | \
  gcloud secrets versions add typesafe-jev-api-key --data-file=-

2. הענקת גישה לסוכן השירות של AlloyDB

‫AlloyDB משתמש בסוכן שירות הפרויקט כדי לאחזר סודות במהלך ביצוע השאילתה. מאתרים את מספר הפרויקט ומקצים את התפקיד Secret Accessor:

PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
ALLOYDB_SA="service-${PROJECT_NUMBER}@gcp-sa-alloydb.iam.gserviceaccount.com"

gcloud secrets add-iam-policy-binding typesafe-jev-api-key \
  --member="serviceAccount:${ALLOYDB_SA}" \
  --role="roles/secretmanager.secretAccessor"

אמור להופיע פלט שמאשר שסוכן השירות קיבל את roles/secretmanager.secretAccessor.

5. התחברות ל-AlloyDB דרך AlloyDB Studio ואתחול של תוספים למסד הנתונים

כדי ליצור אינטראקציה עם מסד הנתונים של AlloyDB ישירות מהדפדפן בלי להגדיר כלי לקוח מקומיים או מנהרות פרוקסי, משתמשים ב-AlloyDB Studio במסוף Google Cloud.

1. פתיחת AlloyDB Studio

  1. ב-מסוף Google Cloud, עוברים אל AlloyDB ל-PostgreSQL > Clusters.
  2. לוחצים על האשכול (my-alloydb-cluster).
  3. בתפריט הניווט השמאלי, לוחצים על AlloyDB Studio.
  4. מאמתים את הגישה למסד הנתונים:
    • מסד נתונים: postgres
    • משתמש: postgres
    • סיסמה: מזינים את הסיסמה $PGPASSWORD שנוצרה במהלך יצירת האשכול (לדוגמה, מריצים את הפקודה echo $PGPASSWORD ב-Cloud Shell כדי לראות אותה).
  5. לוחצים על אימות.

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

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

בעורך השאילתות של AlloyDB Studio, מדביקים את ההצהרות הבאות ומריצים אותן כדי להפעיל את התוסף google_ml_integration ולהגדיר את הגדרות זמן הריצה:

-- Enable the AlloyDB AI integration extension
CREATE EXTENSION IF NOT EXISTS google_ml_integration;

-- Enable preview AI functions (ai.analyze_sentiment, ai.classify)
SET google_ml_integration.enable_preview_ai_functions = 'on';

-- Disable async batching for the session to use custom transform functions directly
SET google_ml_integration.enable_async_operation = 'off';

לוחצים על Run כדי להריץ את השאילתה.

3. רישום הסוד ב-AlloyDB MEM

מציינים ל-AlloyDB איפה נמצא מפתח ה-API של TypeSafe AI:

CALL google_ml.create_sm_secret(
    secret_id => 'jev_api_key',
    secret_path => 'projects/<YOUR_PROJECT_ID>/secrets/typesafe-jev-api-key/versions/latest'
);

6. יצירת פונקציות להמרת SQL

ממשק System One API של TypeSafe AI מקבל JSON מובנה עם פרימיטיבים שונים של חשיבה רציונלית (noul לאילוצים בוליאניים, choice לסיווג רב-מחלקתי).

התכונה 'ניהול נקודות קצה של מודלים' ב-AlloyDB משתמשת בארבע פונקציות טרנספורמציה (קלט סקלרי, פלט סקלרי, קלט אצווה, פלט אצווה) כדי לגשר בין חתימות פונקציות רגילות של PostgreSQL ai.* לבין הפורמט של Jev API בארכיטקטורת REST.

1. טרנספורמציה של קלט סקלרי

מוסיפים את הפונקציה הבאה כדי להמיר קריאות של ai.if ו-ai.analyze_sentiment בשורה אחת למטענים ייעודיים (payloads) של בקשות Jev:

CREATE OR REPLACE FUNCTION public.jev_model_input_transform(
    model_id VARCHAR(100),
    input_text TEXT,
    generation_config JSON,
    system_instruction TEXT
) RETURNS JSON
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
    full_instruction TEXT;
    enum_json JSON;
    criteria_obj JSONB := '{}'::JSONB;
    elem TEXT;
    q_obj JSON;
BEGIN
    -- Combine optional system instructions with the input prompt
    IF system_instruction IS NOT NULL AND length(trim(system_instruction)) > 0 THEN
        full_instruction := system_instruction || E'\n\n' || input_text;
    ELSE
        full_instruction := input_text;
    END IF;

    -- Inspect generation_config to determine whether caller expects a multi-class choice or boolean
    enum_json := COALESCE(
        generation_config->'generationConfig'->'responseSchema'->'enum',
        generation_config->'responseSchema'->'enum'
    );

    IF enum_json IS NOT NULL AND json_typeof(enum_json) = 'array' THEN
        -- Multi-class classification (ai.analyze_sentiment) -> Jev choice primitive
        FOR elem IN SELECT json_array_elements_text(enum_json) LOOP
            criteria_obj := criteria_obj || jsonb_build_object(elem, 'Category: ' || elem);
        END LOOP;
        q_obj := json_build_object(
            'type', 'choice',
            'instructions', full_instruction,
            'criteria', criteria_obj::JSON
        );
    ELSE
        -- Boolean filtering (ai.if) -> Jev noul primitive
        q_obj := json_build_object(
            'type', 'noul',
            'instructions', full_instruction
        );
    END IF;

    RETURN json_build_object(
        'model', 'jev-latest',
        'state', COALESCE(generation_config->>'state', 'Evaluate the question accurately based on the provided text.'),
        'questions', json_build_object('q_1', q_obj)
    );
END;
$$;

2. טרנספורמציה של פלט סקלרי

מוסיפים את הפונקציה כדי לפרוק את תגובת ה-JSON של Jev בחזרה לתוצאה של PostgreSQL TEXT:

CREATE OR REPLACE FUNCTION public.jev_model_output_transform(
    model_id VARCHAR(100),
    response_json JSON
) RETURNS TEXT
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
    q_ans JSON := response_json->'answers'->'q_1';
    ans_type TEXT := q_ans->>'type';
BEGIN
    IF ans_type = 'choice' THEN
        RETURN q_ans->>'choice';
    ELSIF ans_type = 'noul' THEN
        -- Convert probability score (0.00 to 1.00) to boolean text
        RETURN CASE WHEN (q_ans->>'noul')::FLOAT >= 0.50 THEN 'true' ELSE 'false' END;
    ELSE
        RAISE EXCEPTION 'Unexpected Jev response payload: %', response_json::TEXT;
    END IF;
END;
$$;

3. Batch Input Transform

‫Jev יכול להעריך עשרות שאלות בו-זמנית בתוך הלוך ושוב של HTTP אחד, בלי שיהיה שיבוש בתוצאות הגנרטיביות.

מוסיפים את הטרנספורמציה של קלט אצווה כדי להמיר מערכים של PostgreSQL prompts TEXT[] לשאלות מקבילות של q_1 .. q_N Jev:

CREATE OR REPLACE FUNCTION public.jev_model_batch_input_transform(
    model_id VARCHAR(100),
    prompts TEXT[],
    generation_config JSON,
    system_instructions JSON
) RETURNS JSON
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
    questions_obj JSONB := '{}'::JSONB;
    enum_json JSON;
    criteria_obj JSONB := '{}'::JSONB;
    elem TEXT;
    is_choice BOOLEAN := FALSE;
    i INT;
BEGIN
    enum_json := COALESCE(
        generation_config->'generationConfig'->'responseSchema'->'items'->'enum',
        generation_config->'generationConfig'->'responseSchema'->'enum',
        generation_config->'responseSchema'->'items'->'enum',
        generation_config->'responseSchema'->'enum'
    );

    IF enum_json IS NOT NULL AND json_typeof(enum_json) = 'array' THEN
        IF NOT (enum_json::JSONB = '["true", "false"]'::JSONB OR enum_json::JSONB = '["false", "true"]'::JSONB) THEN
            is_choice := TRUE;
            FOR elem IN SELECT json_array_elements_text(enum_json) LOOP
                criteria_obj := criteria_obj || jsonb_build_object(elem, 'Category: ' || elem);
            END LOOP;
        END IF;
    END IF;

    -- Construct parallel questions for every prompt in the array
    FOR i IN 1 .. COALESCE(array_length(prompts, 1), 0) LOOP
        IF is_choice THEN
            questions_obj := questions_obj || jsonb_build_object(
                'q_' || i::TEXT,
                jsonb_build_object(
                    'type', 'choice',
                    'instructions', prompts[i],
                    'criteria', criteria_obj
                )
            );
        ELSE
            questions_obj := questions_obj || jsonb_build_object(
                'q_' || i::TEXT,
                jsonb_build_object(
                    'type', 'noul',
                    'instructions', prompts[i]
                )
            );
        END IF;
    END LOOP;

    RETURN json_build_object(
        'model', 'jev-latest',
        'state', COALESCE(generation_config->>'state', 'Evaluate each question independently based on the record data.'),
        'questions', questions_obj::JSON
    );
END;
$$;

4. Batch Output Transform

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

CREATE OR REPLACE FUNCTION public.jev_model_batch_output_transform(
    model_id VARCHAR(100),
    response JSON
) RETURNS TEXT[]
LANGUAGE plpgsql IMMUTABLE AS $$
DECLARE
    results TEXT[] := ARRAY[]::TEXT[];
    answers JSON := response->'answers';
    i INT := 1;
    q_ans JSON;
    ans_type TEXT;
BEGIN
    LOOP
        q_ans := answers->('q_' || i::TEXT);
        EXIT WHEN q_ans IS NULL;
        ans_type := q_ans->>'type';
        IF ans_type = 'choice' THEN
            results := array_append(results, q_ans->>'choice');
        ELSE
            results := array_append(results, CASE WHEN (q_ans->>'noul')::FLOAT >= 0.50 THEN 'true' ELSE 'false' END);
        END IF;
        i := i + 1;
    END LOOP;
    RETURN results;
END;
$$;

7. רישום נקודת הקצה של המודל המותאם אישית ב-AlloyDB

עכשיו צריך לקשר בין פונקציות ההמרה, פרטי הכניסה הסודיים ונקודת הקצה של TypeSafe AI REST למודל רשום בשם jev-model.

1. רישום של jev-model

בעורך השאילתות של AlloyDB Studio, מדביקים את השאילתה הבאה ומריצים אותה:

-- Drop previous registration if updating
DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM google_ml.model_info_view WHERE model_id = 'jev-model') THEN
        CALL google_ml.drop_model('jev-model');
    END IF;
END $$;

-- Register custom LLM endpoint
CALL google_ml.create_model(
    model_id => 'jev-model',
    model_request_url => 'https://api.typesafe.ai/v1/systemone',
    model_provider => 'custom',
    model_type => 'llm',
    model_qualified_name => 'jev-latest',
    model_auth_type => 'secret_manager',
    model_auth_id => 'jev_api_key',
    model_in_transform_fn => 'jev_model_input_transform',
    model_out_transform_fn => 'jev_model_output_transform',
    model_batch_in_transform_fn => 'jev_model_batch_input_transform',
    model_batch_out_transform_fn => 'jev_model_batch_output_transform'
);

2. אימות הרישום

מריצים שאילתה על התצוגה של רישום המודלים כדי לוודא ש-jev-model פעיל ומזוהה:

SELECT model_id, model_type, model_provider, model_auth_type 
FROM google_ml.model_info_view 
WHERE model_id = 'jev-model';

הפלט אמור להיראות כך:

 model_id | model_type | model_provider | model_auth_type 
----------+------------+----------------+-----------------
 jev-model  | llm        | custom         | secret_manager
(1 row)

8. הרצת שאילתות בדיקה מול Jev

אחרי שרושמים את jev-model, אפשר להריץ פעולות סמנטיות ישירות בתוך שאילתות רגילות של PostgreSQL.

1. בדיקת סינון בוליאני סקלרי (ai.if)

הערכת טענה אחת באמצעות ai.if:

SELECT ai.if(
    prompt => 'Is the product "North Face Waterproof Gore-Tex Hiking Jacket ($249)" suitable for rainy outdoor conditions?',
    model_id => 'jev-model'
) AS is_suitable;

הפלט אמור להיראות כך:

 is_suitable 
-------------
 true
(1 row)

2. בדיקה של ניתוח סנטימנט סקלרי (ai.analyze_sentiment)

ניתוח משוב מלקוחות והקצאת תווית קטגורית (positive, negative או neutral):

SET google_ml_integration.enable_preview_ai_functions = 'on';
SELECT ai.analyze_sentiment(
    input => 'The stitching on this jacket tore on the second day and the zipper jammed completely.',
    model_id => 'jev-model'
) AS sentiment;

הפלט אמור להיראות כך:

 sentiment 
-----------
 negative
(1 row)

3. בדיקה של סינון בוליאני עם מערך אצווה

כדי להעריך כמה פריטים בשאילתה אחת, מעבירים מערך של הנחיות:

SET google_ml_integration.enable_async_operation = 'off';
SELECT unnest(prompts) AS product_prompt,
       unnest(ai.if(prompts => prompts, model_id => 'jev-model')) AS is_cold_weather
FROM (
    SELECT ARRAY[
        'Is "Arctic Expedition Down Parka (-30F)" cold-weather winter outerwear?',
        'Is "Men''s Boardshorts Beach Swim Trunk" cold-weather winter outerwear?',
        'Is "Merino Wool Thermal Base Layer" cold-weather winter outerwear?'
    ] AS prompts
) b;

הפלט אמור להיראות כך:

                             product_prompt                              | is_cold_weather 
-------------------------------------------------------------------------+-----------------
 Is "Arctic Expedition Down Parka (-30F)" cold-weather winter outerwear? | true
 Is "Men's Boardshorts Beach Swim Trunk" cold-weather winter outerwear?  | false
 Is "Merino Wool Thermal Base Layer" cold-weather winter outerwear?      | false
(3 rows)

4. בדיקת שאילתות של טבלאות באמצעות אריזת מערכים

עכשיו מריצים שאילתה בטבלה ecomm.products שנטענה קודם במהלך הקצאת האשכול, באמצעות ai.if על קבוצת פריטים כדי לבדוק אילו מהמוצרים בקטלוג הם ציוד למזג אוויר קר.

כדי להפעיל סינון סמנטי של נתוני טבלה עם תפוקה גבוהה (למשל 50 שורות בכל אצווה), משתמשים בפונקציה אנליטית (window function) row_number() של PostgreSQL כדי לחלק את השורות לאצוות:

-- Evaluate a 50-row batch of real catalog items from ecomm.products
WITH numbered AS (
    SELECT 
        id, 
        name, 
        category,
        product_description,
        ((row_number() OVER (ORDER BY id) - 1) / 50) AS batch_id
    FROM ecomm.products
    ORDER BY id DESC
    LIMIT 50
),
batched AS (
    SELECT 
        batch_id,
        array_agg(id ORDER BY id) AS ids,
        array_agg(name ORDER BY id) AS names,
        array_agg(category ORDER BY id) AS categories,
        array_agg(product_description ORDER BY id) AS descs,
        ai.if(
            prompts => array_agg('Is "' || name || '" designed for cold weather winter outerwear?' ORDER BY id),
            model_id => 'jev-model'
        ) AS decisions
    FROM numbered
    GROUP BY batch_id
),
unrolled AS (
    SELECT 
        b.batch_id,
        u.id,
        u.name,
        u.category,
        u.product_description,
        u.decision AS cold_weather_gear
    FROM batched b
    CROSS JOIN LATERAL unnest(b.ids, b.names, b.categories, b.descs, b.decisions) 
      AS u(id, name, category, product_description, decision)
)
SELECT id, name, category, product_description, cold_weather_gear
FROM unrolled
WHERE cold_weather_gear = TRUE
LIMIT 5;

הפלט אמור להיראות כך:

  id   |                                 name                                 |  category   |                                                product_description                                                 | cold_weather_gear 
-------+----------------------------------------------------------------------+-------------+--------------------------------------------------------------------------------------------------------------------+-------------------
 29071 | Winter Striped Beanie with Pom                                       | Accessories | Brave the chill in style with the SoleStyle Winter Striped Beanie! This cozy beanie adds a pop of personality...  | true
 29077 | Knit Collegiate Rugby Stripe Winter Scarf & Beanie Hat Set           | Accessories | Stay cozy and show off your sporty side with our Knit Collegiate Rugby Stripe Scarf & Beanie Set...               | true
 29083 | Junction Finds Men's Muscle Wool Blend Trapper Hat                   | Accessories | Brave the elements in style with the Junction Finds Trapper Hat! This wool-blend hat will keep you warm...        | true
 29086 | Mens Soft Thermal Insulated Wrist Length Leather Gloves - Black      | Accessories | Brave the chill in style with these supple leather gloves from Gavel Goods! Lined with a soft, thermal knit...    | true
 29090 | Long Beanie-Red W16S24E                                              | Accessories | Stay cozy and stylish all season long with this vibrant red beanie from Enchant! This isn't just any hat...       | true
(5 rows)

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

עכשיו מנתחים את ביקורות הלקוחות מהטבלה ecomm.product_reviews באמצעות ai.analyze_sentiment שמבוסס על הפרימיטיב choice של Jev:

SET google_ml_integration.enable_preview_ai_functions = 'on'
SELECT 
    r.id,
    p.name AS product_name,
    r.rating AS customer_star_rating,
    substring(r.review_text FROM 1 FOR 70) || '...' AS review_snippet,
    ai.analyze_sentiment(
        input => r.review_text,
        model_id => 'jev-model'
    ) AS detected_sentiment
FROM ecomm.product_reviews r
JOIN ecomm.products p ON r.product_id = p.id
ORDER BY r.id
LIMIT 5;

הפלט אמור להיראות כך:

 id |                  product_name                  | customer_star_rating |                             review_snippet                              | detected_sentiment 
----+------------------------------------------------+----------------------+-------------------------------------------------------------------------+--------------------
  1 | Hugs & Kisses Juniors Peplum Skirt             |                    1 | This skirt was poorly made and the fabric felt extremely cheap for for...| negative
  2 | Bonkers Larry Full Length Pull on Pants        |                    1 | These pants are way too expensive for the terrible quality you get. Th...| negative
  3 | Gifts and Garb Brah Bra Extenders              |                    3 | These extenders are okay but nothing special. They do add a bit of ext...| neutral
  4 | Auto Forge Men's Tall Duck Traditional Coat    |                    1 | This coat is terribly stiff and heavy, making it very uncomfortable to...| negative
  5 | Weaver's Way Men's Shetland Zip Neck Sweater   |                    2 | This sweater is just okay and not really worth the high price tag. The...| negative
(5 rows)

9. בונוס: ניתוח סנטימנט רציף באמצעות Jev score

בעוד שפונקציות ה-AI של AlloyDB כמו ai.analyze_sentiment מחזירות קטגוריות (positive, ‏ negative, ‏ neutral), הפרימיטיב score של TypeSafe AI מעריך ניואנסים לפי קריטריוני הערכה מסודרים ומחזיר דירוג רציף שמשוקלל לפי ציפיות, מ-0 עד 100.

מכיוון ש-score מחזיר מטא-נתונים מספריים ולא תווית סיווג טקסט, אפשר לרשום נקודת קצה של מודל מותאם אישית של generic (jev-systemone) ולהפעיל אותה באמצעות google_ml.predict_row.

1. רישום נקודת קצה כללית

ב-AlloyDB Studio, רושמים את jev-systemone באמצעות model_type => 'generic':

DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM google_ml.model_info_view WHERE model_id = 'jev-systemone') THEN
        CALL google_ml.drop_model('jev-systemone');
    END IF;
EXCEPTION WHEN OTHERS THEN
    NULL;
END $$;

CALL google_ml.create_model(
    model_id => 'jev-systemone',
    model_request_url => 'https://api.typesafe.ai/v1/systemone',
    model_provider => 'custom',
    model_type => 'generic',
    model_qualified_name => 'jev-latest',
    model_auth_type => 'secret_manager',
    model_auth_id => 'jev_api_key'
);

2. הרצת הערכה של כמה משימות (בוליאני + ציון רציף)

הערכה של בדיקה בוליאנית של פגם (noul) ושל עוצמת סנטימנט רציפה (score) בו-זמנית בקריאת HTTP אחת:

SELECT jsonb_pretty(google_ml.predict_row(
    model_id => 'jev-systemone',
    request_body => json_build_object(
        'model', 'jev-latest',
        'state', 'Review for North Face Parka: "The zipper broke on day 3 and customer service refused an exchange, though the fabric itself is very soft."',
        'questions', json_build_object(
            'has_defect', json_build_object(
                'type', 'noul',
                'instructions', 'Does this review report a physical product defect or failure?'
            ),
            'satisfaction_score', json_build_object(
                'type', 'score',
                'instructions', 'Rate the customer sentiment and satisfaction on a 1-5 scale.',
                'criteria', json_build_array(
                    '1: Extremely dissatisfied / defect failure',
                    '2: Dissatisfied',
                    '3: Mixed or neutral',
                    '4: Satisfied',
                    '5: Delighted'
                )
            )
        )
    )
)::jsonb) AS jev_analysis;

הפלט שיוצג אמור להיות דומה לזה (יכול להיות שיהיו הבדלים בתוצאות):

{
  "model": "jev-1.13.0",
  "usage": {
    "input_tokens": 388,
    "output_tokens": 39
  },
  "answers": {
    "has_defect": {
      "noul": 0.98,
      "type": "noul"
    },
    "satisfaction_score": {
      "type": "score",
      "score": 0.11,
      "legend": {
        "0": "1: Extremely dissatisfied / defect failure",
        "1": "2: Dissatisfied",
        "2": "3: Mixed or neutral",
        "3": "4: Satisfied",
        "4": "5: Delighted"
      },
      "confidence": 0.91,
      "probabilities": {
        "0": 0.9,
        "1": 0.08,
        "2": 0.02,
        "3": 0.0,
        "4": 0.0
      }
    }
  }
}

שימו לב שהערך של satisfaction_score הוא 11 / 100, ושהוא משקף בצורה מדויקת את העובדה שללקוח הייתה חוויה שלילית ברובה, למרות שהוא ציין שהבד רך.

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

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

1. מחיקת משאבים ב-Secret Manager

ב-Cloud Shell, מוחקים את הסוד שנוצר עבור מפתח ה-API של TypeSafe AI:

gcloud secrets delete typesafe-jev-api-key --quiet

2. מחיקת המכונה הראשית ומקבץ AlloyDB

קודם מוחקים את המכונה הראשית:

gcloud alloydb instances delete $ADBINSTANCE \
  --cluster=$ADBCLUSTER \
  --region=$REGION \
  --quiet

אחרי שמחיקת המכונה מסתיימת, מוחקים את האשכול:

gcloud alloydb clusters delete $ADBCLUSTER \
  --region=$REGION \
  --quiet

11. מזל טוב

מעולה! הקצאתם בהצלחה אשכול AlloyDB ב-Google Cloud ושילבתם אותו עם TypeSafe AI System One ‏ (Jev).

מה למדתם

  • איך להגדיר גישה פרטית לשירותים (PSA) ולספק אשכול AlloyDB ל-PostgreSQL ומכונה ראשית.
  • איך מפעילים את --outbound-public-ip כדי ש-AlloyDB יוכל לגשת לממשקי API חיצוניים של SaaS מסוג HTTPS.
  • איך מאחסנים פרטי כניסה של ספק AI מצד שלישי באופן מאובטח ב-Google Secret Manager ומקשרים אותם ל-AlloyDB.
  • איך כותבים פונקציות טרנספורמציה ב-PL/pgSQL שמגשרות בין פונקציות AI סטנדרטיות של PostgreSQL ‏ (ai.if ו-ai.analyze_sentiment) לבין ממשקי REST API חיצוניים.
  • איך רושמים נקודות קצה של מודלים מותאמים אישית של LLM ב-AlloyDB באמצעות google_ml.create_model.
  • איך מריצים שאילתות SQL סמנטיות, גם סקלריות וגם כאלה שמעבדות נפח גדול של נתונים, ישירות ב-PostgreSQL.
  • איך לנתח ביקורות אמיתיות של לקוחות על מוצרים כדי להבין את הסנטימנט שלהם לגבי קטגוריות.
  • איך רושמים נקודת קצה של מודל גנרי ומעריכים ציוני שביעות רצון לקוחות (0..100) באופן רציף דרך google_ml.predict_row.

השלבים הבאים

מסמכי עזר