AlloyDB Omni e modelo de IA local no Kubernetes.

1. Introdução

Neste codelab, você vai aprender a implantar o AlloyDB Omni no GKE e usá-lo com um modelo de incorporação aberto implantado no mesmo cluster do Kubernetes. A implantação de um modelo ao lado da instância do banco de dados no mesmo cluster do GKE reduz a latência e as dependências de serviços de terceiros. Além disso, pode ser exigido por requisitos de segurança quando os dados não podem sair da organização e o uso de serviços de terceiros não é permitido.

391e4244b25a7db0.png

Pré-requisitos

  • Conhecimento básico do console do Google Cloud
  • Habilidades básicas na interface de linha de comando e no Cloud Shell

O que você vai aprender

  • Como implantar o AlloyDB Omni no cluster do Google Kubernetes
  • Como se conectar ao AlloyDB Omni
  • Como carregar dados para o AlloyDB Omni
  • Como implantar um modelo de incorporação aberto no GKE
  • Como registrar o modelo de incorporação no AlloyDB Omni
  • Como gerar embeddings para pesquisa semântica
  • Como usar embeddings gerados para pesquisa semântica no AlloyDB Omni
  • Como criar e usar índices de vetores no AlloyDB

O que é necessário

  • Uma conta e um projeto do Google Cloud
  • Um navegador da Web, como o Chrome, com suporte para o console do Google Cloud e o Cloud Shell

2. Configuração e requisitos

Configuração de ambiente autoguiada

  1. Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.

fbef9caa1602edd0.png

a99b7ace416376c4.png

5e3ff691252acf41.png

  • O Nome do projeto é o nome de exibição para os participantes do projeto. É uma string de caracteres não usada pelas APIs do Google e pode ser atualizada quando você quiser.
  • O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser mudado após a definição. O console do Cloud gera automaticamente uma string exclusiva. Em geral, não importa o que seja. Na maioria dos codelabs, é necessário fazer referência ao ID do projeto, normalmente identificado como PROJECT_ID. Se você não gostar do ID gerado, crie outro aleatório. Se preferir, teste o seu e confira se ele está disponível. Ele não pode ser mudado após essa etapa e permanece durante o projeto.
  • Para sua informação, há um terceiro valor, um Número do projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
  1. Em seguida, ative o faturamento no console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não vai ser muito cara, se tiver algum custo. Para encerrar os recursos e evitar cobranças além deste tutorial, exclua os recursos criados ou exclua o projeto. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.

Inicie o Cloud Shell

Embora o Google Cloud e o Spanner possam ser operados remotamente do seu laptop, neste codelab usaremos o Google Cloud Shell, um ambiente de linha de comando executado no Cloud.

No Console do Google Cloud, clique no ícone do Cloud Shell na barra de ferramentas superior à direita:

55efc1aaa7a4d3ad.png

O provisionamento e a conexão com o ambiente levarão apenas alguns instantes para serem concluídos: Quando o processamento for concluído, você verá algo como:

7ffe5cbb04455448.png

Essa máquina virtual contém todas as ferramentas de desenvolvimento necessárias. Ela oferece um diretório principal persistente de 5 GB, além de ser executada no Google Cloud. Isso aprimora o desempenho e a autenticação da rede. Neste codelab, todo o trabalho pode ser feito com um navegador. Você não precisa instalar nada.

3. Antes de começar

Ativar API

Saída:

No Cloud Shell, verifique se o ID do projeto está configurado:

PROJECT_ID=$(gcloud config get-value project)
echo $PROJECT_ID

Se não estiver definido na configuração do Cloud Shell, configure-o usando os comandos a seguir.

export PROJECT_ID=<your project>
gcloud config set project $PROJECT_ID

Ative todos os serviços necessários:

gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com

Resultado esperado

student@cloudshell:~ (test-project-001-402417)$ PROJECT_ID=test-project-001-402417
student@cloudshell:~ (test-project-001-402417)$ gcloud config set project test-project-001-402417
Updated property [core/project].
student@cloudshell:~ (test-project-001-402417)$ gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
Operation "operations/acat.p2-4470404856-1f44ebd8-894e-4356-bea7-b84165a57442" finished successfully.

4. Implantar o AlloyDB Omni no GKE

Para implantar o AlloyDB Omni no GKE, precisamos preparar um cluster do Kubernetes seguindo os requisitos listados nos requisitos do operador do AlloyDB Omni.

Criar um cluster do GKE

Precisamos implantar um cluster padrão do GKE com uma configuração de pool suficiente para implantar um pod com uma instância do AlloyDB Omni. Para o Omni, precisamos de pelo menos 2 CPUs e 8 GB de RAM, com espaço para serviços de operador e monitoramento.

Configure as variáveis de ambiente para a implantação.

export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=e2-standard-4

Em seguida, usamos o gcloud para criar o cluster padrão do GKE.

gcloud container clusters create ${CLUSTER_NAME} \
  --project=${PROJECT_ID} \
  --region=${LOCATION} \
  --workload-pool=${PROJECT_ID}.svc.id.goog \
  --release-channel=rapid \
  --machine-type=${MACHINE_TYPE} \
  --num-nodes=1

Saída esperada do console:

student@cloudshell:~ (gleb-test-short-001-415614)$ export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
export MACHINE_TYPE=n2-highmem-2
Your active configuration is: [gleb-test-short-001-415614]
student@cloudshell:~ (gleb-test-short-001-415614)$ gcloud container clusters create ${CLUSTER_NAME} \
  --project=${PROJECT_ID} \
  --region=${LOCATION} \
  --workload-pool=${PROJECT_ID}.svc.id.goog \
  --release-channel=rapid \
  --machine-type=${MACHINE_TYPE} \
  --num-nodes=1
Note: The Kubelet readonly port (10255) is now deprecated. Please update your workloads to use the recommended alternatives. See https://cloud.google.com/kubernetes-engine/docs/how-to/disable-kubelet-readonly-port for ways to check usage and for migration instructions.
Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s).
Creating cluster alloydb-ai-gke in us-central1..


NAME: omni01
ZONE: us-central1-a
MACHINE_TYPE: e2-standard-4
PREEMPTIBLE: 
INTERNAL_IP: 10.128.0.3
EXTERNAL_IP: 35.232.157.123
STATUS: RUNNING
student@cloudshell:~ (gleb-test-short-001-415614)$ 

Preparar o cluster

Precisamos instalar os componentes necessários, como o serviço cert-manager. Podemos seguir as etapas na documentação para instalação do cert-manager.

Usamos a ferramenta de linha de comando do Kubernetes, kubectl, que já está instalada no Cloud Shell. Antes de usar o utilitário, precisamos receber as credenciais do cluster.

gcloud container clusters get-credentials ${CLUSTER_NAME} --region=${LOCATION}

Agora podemos usar o kubectl para instalar o cert-manager:

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml

Saída esperada do console (editada):

student@cloudshell:~$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml
namespace/cert-manager created
customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created
customresourcedefinition.apiextensions.k8s.io/clusterissuers.cert-manager.io created
...
validatingwebhookconfiguration.admissionregistration.k8s.io/cert-manager-webhook created

Instalar o AlloyDB Omni

O operador AlloyDB Omni pode ser instalado usando o utilitário helm.

Execute o comando abaixo para instalar o operador AlloyDB Omni:

export GCS_BUCKET=alloydb-omni-operator
export HELM_PATH=$(gcloud storage cat gs://$GCS_BUCKET/latest)
export OPERATOR_VERSION="${HELM_PATH%%/*}"
gcloud storage cp gs://$GCS_BUCKET/$HELM_PATH ./ --recursive
helm install alloydbomni-operator alloydbomni-operator-${OPERATOR_VERSION}.tgz \
--create-namespace \
--namespace alloydb-omni-system \
--atomic \
--timeout 5m

Saída esperada do console (editada):

student@cloudshell:~$ gcloud storage cp gs://$GCS_BUCKET/$HELM_PATH ./ --recursive
Copying gs://alloydb-omni-operator/1.2.0/alloydbomni-operator-1.2.0.tgz to file://./alloydbomni-operator-1.2.0.tgz
  Completed files 1/1 | 126.5kiB/126.5kiB
student@cloudshell:~$ helm install alloydbomni-operator alloydbomni-operator-${OPERATOR_VERSION}.tgz \
> --create-namespace \
> --namespace alloydb-omni-system \
> --atomic \
> --timeout 5m
NAME: alloydbomni-operator
LAST DEPLOYED: Mon Jan 20 13:13:20 2025
NAMESPACE: alloydb-omni-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
student@cloudshell:~$

Quando o operador do AlloyDB Omni estiver instalado, poderemos acompanhar a implantação do cluster do banco de dados.

Confira um exemplo de manifesto de implantação com o parâmetro googleMLExtension ativado e o balanceador de carga interno (privado):

apiVersion: v1
kind: Secret
metadata:
  name: db-pw-my-omni
type: Opaque
data:
  my-omni: "VmVyeVN0cm9uZ1Bhc3N3b3Jk"
---
apiVersion: alloydbomni.dbadmin.goog/v1
kind: DBCluster
metadata:
  name: my-omni
spec:
  databaseVersion: "15.7.0"
  primarySpec:
    adminUser:
      passwordRef:
        name: db-pw-my-omni
    features:
      googleMLExtension:
        enabled: true
    resources:
      cpu: 1
      memory: 8Gi
      disks:
      - name: DataDisk
        size: 20Gi
        storageClass: standard
    dbLoadBalancerOptions:
      annotations:
        networking.gke.io/load-balancer-type: "internal"
  allowExternalIncomingTraffic: true

O valor secreto da senha é uma representação Base64 da palavra de senha "VeryStrongPassword". A maneira mais confiável é usar o Secret Manager do Google para armazenar o valor da senha. Leia mais sobre isso na documentação.

Salve o manifesto como my-omni.yaml para ser aplicado na próxima etapa. Se você estiver no Cloud Shell, use o editor pressionando o botão "Abrir editor" no canto superior direito do terminal.

47ab85dad9afdff7.png

Depois de salvar o arquivo com o nome my-omni.yaml, volte ao terminal pressionando o botão "Abrir terminal".

b9b7747b39dbe8c7.png

Aplique o manifesto my-omni.yaml ao cluster usando o utilitário kubectl:

kubectl apply -f my-omni.yaml

Saída esperada do console:

secret/db-pw-my-omni created
dbcluster.alloydbomni.dbadmin.goog/my-omni created

Verifique o status do cluster my-omni usando o utilitário kubectl:

kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default

Durante a implantação, o cluster passa por diferentes fases e, por fim, deve terminar com o estado DBClusterReady.

Saída esperada do console:

$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
NAME      PRIMARYENDPOINT   PRIMARYPHASE   DBCLUSTERPHASE   HAREADYSTATUS   HAREADYREASON
my-omni   10.131.0.33        Ready          DBClusterReady

Conectar-se ao AlloyDB Omni

Conectar usando o pod do Kubernetes

Quando o cluster estiver pronto, poderemos usar os binários do cliente PostgreSQL no pod da instância do AlloyDB Omni. Encontramos o ID do pod e usamos o kubectl para nos conectarmos diretamente ao pod e executar o software cliente. A senha é "VeryStrongPassword", definida pelo hash em my-omni.yaml:

DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres

Exemplo de saída do console:

DB_CLUSTER_NAME=my-omni
DB_CLUSTER_NAMESPACE=default
DBPOD=`kubectl get pod --selector=alloydbomni.internal.dbadmin.goog/dbcluster=$DB_CLUSTER_NAME,alloydbomni.internal.dbadmin.goog/task-type=database -n $DB_CLUSTER_NAMESPACE -o jsonpath='{.items[0].metadata.name}'`
kubectl exec -ti $DBPOD -n $DB_CLUSTER_NAMESPACE -c database -- psql -h localhost -U postgres
Password for user postgres: 
psql (15.7)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_128_GCM_SHA256, compression: off)
Type "help" for help.

postgres=#

5. Implantar o modelo de IA no GKE

Para testar a integração da IA do AlloyDB Omni com modelos locais, precisamos implantar um modelo no cluster.

Criar um pool de nós para o modelo

Para executar o modelo, precisamos preparar um pool de nós para executar a inferência. A melhor abordagem do ponto de vista de desempenho é um pool com aceleradores gráficos usando uma configuração de nó como g2-standard-8 com acelerador L4 da Nvidia.

Crie o pool de nós com o acelerador L4:

export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container node-pools create gpupool \
  --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
  --project=${PROJECT_ID} \
  --location=${LOCATION} \
  --node-locations=${LOCATION}-a \
  --cluster=${CLUSTER_NAME} \
  --machine-type=g2-standard-8 \
  --num-nodes=1

Resultado esperado

student@cloudshell$ export PROJECT_ID=$(gcloud config get project)
Your active configuration is: [pant]
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
student@cloudshell$ gcloud container node-pools create gpupool \
>   --accelerator type=nvidia-l4,count=1,gpu-driver-version=latest \
>   --project=${PROJECT_ID} \
>   --location=${LOCATION} \
>   --node-locations=${LOCATION}-a \
>   --cluster=${CLUSTER_NAME} \
>   --machine-type=g2-standard-8 \
>   --num-nodes=1
Note: Machines with GPUs have certain limitations which may affect your workflow. Learn more at https://cloud.google.com/kubernetes-engine/docs/how-to/gpus
Note: Starting in GKE 1.30.1-gke.115600, if you don't specify a driver version, GKE installs the default GPU driver for your node's GKE version.
Creating node pool gpupool...done.
Created [https://container.googleapis.com/v1/projects/student-test-001/zones/us-central1/clusters/alloydb-ai-gke/nodePools/gpupool].
NAME     MACHINE_TYPE   DISK_SIZE_GB  NODE_VERSION
gpupool  g2-standard-8  100           1.31.4-gke.1183000

Preparar o manifesto de implantação

Para implantar o modelo, precisamos preparar um manifesto de implantação.

Estamos usando o modelo de embedding BGE Base v1.5 do Hugging Face. Leia o card de modelo aqui. Para implantar o modelo, podemos usar as instruções já preparadas do Hugging Face e o pacote de implantação do GitHub.

Clonar o pacote

git clone https://github.com/huggingface/Google-Cloud-Containers

Edite o manifesto substituindo o valor cloud.google.com/gke-accelerator pelo nvidia-l4 e adicionando limites aos recursos.

vi Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config/deployment.yaml

Confira um manifesto corrigido.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: tei-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tei-server
  template:
    metadata:
      labels:
        app: tei-server
        hf.co/model: Snowflake--snowflake-arctic-embed-m
        hf.co/task: text-embeddings
    spec:
      containers:
        - name: tei-container
          image: us-docker.pkg.dev/deeplearning-platform-release/gcr.io/huggingface-text-embeddings-inference-cu122.1-4.ubuntu2204:latest
          resources:
            requests:
              nvidia.com/gpu: 1
            limits:
              nvidia.com/gpu: 1
          env:
            - name: MODEL_ID
              value: Snowflake/snowflake-arctic-embed-m
            - name: NUM_SHARD
              value: "1"
            - name: PORT
              value: "8080"
          volumeMounts:
            - mountPath: /dev/shm
              name: dshm
            - mountPath: /data
              name: data
      volumes:
        - name: dshm
          emptyDir:
            medium: Memory
            sizeLimit: 1Gi
        - name: data
          emptyDir: {}
      nodeSelector:
        cloud.google.com/gke-accelerator: nvidia-l4

Implantar o modelo

Precisamos preparar uma conta de serviço e um namespace para a implantação.

Crie um namespace do Kubernetes hf-gke-namespace.

export NAMESPACE=hf-gke-namespace
kubectl create namespace $NAMESPACE

Criar uma conta de serviço do Kubernetes

export SERVICE_ACCOUNT=hf-gke-service-account
kubectl create serviceaccount $SERVICE_ACCOUNT --namespace $NAMESPACE

Implantar o modelo

kubectl apply -f Google-Cloud-Containers/examples/gke/tei-deployment/gpu-config

Verificar as implantações

kubectl get pods

Verificar o serviço do modelo

kubectl get service tei-service

Ele deve mostrar o tipo de serviço em execução ClusterIP.

Exemplo de resposta:

student@cloudshell$ kubectl get service tei-service
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
tei-service   ClusterIP   34.118.233.48   <none>        8080/TCP   10m

O CLUSTER-IP do serviço é o que vamos usar como endereço do endpoint. A incorporação do modelo pode responder pelo URI http://34.118.233.48:8080/embed. Ele será usado mais tarde quando você registrar o modelo no AlloyDB Omni.

Podemos testar expondo o comando kubectl port-forward.

kubectl port-forward service/tei-service 8080:8080

O encaminhamento de portas vai ser executado em uma sessão do Cloud Shell, e precisamos de outra sessão para testá-lo.

Abra outra guia do Cloud Shell pelo sinal "+" na parte superior.

4ca978f5142bb6ce.png

E execute um comando curl na nova sessão de shell.

curl http://localhost:8080/embed \
    -X POST \
    -d '{"inputs":"Test"}' \
    -H 'Content-Type: application/json'

Ele precisa retornar um array de vetores, como na saída de exemplo a seguir (reduzida):

curl http://localhost:8080/embed \
>     -X POST \
>     -d '{"inputs":"Test"}' \
>     -H 'Content-Type: application/json'
[[-0.018975832,0.0071419072,0.06347208,0.022992613,0.014205903
...
-0.03677433,0.01636146,0.06731572]]

6. Registrar o modelo no AlloyDB Omni

Para testar como o AlloyDB Omni funciona com o modelo implantado, precisamos criar um banco de dados e registrar o modelo.

Criar banco de dados

Crie uma VM do GCE como uma jump box, conecte-se ao AlloyDB Omni pela VM do cliente e crie um banco de dados.

Precisamos do jump box porque o balanceador de carga externo do GKE para Omni dá acesso à VPC usando endereços IP privados, mas não permite a conexão de fora da VPC. Ele é mais seguro em geral e não expõe sua instância de banco de dados à Internet. Confira o diagrama para mais clareza.

391e4244b25a7db0.png

Para criar uma VM na sessão do Cloud Shell, execute:

export ZONE=us-central1-a
gcloud compute instances create instance-1 \
    --zone=$ZONE 

Encontre o IP do endpoint do AlloyDB Omni usando o kubectl no Cloud Shell:

kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default

Anote o PRIMARYENDPOINT. Veja um exemplo.

saída:

student@cloudshell:~$ kubectl get dbclusters.alloydbomni.dbadmin.goog my-omni -n default
NAME      PRIMARYENDPOINT   PRIMARYPHASE   DBCLUSTERPHASE   HAREADYSTATUS   HAREADYREASON
my-omni   10.131.0.33        Ready          DBClusterReady
student@cloudshell:~$

O 10.131.0.33 é o IP que vamos usar nos exemplos para nos conectar à instância do AlloyDB Omni.

Conecte-se à VM usando o gcloud:

gcloud compute ssh instance-1 --zone=$ZONE 

Se for solicitado para geração de chaves SSH, siga as instruções. Leia mais sobre a conexão SSH na documentação.

Na sessão SSH para a VM, instale o cliente PostgreSQL:

sudo apt-get update
sudo apt-get install --yes postgresql-client

Exporte o IP do balanceador de carga do AlloyDB Omni, conforme o exemplo abaixo (substitua o IP pelo IP do seu balanceador de carga):

export INSTANCE_IP=10.131.0.33

Conecte-se ao AlloyDB Omni. A senha é VeryStrongPassword, definida pelo hash em my-omni.yaml:

psql "host=$INSTANCE_IP user=postgres sslmode=require"

Na sessão psql estabelecida, execute:

create database demo;

Saia da sessão e conecte-se à demonstração do banco de dados. Você também pode executar "\c demo" na mesma sessão.

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Criar funções de transformação

Para modelos de incorporação de terceiros, precisamos criar funções de transformação que formatem a entrada e a saída no formato esperado pelo modelo e pelas nossas funções internas.

Esta é a função de transformação que processa a entrada:

-- Input Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_input JSON;
  model_qualified_name TEXT;
BEGIN
  SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
  RETURN transformed_input;
END;
$$;

Execute o código fornecido enquanto estiver conectado ao banco de dados de demonstração, conforme mostrado na saída de exemplo:

demo=# -- Input Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_input_transform(model_id VARCHAR(100), input_text TEXT)
RETURNS JSON
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_input JSON;
  model_qualified_name TEXT;
BEGIN
  SELECT json_build_object('inputs', input_text, 'truncate', true)::JSON INTO transformed_input;
  RETURN transformed_input;
END;
$$;
CREATE FUNCTION
demo=#

E aqui está a função de saída que transforma a resposta do modelo na matriz de números reais:

-- Output Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON)
RETURNS REAL[]
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_output REAL[];
BEGIN
  SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output;
  RETURN transformed_output;
END;
$$;

Execute na mesma sessão:

demo=# -- Output Transform Function corresponding to the custom model endpoint
CREATE OR REPLACE FUNCTION tei_text_output_transform(model_id VARCHAR(100), response_json JSON)
RETURNS REAL[]
LANGUAGE plpgsql
AS $$
DECLARE
  transformed_output REAL[];
BEGIN
  SELECT ARRAY(SELECT json_array_elements_text(response_json->0)) INTO transformed_output;
  RETURN transformed_output;
END;
$$;
CREATE FUNCTION
demo=#

Registrar o modelo

Agora podemos registrar o modelo no banco de dados.

Esta é a chamada de procedimento para registrar o modelo com o nome bge-base-1.5. Substitua o IP 34.118.233.48 pelo endereço IP do serviço do modelo (a saída de kubectl get service tei-service):

CALL
  google_ml.create_model(
    model_id => 'bge-base-1.5',
    model_request_url => 'http://34.118.233.48:8080/embed',
    model_provider => 'custom',
    model_type => 'text_embedding',
    model_in_transform_fn => 'tei_text_input_transform',
    model_out_transform_fn => 'tei_text_output_transform');

Execute o código fornecido enquanto estiver conectado ao banco de dados de demonstração:

demo=# CALL
  google_ml.create_model(
    model_id => 'bge-base-1.5',
    model_request_url => 'http://34.118.233.48:8080/embed',
    model_provider => 'custom',
    model_type => 'text_embedding',
    model_in_transform_fn => 'tei_text_input_transform',
    model_out_transform_fn => 'tei_text_output_transform');
CALL
demo=#

Podemos testar o modelo de registro usando a consulta de teste a seguir, que vai retornar uma matriz de números reais.

select google_ml.embedding('bge-base-1.5','What is AlloyDB Omni?');

7. Testar o modelo no AlloyDB Omni

Carregar dados

Para testar como o AlloyDB Omni funciona com o modelo implantado, precisamos carregar alguns dados. Usei os mesmos dados de um dos outros codelabs para a pesquisa de vetores no AlloyDB.

Uma maneira de carregar os dados é usar o SDK do Google Cloud e o software cliente do PostgreSQL. Podemos usar a mesma VM de cliente usada para criar o banco de dados de demonstração. O SDK do Google Cloud já deve estar instalado se você tiver usado os padrões para a imagem da VM. No entanto, se você usou uma imagem personalizada sem o SDK do Google, adicione-a seguindo a documentação.

Exporte o IP do balanceador de carga do AlloyDB Omni conforme o exemplo abaixo (substitua o IP pelo IP do seu balanceador de carga):

export INSTANCE_IP=10.131.0.33

Conecte-se ao banco de dados e ative a extensão pgvector.

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Na sessão psql:

CREATE EXTENSION IF NOT EXISTS vector;

Saia da sessão do psql e, na sessão da linha de comando, execute comandos para carregar os dados no banco de dados de demonstração.

Crie as tabelas:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"

Saída esperada do console:

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_demo_schema.sql |psql "host=$INSTANCE_IP user=postgres dbname=demo"
Password for user postgres:
SET
SET
SET
SET
SET
 set_config
------------

(1 row)

SET
SET
SET
SET
SET
SET
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE TABLE
ALTER TABLE
CREATE SEQUENCE
ALTER TABLE
ALTER SEQUENCE
ALTER TABLE
ALTER TABLE
ALTER TABLE
student@cloudshell:~$ 

Confira a lista de tabelas criadas:

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"

Saída:

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\dt+"
Password for user postgres: 
                                           List of relations
 Schema |       Name       | Type  |  Owner   | Persistence | Access method |    Size    | Description 
--------+------------------+-------+----------+-------------+---------------+------------+-------------
 public | cymbal_embedding | table | postgres | permanent   | heap          | 8192 bytes | 
 public | cymbal_inventory | table | postgres | permanent   | heap          | 8192 bytes | 
 public | cymbal_products  | table | postgres | permanent   | heap          | 8192 bytes | 
 public | cymbal_stores    | table | postgres | permanent   | heap          | 8192 bytes | 
(4 rows)
student@cloudshell:~$ 

Carregue dados para a tabela cymbal_products:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header"

Saída esperada do console:

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_products.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_products from stdin csv header"
COPY 941
student@cloudshell:~$ 

Confira um exemplo de algumas linhas da tabela cymbal_products.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"

Saída:

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT uniq_id,left(product_name,30),left(product_description,50),sale_price FROM cymbal_products limit 3"
Password for user postgres: 
             uniq_id              |              left              |                        left                        | sale_price 
----------------------------------+--------------------------------+----------------------------------------------------+------------
 a73d5f754f225ecb9fdc64232a57bc37 | Laundry Tub Strainer Cup       |   Laundry tub strainer cup Chrome For 1-.50, drain |      11.74
 41b8993891aa7d39352f092ace8f3a86 | LED Starry Star Night Light La |  LED Starry Star Night Light Laser Projector 3D Oc |      46.97
 ed4a5c1b02990a1bebec908d416fe801 | Surya Horizon HRZ-1060 Area Ru |  The 100% polypropylene construction of the Surya  |       77.4
(3 rows)
student@cloudshell:~$ 

Carregue dados na tabela cymbal_inventory:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header"

Saída esperada do console:

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_inventory.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_inventory from stdin csv header"
Password for user postgres: 
COPY 263861
student@cloudshell:~$ 

Confira um exemplo de algumas linhas da tabela cymbal_inventory.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"

Saída:

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT * FROM cymbal_inventory LIMIT 3"
Password for user postgres: 
 store_id |             uniq_id              | inventory 
----------+----------------------------------+-----------
     1583 | adc4964a6138d1148b1d98c557546695 |         5
     1490 | adc4964a6138d1148b1d98c557546695 |         4
     1492 | adc4964a6138d1148b1d98c557546695 |         3
(3 rows)
student@cloudshell:~$ 

Carregue dados na tabela cymbal_stores:

gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header"

Saída esperada do console:

student@cloudshell:~$ gcloud storage cat gs://cloud-training/gcc/gcc-tech-004/cymbal_stores.csv |psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "\copy cymbal_stores from stdin csv header"
Password for user postgres: 
COPY 4654
student@cloudshell:~$

Confira um exemplo de algumas linhas da tabela cymbal_stores.

psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"

Saída:

student@cloudshell:~$ psql "host=$INSTANCE_IP user=postgres dbname=demo" -c "SELECT store_id, name, zip_code FROM cymbal_stores limit 3"
Password for user postgres: 
 store_id |       name        | zip_code 
----------+-------------------+----------
     1990 | Mayaguez Store    |      680
     2267 | Ware Supercenter  |     1082
     4359 | Ponce Supercenter |      780
(3 rows)
student@cloudshell:~$ 

Build Embeddings

Conecte-se ao banco de dados de demonstração usando o psql e crie incorporações para os produtos descritos na tabela cymbal_products com base nos nomes e descrições dos produtos.

Conecte-se ao banco de dados de demonstração:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Estamos usando uma tabela cymbal_embedding com embedding de coluna para armazenar nossos embeddings e usamos a descrição do produto como entrada de texto para a função.

Ative o tempo das consultas para comparar depois com modelos remotos:

\timing

Execute a consulta para criar os embeddings:

INSERT INTO cymbal_embedding(uniq_id,embedding)  SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products;

Saída esperada do console:

demo=#  INSERT INTO cymbal_embedding(uniq_id,embedding)  SELECT uniq_id, google_ml.embedding('bge-base-1.5',product_description)::vector FROM cymbal_products;
INSERT 0 941
Time: 11069.762 ms (00:11.070)
demo=#

Neste exemplo, a criação de representações para 941 registros levou cerca de 11 segundos.

Executar consultas de teste

Conecte-se ao banco de dados de demonstração usando o psql e ative o tempo para medir o tempo de execução das consultas, como fizemos para criar embeddings.

Vamos encontrar os 5 principais produtos que correspondem a uma solicitação como "Que tipo de árvores frutíferas cresce bem aqui?" usando a distância do cosseno como o algoritmo para pesquisa vetorial.

Na sessão psql, execute:

SELECT
        cp.product_name,
        left(cp.product_description,80) as description,
        cp.sale_price,
        cs.zip_code,
        (ce.embedding <=> google_ml.embedding('bge-base-1.5','What kind of fruit trees grow well here?')::vector) as distance
FROM
        cymbal_products cp
JOIN cymbal_embedding ce on
        ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
        ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
        cs.store_id=ci.store_id
        AND ci.inventory>0
        AND cs.store_id = 1583
ORDER BY
        distance ASC
LIMIT 5;

Saída esperada do console:

demo=# SELECT
        cp.product_name,
        left(cp.product_description,80) as description,
        cp.sale_price,
        cs.zip_code,
        (ce.embedding <=> google_ml.embedding('bge-base-1.5','What kind of fruit trees grow well here?')::vector) as distance
FROM
        cymbal_products cp
JOIN cymbal_embedding ce on
        ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
        ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
        cs.store_id=ci.store_id
        AND ci.inventory>0
        AND cs.store_id = 1583
ORDER BY
        distance ASC
LIMIT 5;
     product_name      |                                   description                                    | sale_price | zip_code |      distance
-----------------------+----------------------------------------------------------------------------------+------------+----------+---------------------
 California Sycamore   | This is a beautiful sycamore tree that can grow to be over 100 feet tall. It is  |     300.00 |    93230 | 0.22753925487632942
 Toyon                 | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e |      10.00 |    93230 | 0.23497374266229387
 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e |      25.00 |    93230 | 0.24215884459965364
 California Redwood    | This is a beautiful redwood tree that can grow to be over 300 feet tall. It is a |    1000.00 |    93230 | 0.24564130578287147
 Cherry Tree           | This is a beautiful cherry tree that will produce delicious cherries. It is an d |      75.00 |    93230 | 0.24846117929767153
(5 rows)

Time: 28.724 ms
demo=#

A consulta foi executada em 28 ms e retornou uma lista de árvores da tabela cymbal_products que correspondem à solicitação e com inventário disponível na loja com o número 1583.

Criar índice ANN

Quando temos apenas um pequeno conjunto de dados, é fácil usar a pesquisa exata para verificar todos os embeddings, mas, quando os dados crescem, o tempo de carga e resposta também aumenta. Para melhorar a performance, crie índices nos dados de incorporação. Confira um exemplo de como fazer isso usando o índice ScaNN do Google para dados vetoriais.

Se você perder a conexão, reconecte ao banco de dados de demonstração:

psql "host=$INSTANCE_IP user=postgres sslmode=require dbname=demo"

Ative a extensão alloydb_scann:

CREATE EXTENSION IF NOT EXISTS alloydb_scann;

Crie o índice:

CREATE INDEX cymbal_embedding_scann ON cymbal_embedding USING scann (embedding cosine);

Faça a mesma consulta e compare os resultados:

demo=# SELECT
        cp.product_name,
        left(cp.product_description,80) as description,
        cp.sale_price,
        cs.zip_code,
        (ce.embedding <=> google_ml.embedding('bge-base-1.5','What kind of fruit trees grow well here?')::vector) as distance
FROM
        cymbal_products cp
JOIN cymbal_embedding ce on
        ce.uniq_id=cp.uniq_id
JOIN cymbal_inventory ci on
        ci.uniq_id=cp.uniq_id
JOIN cymbal_stores cs on
        cs.store_id=ci.store_id
        AND ci.inventory>0
        AND cs.store_id = 1583
ORDER BY
        distance ASC
LIMIT 5;
     product_name      |                                   description                                    | sale_price | zip_code |      distance
-----------------------+----------------------------------------------------------------------------------+------------+----------+---------------------
 California Sycamore   | This is a beautiful sycamore tree that can grow to be over 100 feet tall. It is  |     300.00 |    93230 | 0.22753925487632942
 Toyon                 | This is a beautiful toyon tree that can grow to be over 20 feet tall. It is an e |      10.00 |    93230 | 0.23497374266229387
 California Peppertree | This is a beautiful peppertree that can grow to be over 30 feet tall. It is an e |      25.00 |    93230 | 0.24215884459965364
 California Redwood    | This is a beautiful redwood tree that can grow to be over 300 feet tall. It is a |    1000.00 |    93230 | 0.24564130578287147
 Fremont Cottonwood    | This is a beautiful cottonwood tree that can grow to be over 100 feet tall. It i |     200.00 |    93230 |  0.2533482837690365
(5 rows)

Time: 14.665 ms
demo=#

O tempo de execução da consulta foi reduzido um pouco, e esse ganho seria mais perceptível com conjuntos de dados maiores. Os resultados são bastante semelhantes, e apenas a cereja foi substituída pela Fremont Cottonwood.

Teste outras consultas e leia mais sobre como escolher o índice de vetor na documentação.

E não se esqueça de que o AlloyDB Omni tem mais recursos e laboratórios.

8. Limpar o ambiente

Agora podemos excluir nosso cluster do GKE com o AlloyDB Omni e um modelo de IA

Excluir cluster do GKE

No Cloud Shell, execute:

export PROJECT_ID=$(gcloud config get project)
export LOCATION=us-central1
export CLUSTER_NAME=alloydb-ai-gke
gcloud container clusters delete ${CLUSTER_NAME} \
  --project=${PROJECT_ID} \
  --region=${LOCATION}

Saída esperada do console:

student@cloudshell:~$ gcloud container clusters delete ${CLUSTER_NAME} \
>   --project=${PROJECT_ID} \
>   --region=${LOCATION}
The following clusters will be deleted.
 - [alloydb-ai-gke] in [us-central1]

Do you want to continue (Y/n)?  Y

Deleting cluster alloydb-ai-gke...done.
Deleted

Excluir VM

No Cloud Shell, execute:

export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
  --project=${PROJECT_ID} \
  --zone=${ZONE}

Saída esperada do console:

student@cloudshell:~$ export PROJECT_ID=$(gcloud config get project)
export ZONE=us-central1-a
gcloud compute instances delete instance-1 \
  --project=${PROJECT_ID} \
  --zone=${ZONE}
Your active configuration is: [cloudshell-5399]
The following instances will be deleted. Any attached disks configured to be auto-deleted will be deleted unless they are attached to any other instances or the `--keep-disks` flag is given and specifies them for keeping. Deleting a disk 
is irreversible and any data on the disk will be lost.
 - [instance-1] in [us-central1-a]

Do you want to continue (Y/n)?  Y

Deleted

Se você criou um novo projeto para este codelab, exclua o projeto completo: https://console.cloud.google.com/cloud-resource-manager

9. Parabéns

Parabéns por concluir o codelab.

O que vimos

  • Como implantar o AlloyDB Omni no cluster do Google Kubernetes
  • Como se conectar ao AlloyDB Omni
  • Como carregar dados para o AlloyDB Omni
  • Como implantar um modelo de incorporação aberto no GKE
  • Como registrar o modelo de incorporação no AlloyDB Omni
  • Como gerar embeddings para pesquisa semântica
  • Como usar embeddings gerados para pesquisa semântica no AlloyDB Omni
  • Como criar e usar índices de vetores no AlloyDB

Leia mais sobre como trabalhar com IA no AlloyDB Omni na documentação.

10. Pesquisa

Saída:

Como você usará este tutorial?

Apenas leitura Leitura e exercícios