Proxy explícito de PSC del motor de agente

1. Introducción

Una interfaz de Private Service Connect es un recurso que permite que una red de nube privada virtual (VPC) de productor inicie conexiones a varios destinos en una red de VPC de consumidor. Las redes de productores y consumidores pueden estar en diferentes proyectos y organizaciones.

Si un adjunto de red acepta una conexión desde una interfaz de Private Service Connect, Google Cloud asigna a la interfaz una dirección IP de una subred de consumidor que especifica el adjunto de red. Las redes del consumidor y del productor están conectadas y pueden comunicarse mediante direcciones IP internas.

Una conexión entre un adjunto de red y una interfaz de Private Service Connect es similar a la conexión entre un extremo de Private Service Connect y un adjunto de servicio, pero tiene dos diferencias clave:

  • Un adjunto de red permite que una red de productor inicie conexiones a una red de consumidor (salida de servicio administrado), mientras que un extremo permite que una red de consumidor inicie conexiones a una red de productor (entrada de servicio administrado).
  • Una conexión de la interfaz de Private Service Connect es transitiva. Esto significa que una red del productor puede comunicarse con otras redes que están conectadas a la red del consumidor.

Consideraciones sobre la accesibilidad a la interfaz de PSC de Vertex AI

  • La interfaz de PSC puede enrutar el tráfico a destinos basados en la VPC o en las instalaciones dentro del bloque de direcciones RFC1918.
  • La interfaz de PSC que segmenta bloques de direcciones que no son RFC-1918 requiere un proxy explícito implementado en la VPC del consumidor con una dirección RFC-1918. Dentro de la implementación de Vertex AI, el proxy se debe definir junto con un FQDN del extremo de destino.
  • Cuando configuras tu implementación solo con una interfaz de PSC, esta conserva su acceso predeterminado a Internet. Este tráfico saliente sale directamente de la red de usuarios segura y administrada por Google.

Consideraciones sobre la VPC-SC de la interfaz de PSC de Vertex AI

  • Cuando tu proyecto forma parte de un perímetro de Controles del servicio de VPC, el perímetro bloquea el acceso predeterminado a Internet de los arrendatarios administrados por Google para evitar el robo de datos.
  • Para permitir que la implementación acceda a Internet pública en este caso, debes configurar de forma explícita una ruta de salida segura que enrute el tráfico a través de tu VPC.
  • La forma recomendada de lograr esto es configurar un servidor proxy dentro del perímetro de tu VPC con una dirección RFC1918 y crear una puerta de enlace de Cloud NAT para permitir que la VM proxy acceda a Internet.

Para obtener más información, consulta los siguientes recursos:

Implementa un agente | IA generativa en Vertex AI | Google Cloud

Configura una interfaz de Private Service Connect para los recursos de Vertex AI | Google Cloud

Qué compilarás

En este instructivo, compilarás un Agent Engine integral implementado con la interfaz de Private Service Connect (PSC) para permitir la conectividad a un sitio público (https://api.frankfurter.app/) a través de una VM de proxy implementada en la VPC del consumidor con una dirección RFC1918. La implementación de ejemplo se aplica en un proyecto habilitado para VPC-SC o para administradores que requieren salida a Internet a través de la red de los clientes en lugar de la VPC del arrendatario.

Figura 1

f42f2db921f6d5af.png

Crearás un solo psc-network-attachment en la VPC del consumidor aprovechando el intercambio de tráfico de DNS para resolver la VM proxy de la red del consumidor en el proyecto del inquilino que aloja Agent Engine, lo que generará los siguientes casos de uso:

Implementar Agent Engine y configurar una VM de proxy para que actúe como proxy explícito, lo que le permite acceder a una URL pública https://api.frankfurter.app

Qué aprenderás

  • Cómo crear un adjunto de red
  • Cómo un productor puede usar un adjunto de red para crear una interfaz de PSC
  • Cómo establecer la comunicación del productor al consumidor con el intercambio de tráfico de DNS
  • Cómo implementar y usar una VM proxy para la salida a Internet

Requisitos

Proyecto de Google Cloud

Permisos de IAM

2. Antes de comenzar

Actualiza el proyecto para que admita el instructivo

En este instructivo, se usan variables para facilitar la implementación de la configuración de gcloud en Cloud Shell.

En Cloud Shell, haz lo siguiente:

gcloud config list project
gcloud config set project [YOUR-PROJECT-NAME]
projectid=YOUR-PROJECT-NAME
echo $projectid

Habilitación de la API

En Cloud Shell, haz lo siguiente:

gcloud services enable "compute.googleapis.com"
gcloud services enable "aiplatform.googleapis.com"
gcloud services enable "dns.googleapis.com"
gcloud services enable "notebooks.googleapis.com"
gcloud services enable "storage.googleapis.com"
gcloud services enable "iap.googleapis.com"

Verifica que las APIs se hayan habilitado correctamente

gcloud services list --enabled

3. Configuración del consumidor

Crea la VPC del consumidor

Esta VPC reside en un proyecto del cliente. En esta VPC, se crearán los siguientes recursos

  • Subred del consumidor
  • Subred de adjunto de red
  • Cloud Router (obligatorio para Cloud NAT)
  • Cloud NAT

En Cloud Shell, haz lo siguiente:

gcloud compute networks create consumer-vpc --project=$projectid --subnet-mode=custom

Crea las subredes de consumidor

Dentro de Cloud Shell, crea la subred para la VM del proxy:

gcloud compute networks subnets create rfc1918-subnet1 --project=$projectid --range=10.10.10.0/28 --network=consumer-vpc --region=us-central1

Crea la subred del adjunto de red de Private Service Connect

Dentro de Cloud Shell, crea la subred para la vinculación de red de PSC:

gcloud compute networks subnets create intf-subnet --project=$projectid --range=192.168.10.0/28 --network=consumer-vpc --region=us-central1

Configuración de Cloud Router y NAT

En este instructivo, se usa Cloud NAT para proporcionar acceso a Internet a la VM de proxy, que no tiene una dirección IP pública. Cloud NAT permite que las VMs con solo direcciones IP privadas se conecten a Internet, lo que les permite realizar tareas como instalar paquetes de software.

Dentro de Cloud Shell, crea el Cloud Router.

gcloud compute routers create cloud-router-for-nat --network consumer-vpc --region us-central1

Dentro de Cloud Shell, crea la puerta de enlace de NAT con el registro habilitado. Usaremos el registro para validar el acceso a la IP pública de la API de Frankfurter (https://api.frankfurter.app/).

gcloud compute routers nats create cloud-nat-us-central1 --router=cloud-router-for-nat --auto-allocate-nat-external-ips --nat-all-subnet-ip-ranges --region us-central1 --enable-logging --log-filter=ALL

4. Habilitar IAP

Para permitir que IAP se conecte a tus instancias de VM, crea una regla de firewall que cumpla con lo siguiente:

  • Se aplica a todas las instancias de VM a las que deseas acceder con IAP.
  • Permite el tráfico de entrada desde el rango de IP 35.235.240.0/20. Este rango contiene todas las direcciones IP que IAP usa para el reenvío de TCP.

Dentro de Cloud Shell, crea la regla de firewall de IAP.

gcloud compute firewall-rules create ssh-iap-consumer \
    --network consumer-vpc \
    --allow tcp:22 \
    --source-ranges=35.235.240.0/20

5. Crea instancias de VM de consumidor

En Cloud Shell, crea la instancia de VM del consumidor, proxy-vm, que actuará como proxy explícito para Agent Engine. Usaremos tinyproxy como la aplicación para el tráfico HTTP de proxy.

gcloud compute instances create proxy-vm \
    --project=$projectid \
    --machine-type=e2-micro \
    --image-family debian-11 \
    --no-address \
    --can-ip-forward \
    --image-project debian-cloud \
    --zone us-central1-a \
    --subnet=rfc1918-subnet1 \
    --shielded-secure-boot \
    --metadata startup-script="#! /bin/bash
      sudo apt-get update
      sudo apt-get install tcpdump
      sudo apt-get install tinyproxy -y
      sudo apt-get install apache2 -y
      sudo service apache2 restart
      echo 'proxy server !!' | tee /var/www/html/index.html
      EOF"

6. Adjunto de red de Private Service Connect

Los adjuntos de red son recursos regionales que representan el lado del consumidor de una interfaz de Private Service Connect. Debes asociar una sola subred con un adjunto de red y el productor asigna IPs a la interfaz de Private Service Connect desde esa subred. La subred debe estar en la misma región que el adjunto de red. Un adjunto de red debe estar en la misma región que el servicio del productor.

Crea el adjunto de red

Dentro de Cloud Shell, crea la conexión de red.

gcloud compute network-attachments create psc-network-attachment \
    --region=us-central1 \
    --connection-preference=ACCEPT_AUTOMATIC \
    --subnets=intf-subnet

Enumera los archivos adjuntos de red

Dentro de Cloud Shell, enumera el adjunto de red.

gcloud compute network-attachments list

Describe los adjuntos de red

Dentro de Cloud Shell, describe el adjunto de red.

gcloud compute network-attachments describe psc-network-attachment --region=us-central1

Toma nota del nombre del adjunto de red de PSC, psc-network-attachment, que usará el productor cuando cree la interfaz de Private Service Connect.

Para ver la URL de la conexión de red de PSC en la consola de Cloud, navega a la siguiente ubicación:

Servicios de red → Private Service Connect → Adjunto de red → psc-network-attachment

8eec51cb197da218.png

7. Zona de DNS privada

Crearás una zona de Cloud DNS para demo.com y la propagarás con un registro A que apunte a las direcciones IP de tu VM de proxy. Más adelante, se implementará el intercambio de tráfico de DNS en Agent Engine, lo que permitirá el acceso a los registros de DNS del consumidor.

Dentro de Cloud Shell, realiza las siguientes acciones para crear un nombre de DNS demo.com.

gcloud dns --project=$projectid managed-zones create private-dns-codelab --description="" --dns-name="demo.com." --visibility="private" --networks="https://compute.googleapis.com/compute/v1/projects/$projectid/global/networks/consumer-vpc"

Obtén y almacena las direcciones IP de las instancias que se usan para los registros A de DNS.

Dentro de Cloud Shell, ejecuta una descripción de las instancias de VM.

gcloud compute instances describe proxy-vm --zone=us-central1-a | grep  networkIP:

En Cloud Shell, crea el conjunto de registros para la VM, proxy-vm.demo.com, y asegúrate de actualizar la dirección IP según el resultado de tu entorno.

gcloud dns --project=$projectid record-sets create proxy-vm.demo.com. --zone="private-dns-codelab" --type="A" --ttl="300" --rrdatas="10.10.10.2"

Crea una regla de Cloud Firewall para permitir el acceso desde la interfaz de PSC

En la siguiente sección, crea una regla de firewall que permita que el tráfico que se origina en el adjunto de red del PSC acceda a la VM de proxy en la VPC del consumidor.

En Cloud Shell, crea la regla de firewall de entrada.

gcloud compute firewall-rules create allow-access-to-compute \
    --network=consumer-vpc \
    --action=ALLOW \
    --rules=ALL \
    --direction=INGRESS \
    --priority=1000 \
    --source-ranges="192.168.10.0/28" \
    --destination-ranges="10.10.10.0/28" \
    --enable-logging

8. Crea un notebook de Jupyter

En la siguiente sección, se te guiará para crear un notebook de Jupyter. Este notebook se usará para implementar Agent Engine con un proxy explícito para la salida a Internet.

Crea una cuenta de servicio administrada por el usuario

En la siguiente sección, crearás una cuenta de servicio que se asociará con la instancia de Vertex AI Workbench que se usa en el instructivo.

En el instructivo, la cuenta de servicio tendrá los siguientes roles aplicados:

Dentro de Cloud Shell, crea la cuenta de servicio.

gcloud iam service-accounts create notebook-sa \
    --display-name="notebook-sa"

En Cloud Shell, actualiza la cuenta de servicio con el rol de administrador de Storage.

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" --role="roles/storage.admin"

Dentro de Cloud Shell, actualiza la cuenta de servicio con el rol de usuario de Vertex AI.

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" --role="roles/aiplatform.user"

Dentro de Cloud Shell, actualiza la cuenta de servicio con el rol de administrador de Artifact Registry.

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" --role="roles/artifactregistry.admin"

Dentro de Cloud Shell, permite que la cuenta de servicio del notebook use la cuenta de servicio predeterminada de Compute Engine.

gcloud iam service-accounts add-iam-policy-binding \
    $(gcloud projects describe $(gcloud config get-value project) --format='value(projectNumber)')-compute@developer.gserviceaccount.com \
    --member="serviceAccount:notebook-sa@$projectid.iam.gserviceaccount.com" \
    --role="roles/iam.serviceAccountUser"

9. Actualiza el proxy explícito

En la siguiente sección, deberás acceder al proxy explícito a través de SSH y actualizar el archivo de configuración tinyproxy.conf antes de realizar un restablecimiento.

Desde Cloud Shell

gcloud compute ssh --zone us-central1-a "proxy-vm" --tunnel-through-iap --project $projectid

Abre el archivo de configuración de tinyproxy y actualízalo con el editor que prefieras. A continuación, se muestra un ejemplo con VIM.

sudo vim /etc/tinyproxy/tinyproxy.conf

# Locate the "Listen" configuration line to restrict listening to only its private IP address of the Proxy-VM, rather than all interfaces. 

Listen 10.10.10.2

# Locate the "Allow" configuration line to allow requests ONLY from the PSC Network Attachment Subnet

Allow 192.168.10.0/24

Save the configs by the following steps:
1. Press the `ESC` key to enter Command Mode.
2. Type `:wq` to save (w) and quit (q).
3. Press `Enter`

Restart the tinyproxy service to apply the changes:
sudo systemctl restart tinyproxy

Validate the tinyproxy service is running:
sudo systemctl status tinyproxy

Perform an exit returning to cloud shell
exit

10. Crea una instancia de Vertex AI Workbench

En la siguiente sección, crea una instancia de Vertex AI Workbench que incorpore la cuenta de servicio creada anteriormente, notebook-sa.

Dentro de Cloud Shell, crea la instancia de private-client.

gcloud workbench instances create workbench-tutorial --vm-image-project=cloud-notebooks-managed --vm-image-family=workbench-instances --machine-type=n1-standard-4 --location=us-central1-a --subnet-region=us-central1 --subnet=rfc1918-subnet1 --disable-public-ip --shielded-secure-boot=true --shielded-integrity-monitoring=true --shielded-vtpm=true --service-account-email=notebook-sa@$projectid.iam.gserviceaccount.com

11. Actualización del agente de servicio de Vertex AI

Vertex AI actúa en tu nombre para realizar operaciones como obtener una dirección IP de la subred del adjunto de red de PSC que se usa para crear la interfaz de PSC. Para ello, Vertex AI usa un agente de servicio (que se indica a continuación) que requiere permiso de administrador de red:

service-$projectnumber@gcp-sa-aiplatform.iam.gserviceaccount.com

En Cloud Shell, obtén el número de tu proyecto.

gcloud projects describe $projectid | grep projectNumber

En Cloud Shell, establece el número de tu proyecto.

projectnumber=YOUR-PROJECT-Number

En Cloud Shell, crea una cuenta de servicio para AI Platform. Omite este paso si ya tienes una cuenta de servicio en tu proyecto.

gcloud beta services identity create --service=aiplatform.googleapis.com --project=$projectnumber

Dentro de Cloud Shell, actualiza la cuenta del agente de servicio con el rol compute.networkAdmin.

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:service-$projectnumber@gcp-sa-aiplatform.iam.gserviceaccount.com" --role="roles/compute.networkAdmin"

Dentro de Cloud Shell, actualiza la cuenta del agente de servicio con el rol dns.peer

gcloud projects add-iam-policy-binding $projectid --member="serviceAccount:service-$projectnumber@gcp-sa-aiplatform.iam.gserviceaccount.com" --role="roles/dns.peer"

Actualización de la cuenta de servicio predeterminada

Otorga a tu cuenta de servicio predeterminada acceso a Vertex AI. Ten en cuenta que el cambio de acceso puede tardar un poco en propagarse.

En Cloud Shell, actualiza la cuenta de servicio predeterminada con el rol aiplatform.user

gcloud projects add-iam-policy-binding $projectid \
  --member="serviceAccount:$projectnumber-compute@developer.gserviceaccount.com" \
    --role="roles/aiplatform.user"

12. Tcpdump de VM de proxy

Para validar la conectividad IP desde Agent Engine, podemos usar TCPDUMP. Esto nos permitirá observar la comunicación que se origina en la subred de la vinculación de red del PSC, 192.168.10.0/28, cuando se invoca la solicitud get desde Agent Engine a la URL pública.

Desde Cloud Shell, establece una conexión SSH con la VM de proxy.

gcloud compute ssh --zone us-central1-a "proxy-vm" --tunnel-through-iap --project $projectid

Desde el SO de la VM del proxy, ejecuta tcpdump.

sudo tcpdump -i any net 192.168.10.0/28 -nn

13. Implementa Agent Engine

Nota: Usaremos la consola de GCP y el notebook de JupyterLab para completar las tareas de esta sección.

En la siguiente sección, crearás un notebook que realizará las siguientes tareas:

  • Usa la API de Frankfurter (https://api.frankfurter.app/) para obtener datos del tipo de cambio.
  • Hace referencia a un proxy explícito (proxy_server) que segmenta la VM del proxy en la VPC de los consumidores con el FQDN proxy-vm.demo.com
  • Define dnsPeeringConfigs "domain": "demo.com.".

Ejecuta el trabajo de entrenamiento en la instancia de Vertex AI Workbench.

  • En la consola de Google Cloud, navega a Vertex AI → Workbench.
  • Junto al nombre de la instancia de Vertex AI Workbench (workbench-tutorial), haz clic en Open JupyterLab. Tu instancia de Vertex AI Workbench abre JupyterLab.
  • Selecciona Archivo > Nuevo > Notebook.
  • Selecciona Kernel > Python 3.

Instala las bibliotecas de Python necesarias: Instala las bibliotecas requeridas para Agent Engine, incluidas pyyaml, google-cloud-aiplatform, cloudpickle, google-cloud-api-keys y langchain-google-vertexai.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

!pip install pyyaml
!pip install google-cloud-aiplatform[agent_engines,langchain]==1.96.0
!pip install cloudpickle==3.1.1
!pip install google-cloud-api-keys
!pip install langchain-google-vertexai==2.0.24

Reinicia el kernel de Jupyter Notebook: Asegúrate de que las bibliotecas recién instaladas se carguen correctamente.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

# Restart the notebook kernel after install, so you can run langchain successfully.

import IPython

app = IPython.Application.instance()
app.kernel.do_shutdown(True)

Configura las variables del proyecto y del bucket: Define tu ID del proyecto de Google Cloud, el número del proyecto, el nombre del servicio, el directorio de GCS, el extremo, el nombre del bucket y la ubicación.

Actualiza los siguientes campos antes de ejecutar la celda.

  • PROJECT_ID = "enter-your-projectid"
  • PROJECT_NUMBER = "enter-your-projectnumber"
  • BUCKET= "enter-a-unique-bucket-name"

Nota: Usaremos la variable BUCKET para crear un bucket de Cloud Storage en el siguiente paso.

En tu notebook de JupyterLab, crea una celda nueva, actualiza y ejecuta lo siguiente.

PROJECT_ID = "enter-your-projectid"  #@param {type:"string"}
PROJECT_NUMBER = "enter-your-projectnumber"  #@param {type:"string"}
SERVICE_NAME = "aiplatform"  #@param ["autopush-aiplatform", "staging-aiplatform", "aiplatform"]
# @markdown  Specify where your agent code should be written in GCS:
GCS_DIR = "reasoning-engine-test"  #@param {type:"string"}
ENDPOINT = "https://us-central1-aiplatform.googleapis.com" # @param ["https://us-central1-aiplatform.googleapis.com", "https://us-central1-autopush-aiplatform.sandbox.googleapis.com", "https://us-central1-staging-aiplatform.sandbox.googleapis.com"]
BUCKET= "enter-a-unique-bucket-name" #@param {type:"string"}
LOCATION="us-central1" #@param {type:"string"}

Crea un bucket de GCS: Crea un bucket de Cloud Storage para almacenar el código del agente.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

!gcloud storage buckets create gs://{BUCKET}

Define el nombre del adjunto de red: Especifica el nombre del adjunto de red de Private Service Connect.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

NETWORK_ATTACHMENT_NAME = 'psc-network-attachment' #@param {type:"string"}

Inicializa las bibliotecas cliente de Python: Configura las bibliotecas cliente necesarias para los servicios de Google Cloud.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

import json
import pprint

import cloudpickle
from google import auth as google_auth
from google.auth.transport import requests as google_requests
from google.cloud import storage
import yaml


def get_identity_token():
    """Gets ID token for calling Cloud Run."""
    credentials, _ = google_auth.default()
    auth_request = google_requests.Request()
    credentials.refresh(auth_request)
    return credentials.id_token

if not GCS_DIR or "your_ldap" in GCS_DIR:
    raise ValueError("GCS_DIR must be set or you must set your ldap.")

if not PROJECT_ID:
    raise ValueError("PROJECT_ID must be set.")


client = storage.Client(project=PROJECT_ID)
bucket = client.get_bucket(BUCKET)

Configura el agente y las herramientas: Define la clase StreamingAgent y la función get_exchange_rate para recuperar los tipos de cambio de divisas con la API de Frankfurter a través del proxy explícito.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta la siguiente configuración. Ten en cuenta los siguientes aspectos destacados:

  • La función def get_exchange_rate usará la API de Frankfurter (https://api.frankfurter.app/) para obtener los datos del tipo de cambio.
  • proxy_server = "http://proxy-vm.demo.com:8888" El FQDN está asociado con la VM de proxy implementada en la VPC del consumidor. Usaremos la interconexión de DNS para resolver el FQDN en un paso posterior.
from langchain_google_vertexai import ChatVertexAI
from langchain.agents import AgentExecutor
from langchain.agents.format_scratchpad.tools import format_to_tool_messages
from langchain.agents.output_parsers.tools import ToolsAgentOutputParser
from langchain.tools.base import StructuredTool
from langchain_core import prompts
from re import S
from typing import Callable, Sequence
import google.auth
import vertexai


class StreamingAgent:

    def __init__(
            self,
            model: str,
            tools: Sequence[Callable],
            project_id: str,
        ):
        self.model_name = model
        self.tools = tools
        self.project_id = project_id

    def set_up(self):
        """All unpickle-able logic should go here.

        The .set_up() method should not be called for an object that is being
        prepared for deployment.
        """
        creds, _ = google.auth.default(quota_project_id=self.project_id)
        vertexai.init(project=self.project_id, location="us-central1", credentials=creds)

        prompt = {
            "input": lambda x: x["input"],
            "agent_scratchpad": (
                lambda x: format_to_tool_messages(x["intermediate_steps"])
            ),
        } | prompts.ChatPromptTemplate.from_messages([
            ("user", "{input}"),
            prompts.MessagesPlaceholder(variable_name="agent_scratchpad"),
        ])

        llm = ChatVertexAI(model_name=self.model_name)
        if self.tools:
            llm = llm.bind_tools(tools=self.tools)

        self.agent_executor = AgentExecutor(
            agent=prompt | llm | ToolsAgentOutputParser(),
            tools=[StructuredTool.from_function(tool) for tool in self.tools],
        )

    def query(self, input: str):
        """Query the application.

        Args:
            input: The user prompt.

        Returns:
            The output of querying the application with the given input.
        """
        return self.agent_executor.invoke(input={"input": input})

    def stream_query(self, input: str):
        """Query the application and stream the output.

        Args:
            input: The user prompt.

        Yields:
            Chunks of the response as they become available.
        """
        for chunk in self.agent_executor.stream(input={"input": input}):
            yield chunk

def get_exchange_rate(
    currency_from: str = "USD",
    currency_to: str = "EUR",
    currency_date: str = "latest",
):
    """Retrieves the exchange rate between two currencies on a specified date.

    Uses the Frankfurter API (https://api.frankfurter.app/) to obtain
    exchange rate data.

    Args:
        currency_from: The base currency (3-letter currency code).
            Defaults to "USD" (US Dollar).
        currency_to: The target currency (3-letter currency code).
            Defaults to "EUR" (Euro).
        currency_date: The date for which to retrieve the exchange rate.
            Defaults to "latest" for the most recent exchange rate data.
            Can be specified in YYYY-MM-DD format for historical rates.

    Returns:
        dict: A dictionary containing the exchange rate information.
            Example: {"amount": 1.0, "base": "USD", "date": "2023-11-24",
                "rates": {"EUR": 0.95534}}
    """
    import requests

    proxy_server = "http://proxy-vm.demo.com:8888" # This is the VM's FQDN to reach the proxy vm in the consumers network

    proxies = {
       "http": proxy_server,
       "https": proxy_server,
    }
    response = requests.get(
        f"https://api.frankfurter.app/{currency_date}",
        params={"from": currency_from, "to": currency_to},
        proxies=proxies,
    )
    return response.json()

Sube los archivos del agente a Cloud Storage: Sube el agente serializado y sus requisitos al bucket de GCS designado.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente:

# Upload files to Cloud Storage.
if not GCS_DIR:
    raise ValueError("GCS_DIR must be set.")

FILE = "streaming_agent.pkl"
blob = bucket.blob(f"{GCS_DIR}/{FILE}")
with blob.open("wb") as f:
    cloudpickle.dump(
        StreamingAgent(
            model="gemini-2.0-flash-001",  # Required.
            tools=[get_exchange_rate],  # Optional.
            project_id=PROJECT_ID
        ), f)


requirements = """
google-cloud-aiplatform[agent_engines,langchain]==1.96.0
cloudpickle==3.1.1
"""

blob = bucket.blob(f"{GCS_DIR}/requirements-streaming.txt")
blob.upload_from_string(requirements)

!gsutil ls gs://{BUCKET}/{GCS_DIR}

Implementa Agent Engine: Implementa Agent Engine y configúralo con la interfaz de PSC y el intercambio de tráfico de DNS para resolver el FQDN de la VM proxy en la VPC del consumidor.

En tu notebook de JupyterLab, crea y ejecuta la siguiente celda. Ten en cuenta los siguientes aspectos destacados:

  • El intercambio de tráfico de DNS a las VPC de consumidor se configura con dnsPeeringConfigs (dnsPeeringConfigs) para el nombre de dominio demo.com.
import requests


token = !gcloud auth application-default print-access-token

response = requests.post(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
    data=json.dumps({
        "displayName": "PSC-I Explicit Proxy",
        "description": "test psc-i agent + proxy vm",
        "spec": {
            "packageSpec": {
                "pickleObjectGcsUri": f"gs://{BUCKET}/{GCS_DIR}/streaming_agent.pkl",
                "requirementsGcsUri": f"gs://{BUCKET}/{GCS_DIR}/requirements-streaming.txt",
                "pythonVersion": "3.10"
            },
            "deploymentSpec": {
                "pscInterfaceConfig": {
                    "networkAttachment": NETWORK_ATTACHMENT_NAME,
                    "dnsPeeringConfigs": [
                    {
                      "domain": "demo.com.",
                      "targetProject": PROJECT_ID,
                      "targetNetwork": "consumer-vpc", #Consumer VPC
                    },
                  ],
                }
            }
        },
    })
)

pprint.pprint(json.loads(response.content))
reasoning_engine_id = json.loads(response.content)["name"].split("/")[5]
pprint.pprint(reasoning_engine_id)

Supervisa el estado de la implementación: Verifica el estado de la operación de implementación de Agent Engine.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

operation_id = json.loads(response.content)["name"].split("/")[7]
pprint.pprint(operation_id)

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

Nota: Esta operación puede tardar alrededor de 5 minutos en completarse. Vuelve a ejecutar la celda para verificar el progreso. No continúes con la siguiente sección hasta que veas un resultado similar al de la siguiente captura de pantalla.

# You can run this multiple times to check the status of the deployment operation, operation takes approx 5 min.
token = !gcloud auth application-default print-access-token
response = requests.get(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/operations/{operation_id}        ",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    }
)
pprint.pprint(json.loads(response.content))

Ejemplo de una ejecución exitosa:

3f6dcd1074af7651.png

Consulta el agente implementado: Envía una consulta al Agent Engine implementado para probar su funcionalidad.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta lo siguiente.

response = requests.post(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/{LOCATION}/reasoningEngines/{reasoning_engine_id}:query",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
    data=json.dumps({ "input": {"input": "What is the exchange rate from US dollars to Euro?"} })
)
print(response.text)

Transmite los resultados de la consulta: Transmite el resultado de la consulta de Agent Engine.

En tu notebook de JupyterLab, crea una celda nueva y ejecuta el siguiente comando que activará la llamada a la API a la URL pública con el proxy explícito en la VPC de los consumidores.

token = !gcloud auth application-default print-access-token
print(f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/us-central1/reasoningEngines/{reasoning_engine_id}:streamQuery")

response = requests.post(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/us-central1/reasoningEngines/{reasoning_engine_id}:streamQuery",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
    data=json.dumps({ "input": {"input": "What is the exchange rate from US dollars to Euro?"} })
)
for chunk in response.iter_lines():
    print(chunk.decode('utf-8'))
# pprint.pprint(json.loads(response.content))

Ejemplo de una ejecución exitosa:

1bd81d12426a348f.png

14. Validación de Tcpdump

Visualiza el resultado de tcpdump que detalla la comunicación entre la dirección IP de la vinculación de red de PSC que usa Agent Engine y la Prox-VM después de la publicación de la solicitud.

user@proxy-vm:~$ sudo tcpdump -i any net 192.168.10.0/28 -nn
tcpdump: data link type LINUX_SLL2
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
22:17:53.983212 ens4  In  IP 192.168.10.2.22261 > 10.10.10.2.8888: Flags [S], seq 3841740961, win 28800, options [mss 1440,sackOK,TS val 4245243253 ecr 0,nop,wscale 7], length 0
22:17:53.983252 ens4  Out IP 10.10.10.2.8888 > 192.168.10.2.22261: Flags [S.], seq 2232973833, ack 3841740962, win 64768, options [mss 1420,sackOK,TS val 2251247643 ecr 4245243253,nop,wscale 7], length 0
22:17:53.985167 ens4  In  IP 192.168.10.2.22261 > 10.10.10.2.8888: Flags [.], ack 1, win 225, options [nop,nop,TS val 4245243256 ecr 2251247643], length 0
22:17:53.986476 ens4  In  IP 192.168.10.2.22261 > 10.10.10.2.8888: Flags [P.], seq 1:45, ack 1, win 16384, options [nop,nop,TS val 4245243256 ecr 2251247643], length 44
22:17:53.986485 ens4  Out IP 10.10.10.2.8888 > 192.168.10.2.22261: Flags [.], ack 45, win 506, options [nop,nop,TS val 2251247646 ecr 4245243256], length 0
22:17:54.043347 ens4  Out IP 10.10.10.2.8888 > 192.168.10.2.22261: Flags [P.], seq 1:71, ack 45, win 506, options [nop,nop,TS val 2251247703 ecr 4245243256], length 70

15. Validación de la interfaz de PSC

También puedes ver las IPs de Network Attachment que usa Agent Engine. Para ello, navega a la siguiente ubicación:

Servicios de red → Private Service Connect → Adjunto de red → psc-network-attachment

Selecciona el proyecto de usuario (el nombre del proyecto termina en -tp).

8a4b5a6e5dfd63d7.png

El campo destacado indica la dirección IP que usa Agent Engine desde el adjunto de red de PSC.

c618359f6eafc0c6.png

16. Validación de Cloud Logging

Sal de la sesión de TCPDump de la VM de proxy y realiza un PING en api.frankfurter.app de Frankfurter para obtener la dirección IP pública asociada.

ping -c4 api.frankfurter.app 

En el ejemplo, se identifica 104.26.1.198 como la IP pública de api.frankfurter.app

user@proxy-vm:~$ ping -c4 api.frankfurter.app

PING api.frankfurter.app (104.26.1.198) 56(84) bytes of data.

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=1 ttl=61 time=10.9 ms

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=2 ttl=61 time=10.9 ms

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=3 ttl=61 time=10.9 ms

64 bytes from 104.26.1.198 (104.26.1.198): icmp_seq=4 ttl=61 time=10.9 ms

Veamos el registro de NAT para ver si se observa tráfico para 104.26.1.198.

Navega a lo siguiente:

Supervisión → Explorador de registros

Usa el siguiente filtro:

resource.type="nat_gateway"

31024dc29c39084.png

Selecciona el período y, luego, Ejecutar consulta

5976857e92d149d3.png

Expande la entrada de registro que identifica la IP pública (de destino) (104.26.1.198) de api.frankfurter.app y la dirección IP de origen, y el nombre de la VM del proxy que valida el uso del proxy explícito para la salida a Internet.

14e293a7fea68db4.png

17. Limpia

En tu notebook de JupyterLab, crea una celda nueva y ejecuta el siguiente comando que activará la eliminación de la implementación de Agent Engine.

token = !gcloud auth application-default print-access-token

response = requests.delete(
    f"{ENDPOINT}/v1beta1/projects/{PROJECT_ID}/locations/us-central1/reasoningEngines/{reasoning_engine_id}",
    headers={
        "Content-Type": "application/json; charset=utf-8",
        "Authorization": f"Bearer {token[0]}"
    },
)
print(response.text)

Desde Cloud Shell, borra los componentes del instructivo.

gcloud dns record-sets delete proxy-vm.demo.com --zone=private-dns-codelab  --type=A

gcloud dns managed-zones delete private-dns-codelab

gcloud compute instances delete proxy-vm --zone=us-central1-a --quiet

gcloud compute instances delete workbench-tutorial --zone=us-central1-a --quiet

gcloud compute routers delete cloud-router-for-nat --region=us-central1 --quiet

gcloud compute network-attachments delete psc-network-attachment --region=us-central1 --quiet

gcloud compute networks subnets delete intf-subnet rfc1918-subnet1 --region=us-central1 --quiet

gcloud compute networks delete consumer-vpc --quiet

18. Felicitaciones

Felicitaciones. Configuraste y validaste correctamente Agent Engine implementado con la interfaz de Private Service Connect con salida a Internet realizada a través de un proxy explícito.

Creaste la infraestructura del consumidor y agregaste un adjunto de red que permitió al productor crear una VM con varias NIC para conectar la comunicación entre el consumidor y el productor. Aprendiste a crear un proxy explícito y un intercambio de tráfico de DNS que permitieron la conectividad a Internet.

Cosmopup cree que los instructivos son increíbles.

c911c127bffdee57.jpeg

¿Qué sigue?

Lecturas y videos adicionales

Documentos de referencia