Cómo usar el modelo Jev de TypeSafe AI con las funciones de AlloyDB AI para realizar consultas de IA de alta velocidad

1. Introducción

En este codelab, conectarás el modelo Jev de TypeSafe AI a AlloyDB AI con las funciones nativas de IA de AlloyDB (ai.if, ai.analyze_sentiment y ml_predict_row). Esto te permitirá ejecutar una inferencia por lotes eficiente con el modelo Jev directamente en tus datos operativos en AlloyDB, sin mover tus datos ni escribir código complejo del lado de la aplicación.

Actividades

  • Aprovisiona el acceso a servicios privados (PSA), un clúster de AlloyDB para PostgreSQL y una instancia principal
  • Crea una regla de firewall de VPC que permita la conectividad interna y de Cloud Shell
  • Carga un conjunto de datos de muestra de comercio electrónico con gcloud alloydb clusters import
  • Habilita Google Secret Manager y la extensión google_ml_integration de AlloyDB
  • Almacena tu clave de API de TypeSafe AI en Secret Manager y otorga acceso a ella
  • Crea funciones de transformación de entrada y salida de PL/pgSQL para las primitivas noul (booleana) y choice (sentimiento) de Jev.
  • Registra el extremo del modelo jev-model personalizado en AlloyDB
  • Ejecuta consultas semánticas escalares y por lotes de arrays (ai.if y ai.analyze_sentiment) directamente en SQL
  • Consultar opiniones de clientes reales sobre productos para la clasificación de opiniones con ai.analyze_sentiment
  • (Bonus) Registra un extremo genérico (jev-systemone) y evalúa la intensidad continua de la satisfacción del cliente con la primitiva score de Jev

Requisitos

  • Un navegador web, como Chrome
  • Un proyecto de Google Cloud con la facturación habilitada.
  • Una clave de API de TypeSafe AI (de TypeSafe AI)

2. Antes de comenzar

Selecciona tu proyecto de Google Cloud.

En la consola de Google Cloud, selecciona o crea el proyecto de Google Cloud que deseas usar.

Inicie Cloud Shell

Haz clic en Activar Cloud Shell en la parte superior derecha de la consola de Google Cloud.

Verifica que tu sesión de Cloud Shell esté autenticada:

gcloud auth list

Establece el ID del proyecto y las variables de entorno:

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"

Habilita las APIs de Google Cloud requeridas

Ejecuta este comando para habilitar todas las APIs de servicios de Google Cloud necesarias:

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

3. Aprovisiona el clúster de AlloyDB y la instancia principal

En este paso, asignarás un rango de IP de acceso privado a servicios en tu red de VPC, establecerás el intercambio de tráfico privado y aprovisionarás tu clúster y tu instancia principal de AlloyDB con acceso a Internet saliente.

1. Crea un rango de IP de acceso privado a servicios (PSA)

AlloyDB requiere una asignación de rango de IP privada en tu red de nube privada virtual (VPC). Si suponemos que usas la red de VPC default, haz lo siguiente:

Crea la asignación del rango de IP privada:

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

Establece la conexión privada de intercambio de tráfico entre VPC:

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

2. Crea el clúster de AlloyDB

Genera una contraseña segura para el usuario administrador inicial postgres:

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

Crea un clúster de prueba gratuita de AlloyDB:

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

3. Crea la instancia principal con marcas de IA y conectividad saliente

AlloyDB se conecta directamente al extremo seguro de SaaS de TypeSafe AI (https://api.typesafe.ai/v1/systemone) a través de HTTPS saliente. Por lo tanto, debes especificar --outbound-public-ip. Además, configura las marcas de la base de datos de AlloyDB AI para el entorno:

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. Carga datos de muestra de comercio electrónico

Para probar consultas semánticas realistas, importa un conjunto de datos de muestra de comercio electrónico que contenga catálogos de productos y opiniones de los clientes directamente desde 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. Almacena la clave de API de TypeSafe AI en Secret Manager

La administración de extremos de modelos de AlloyDB se integra de forma nativa con Google Secret Manager para autenticar llamadas a la API externas sin exponer credenciales de texto simple en el código de la base de datos ni en los registros de configuración.

1. Crea el secreto

En Cloud Shell, crea un secreto nuevo llamado typesafe-jev-api-key:

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

Agrega tu clave de API de TypeSafe AI como una versión secreta:

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

2. Otorga acceso al agente de servicio de AlloyDB

AlloyDB usa su agente de servicio del proyecto para recuperar secretos durante la ejecución de consultas. Busca el número de tu proyecto y otorga el rol de 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"

Deberías ver un resultado que confirme que el agente de servicio recibió roles/secretmanager.secretAccessor.

5. Conéctate a AlloyDB a través de AlloyDB Studio y, luego, inicializa las extensiones de la base de datos

Para interactuar con tu base de datos de AlloyDB directamente desde tu navegador sin configurar herramientas de cliente locales ni túneles de proxy, usa AlloyDB Studio en la consola de Google Cloud.

1. Abre AlloyDB Studio

  1. En la consola de Google Cloud, navega a AlloyDB para PostgreSQL > Clústeres.
  2. Haz clic en tu clúster (my-alloydb-cluster).
  3. En el menú de navegación de la izquierda, haz clic en AlloyDB Studio.
  4. Autentícate en la base de datos:
    • Base de datos: postgres
    • Usuario: postgres
    • Contraseña: Ingresa el $PGPASSWORD generado durante la creación del clúster (p.ej., ejecuta echo $PGPASSWORD en Cloud Shell para verlo).
  5. Haz clic en Autenticar.

Ahora estás en el editor de consultas en SQL interactivo de AlloyDB Studio.

2. Habilita las extensiones y las marcas de bases de datos

En el editor de consultas de AlloyDB Studio, pega y ejecuta las siguientes instrucciones para habilitar la extensión google_ml_integration y configurar los parámetros de ejecución:

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

Haz clic en Ejecutar para ejecutar la consulta.

3. Registra el secreto en AlloyDB MEM

Indícale a AlloyDB dónde encontrar tu clave de API de 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. Crea funciones de transformación de SQL

La API de System One de TypeSafe AI acepta JSON estructurado con primitivas de razonamiento distintas (noul para restricciones booleanas y choice para clasificación de clases múltiples).

La administración de extremos de modelos de AlloyDB usa cuatro funciones de transformación (entrada escalar, salida escalar, entrada por lotes y salida por lotes) para conectar las firmas de funciones ai.* estándar de PostgreSQL con el formato de la API de REST de Jev.

1. Transformación de entrada escalar

Agrega la siguiente función para convertir las llamadas de una sola fila ai.if y ai.analyze_sentiment en cargas útiles de solicitudes de 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. Transformación de salida escalar

Agrega la función para desempaquetar la respuesta JSON de Jev y volver a convertirla en un resultado TEXT de 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. Transformación de entrada por lotes

Jev puede evaluar docenas de preguntas de forma simultánea dentro de un solo viaje de ida y vuelta HTTP sin degradación generativa.

Agrega la transformación de entrada por lotes para convertir los arrays de prompts TEXT[] de PostgreSQL en preguntas de Jev paralelas de q_1 .. q_N:

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. Transformación de salida por lotes

Agrega la función para extraer respuestas en el orden correcto del índice del array 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. Registra el extremo del modelo personalizado en AlloyDB

Ahora vincularás las funciones de transformación, la credencial secreta y el extremo de la API de REST de TypeSafe AI en un modelo registrado llamado jev-model.

1. Registra jev-model

En el editor de consultas de AlloyDB Studio, pega y ejecuta lo siguiente:

-- 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. Verifica el registro

Consulta la vista del registro de modelos para asegurarte de que jev-model esté activo y reconocido:

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

Deberías ver un resultado similar a este:

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

8. Ejecuta consultas de prueba en Jev

Con jev-model registrado, ahora puedes ejecutar operaciones semánticas directamente dentro de las consultas estándar de PostgreSQL.

1. Prueba el filtrado booleano escalar (ai.if)

Evalúa una sola instrucción de texto con 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;

Deberías ver un resultado similar a este:

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

2. Prueba de análisis de opiniones escalar (ai.analyze_sentiment)

Evalúa los comentarios de los clientes y asígnales una etiqueta categórica (positive, negative o 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;

Deberías ver un resultado similar a este:

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

3. Prueba el filtrado booleano por lotes de arrays

Para evaluar varios elementos en una sola consulta, pasa un array de instrucciones:

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;

Deberías ver un resultado similar a este:

                             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. Prueba las consultas de tablas con el procesamiento por lotes de arrays

Ahora, consulta la tabla ecomm.products que se cargó anteriormente durante el aprovisionamiento del clúster con ai.if en un lote de elementos para verificar cuáles de los productos del catálogo son equipos para clima frío.

Para ejecutar un filtrado semántico de alta capacidad de procesamiento en los datos de la tabla (p.ej., 50 filas por lote), usa la función analítica row_number() de PostgreSQL para dividir las filas en lotes:

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

Deberías ver un resultado similar a este:

  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. Consulta las opiniones de los clientes sobre los productos para conocer su sentimiento

Ahora analiza las opiniones de los clientes de la tabla ecomm.product_reviews con ai.analyze_sentiment respaldado por la primitiva 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;

Deberías ver un resultado similar a este:

 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. Contenido adicional: Puntuación continua del sentimiento con la puntuación de Jev

Si bien las funciones basadas en IA de AlloyDB, como ai.analyze_sentiment, devuelven discretizaciones categóricas (positive, negative, neutral), la primitiva score de TypeSafe AI evalúa los matices en una rúbrica ordenada y devuelve una calificación continua ponderada según las expectativas de 0 a 100.

Dado que score devuelve metadatos numéricos en lugar de una etiqueta de clasificación de texto, puedes registrar un extremo de modelo generic personalizado (jev-systemone) y llamarlo con google_ml.predict_row.

1. Registra el extremo genérico

En AlloyDB Studio, registra jev-systemone con 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. Ejecuta una evaluación de varias tareas (puntuación booleana y continua)

Evalúa simultáneamente una verificación booleana de defectos (noul) y una intensidad de opinión continua (score) en una sola llamada 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;

Deberías ver un resultado similar al siguiente (tus resultados pueden variar):

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

Observa que satisfaction_score se evaluó como 11 / 100, lo que capta con precisión que el cliente tuvo una experiencia mayormente negativa a pesar de reconocer que la tela era suave.

10. Limpia

Para evitar que se apliquen cargos continuos por los recursos creados durante este codelab, limpia los objetos secretos y de la base de datos, y borra el clúster de AlloyDB.

1. Borra los recursos de Secret Manager

En Cloud Shell, borra el secreto creado para la clave de API de TypeSafe AI:

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

2. Borra la instancia principal y el clúster de AlloyDB

Primero, borra la instancia principal:

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

Una vez que se complete el borrado de la instancia, borra el clúster:

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

11. Felicitaciones

¡Felicitaciones! Aprovisionaste correctamente un clúster de AlloyDB de Google Cloud y lo integraste en TypeSafe AI System One (Jev).

Qué aprendiste

  • Cómo establecer el acceso privado a servicios (PSA) y aprovisionar un clúster y una instancia principal de AlloyDB para PostgreSQL
  • Cómo habilitar --outbound-public-ip para que AlloyDB pueda acceder a las APIs externas de SaaS a través de HTTPS
  • Cómo almacenar de forma segura las credenciales de proveedores externos de IA en Google Secret Manager y vincularlas a AlloyDB
  • Cómo escribir funciones de transformación en PL/pgSQL que conecten las funciones de IA estándar de PostgreSQL (ai.if y ai.analyze_sentiment) con APIs de REST externas
  • Cómo registrar extremos de LLM personalizados en AlloyDB con google_ml.create_model
  • Cómo ejecutar consultas semánticas en SQL por lotes de alto rendimiento y escalares directamente en PostgreSQL
  • Cómo analizar las opiniones sobre productos reales de los clientes para determinar el sentimiento categórico
  • Cómo registrar un extremo de modelo genérico y evaluar las puntuaciones continuas de satisfacción del cliente (0..100) a través de google_ml.predict_row.

Próximos pasos

Documentos de referencia