Utiliser le modèle Jev de TypeSafe AI avec les fonctions AlloyDB AI pour des requêtes d'IA à grande vitesse

1. Introduction

Dans cet atelier de programmation, vous allez connecter le modèle Jev de TypeSafe AI à AlloyDB/AI à l'aide des fonctions d'IA natives d'AlloyDB (ai.if, ai.analyze_sentiment et ml_predict_row). Vous pourrez ainsi exécuter une inférence par lot efficace à l'aide du modèle Jev directement sur vos données opérationnelles dans AlloyDB, sans avoir à déplacer vos données ni à écrire de code complexe côté application.

Objectifs de l'atelier

  • Provisionner un accès aux services privés (ASP), un cluster AlloyDB pour PostgreSQL et une instance principale
  • Créer une règle de pare-feu VPC autorisant Cloud Shell et la connectivité interne
  • Charger un exemple d'ensemble de données d'e-commerce à l'aide de gcloud alloydb clusters import
  • Activer Google Secret Manager et l'extension google_ml_integration d'AlloyDB
  • Stocker votre clé API TypeSafe AI et accorder l'accès à celle-ci dans Secret Manager
  • Créez des fonctions de transformation d'entrée et de sortie PL/pgSQL pour les primitives noul (booléen) et choice (sentiment) de Jev
  • Enregistrer le point de terminaison du modèle jev-model personnalisé dans AlloyDB
  • Exécuter des requêtes sémantiques scalaires et par lots de tableaux (ai.if et ai.analyze_sentiment) directement dans SQL
  • Interroger les avis clients réels sur les produits pour la classification des sentiments à l'aide de ai.analyze_sentiment
  • (Bonus) Enregistrez un point de terminaison générique (jev-systemone) et évaluez l'intensité de la satisfaction client continue à l'aide de la primitive score de Jev

Prérequis

  • Un navigateur Web (par exemple, Chrome)
  • Un projet Google Cloud avec facturation activée
  • Une clé API TypeSafe AI (fournie par TypeSafe AI)

2. Avant de commencer

Sélectionner votre projet Google Cloud

Dans la console Google Cloud, sélectionnez ou créez le projet Google Cloud que vous souhaitez utiliser.

Démarrer Cloud Shell

Cliquez sur Activer Cloud Shell en haut à droite de la console Google Cloud.

Vérifiez que votre session Cloud Shell est authentifiée :

gcloud auth list

Définissez votre ID de projet et vos variables d'environnement :

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"

Activer les API Google Cloud requises

Exécutez la commande suivante pour activer toutes les API de service Google Cloud nécessaires :

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

3. Provisionner le cluster et l'instance principale AlloyDB

Dans cette étape, vous allez attribuer une plage d'adresses IP d'accès aux services privés dans votre réseau VPC, établir un appairage privé et provisionner votre cluster et votre instance principale AlloyDB avec un accès Internet sortant.

1. Créer une plage d'adresses IP pour l'accès aux services privés

AlloyDB nécessite l'attribution d'une plage d'adresses IP privées dans votre réseau cloud privé virtuel (VPC). En supposant que vous utilisiez le réseau VPC default :

Créez l'allocation de la plage d'adresses IP privées :

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

Établissez la connexion d'appairage VPC privée :

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

2. Créer le cluster AlloyDB

Générez un mot de passe de base de données sécurisé pour l'utilisateur avec accès administrateur initial postgres :

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

Créez un cluster AlloyDB en essai sans frais :

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

3. Créer l'instance principale avec des indicateurs d'IA et une connectivité sortante

AlloyDB se connecte directement au point de terminaison SaaS sécurisé de TypeSafe AI (https://api.typesafe.ai/v1/systemone) via HTTPS sortant. Vous devez donc spécifier --outbound-public-ip. Configurez également les options de base de données AlloyDB/AI pour l'environnement :

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. Charger des exemples de données d'e-commerce

Pour tester des requêtes sémantiques réalistes, importez un exemple d'ensemble de données d'e-commerce contenant des catalogues de produits et des avis client directement depuis 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. Stocker la clé API TypeSafe AI dans Secret Manager

La gestion des points de terminaison de modèle d'AlloyDB s'intègre de manière native à Google Secret Manager pour authentifier les appels d'API externes sans exposer les identifiants en texte brut dans le code de la base de données ni dans les journaux de configuration.

1. Créer le secret

Dans Cloud Shell, créez un secret nommé typesafe-jev-api-key :

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

Ajoutez votre clé API TypeSafe AI en tant que version secrète :

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

2. Accorder l'accès à l'agent de service AlloyDB

AlloyDB utilise l'agent de service de son projet pour récupérer les secrets lors de l'exécution des requêtes. Recherchez le numéro de votre projet et attribuez le rôle "Accesseur de secrets" :

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"

Vous devriez voir un résultat confirmant que l'agent de service a reçu roles/secretmanager.secretAccessor.

5. Se connecter à AlloyDB via AlloyDB Studio et initialiser les extensions de base de données

Pour interagir avec votre base de données AlloyDB directement depuis votre navigateur sans configurer d'outils client locaux ni de tunnels proxy, utilisez AlloyDB Studio dans la console Google Cloud.

1. Ouvrir AlloyDB Studio

  1. Dans la console Google Cloud, accédez à AlloyDB pour PostgreSQL > Clusters.
  2. Cliquez sur votre cluster (my-alloydb-cluster).
  3. Dans le menu de navigation de gauche, cliquez sur AlloyDB Studio.
  4. Authentifiez-vous auprès de la base de données :
    • Base de données : postgres
    • Utilisateur : postgres
    • Mot de passe : saisissez le $PGPASSWORD généré lors de la création du cluster (par exemple, exécutez echo $PGPASSWORD dans Cloud Shell pour l'afficher).
  5. Cliquez sur Authentifier.

Vous êtes maintenant dans l'éditeur de requête SQL interactif AlloyDB Studio.

2. Activer les extensions et les indicateurs de base de données

Dans l'éditeur de requête AlloyDB Studio, collez et exécutez les instructions suivantes pour activer l'extension google_ml_integration et configurer les paramètres d'exécution :

-- 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';

Cliquez sur Run (Exécuter) pour exécuter la requête.

3. Enregistrer le secret dans AlloyDB MEM

Indiquez à AlloyDB où trouver votre clé 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. Créer des fonctions de transformation SQL

L'API System One de TypeSafe AI accepte le format JSON structuré avec des primitives de raisonnement distinctes (noul pour les contraintes booléennes, choice pour la classification multiclasse).

La gestion des points de terminaison de modèle d'AlloyDB utilise quatre fonctions de transformation (entrée scalaire, sortie scalaire, entrée par lot, sortie par lot) pour faire le lien entre les signatures de fonction ai.* PostgreSQL standards et le format de l'API REST Jev.

1. Transformation d'entrée scalaire

Ajoutez la fonction suivante pour convertir les appels ai.if et ai.analyze_sentiment à une seule ligne en charges utiles de requête 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. Transformation de la sortie scalaire

Ajoutez la fonction pour décompresser la réponse JSON de Jev dans un résultat TEXT PostgreSQL :

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. Transformation des entrées par lot

Jev peut évaluer des dizaines de questions simultanément dans un seul aller-retour HTTP sans dégradation générative.

Ajoutez la transformation d'entrée par lot pour convertir les tableaux prompts TEXT[] PostgreSQL en questions Jev q_1 .. q_N parallèles :

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. Transformation de la sortie par lot

Ajoutez la fonction permettant d'extraire les réponses dans l'ordre d'index du tableau 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. Enregistrer le point de terminaison du modèle personnalisé dans AlloyDB

Vous allez maintenant associer les fonctions de transformation, les identifiants secrets et le point de terminaison REST TypeSafe AI dans un modèle enregistré nommé jev-model.

1. Enregistrer un jev-model

Dans l'éditeur de requête AlloyDB Studio, collez et exécutez la requête suivante :

-- 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. Valider l'inscription

Interrogez la vue du registre de modèles pour vous assurer que jev-model est actif et reconnu :

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

Un résultat semblable à celui-ci s'affiche :

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

8. Exécuter des requêtes de test sur Jev

Une fois jev-model enregistré, vous pouvez exécuter des opérations sémantiques directement dans les requêtes PostgreSQL standards.

1. Tester le filtrage booléen scalaire (ai.if)

Évaluez une seule instruction textuelle à l'aide de 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;

Un résultat semblable à celui-ci s'affiche :

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

2. Tester l'analyse des sentiments scalaires (ai.analyze_sentiment)

Évaluez les commentaires des clients et attribuez-leur une étiquette de catégorie (positive, negative ou 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;

Un résultat semblable à celui-ci s'affiche :

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

3. Tester le filtrage booléen par lots de tableaux

Évaluez plusieurs éléments dans une seule requête en transmettant un tableau de requêtes :

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;

Un résultat semblable à celui-ci s'affiche :

                             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. Tester les requêtes de table avec le traitement par lot de tableaux

Interrogez maintenant la table ecomm.products chargée précédemment lors du provisionnement du cluster, en utilisant ai.if sur un lot d'articles pour vérifier quels produits du catalogue sont des équipements pour temps froid.

Pour exécuter un filtrage sémantique à haut débit sur les données de table (par exemple, 50 lignes par lot), utilisez la fonction de fenêtrage row_number() de PostgreSQL pour regrouper les lignes en lots :

-- 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;

Un résultat semblable à celui-ci s'affiche :

  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. Interroger les avis sur les produits des clients pour connaître leur sentiment

Analysez maintenant les avis clients du tableau ecomm.product_reviews à l'aide de ai.analyze_sentiment, qui s'appuie sur la primitive choice de 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;

Un résultat semblable à celui-ci s'affiche :

 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. Bonus : Score de sentiment continu avec Jev score

Alors que les fonctions d'IA d'AlloyDB, comme ai.analyze_sentiment, renvoient des catégories (positive, negative, neutral), la primitive score de TypeSafe AI évalue les nuances d'une grille d'évaluation ordonnée et renvoie une note continue pondérée par les attentes, comprise entre 0 et 100.

Étant donné que score renvoie des métadonnées numériques plutôt qu'un libellé de classification textuelle, vous pouvez enregistrer un point de terminaison de modèle generic personnalisé (jev-systemone) et l'appeler à l'aide de google_ml.predict_row.

1. Enregistrer le point de terminaison générique

Dans AlloyDB Studio, enregistrez jev-systemone avec 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. Exécuter une évaluation multitâche (score booléen + continu)

Évaluez simultanément une vérification booléenne des défauts (noul) et une intensité du sentiment continue (score) dans un seul appel 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;

Vous devriez obtenir un résultat semblable à celui-ci (les vôtres peuvent être différents) :

{
  "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
      }
    }
  }
}

Notez que satisfaction_score a été évalué à 11 / 100, ce qui reflète précisément l'expérience plutôt négative du client, même s'il a reconnu que le tissu était doux.

10. Effectuer un nettoyage

Pour éviter que les ressources créées au cours de cet atelier de programmation ne soient facturées en permanence, nettoyez le secret et les objets de base de données, puis supprimez le cluster AlloyDB.

1. Supprimer les ressources Secret Manager

Dans Cloud Shell, supprimez le secret créé pour la clé API TypeSafe AI :

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

2. Supprimer l'instance principale et le cluster AlloyDB

Supprimez d'abord l'instance principale :

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

Une fois la suppression de l'instance terminée, supprimez le cluster :

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

11. Félicitations

Félicitations ! Vous avez provisionné un cluster Google Cloud AlloyDB et l'avez intégré à TypeSafe AI System One (Jev).

Connaissances acquises

  • Comment établir un accès aux services privés (PSA) et provisionner un cluster et une instance principale AlloyDB pour PostgreSQL.
  • Comment activer --outbound-public-ip pour qu'AlloyDB puisse accéder aux API SaaS HTTPS externes.
  • Comment stocker de manière sécurisée les identifiants d'un fournisseur d'IA tiers dans Google Secret Manager et les associer à AlloyDB.
  • Comment écrire des fonctions de transformation PL/pgSQL qui font le lien entre les fonctions d'IA PostgreSQL standards (ai.if et ai.analyze_sentiment) et les API REST externes.
  • Comment enregistrer des points de terminaison LLM personnalisés dans AlloyDB à l'aide de google_ml.create_model.
  • Découvrez comment exécuter des requêtes SQL sémantiques scalaires et par lot à haut débit directement dans PostgreSQL.
  • Comment analyser les avis sur les produits de clients réels pour déterminer la catégorie de sentiment.
  • Découvrez comment enregistrer un point de terminaison de modèle générique et évaluer les scores de satisfaction client continue (0..100) via google_ml.predict_row.

Étapes suivantes

Documents de référence