Techniques d'observabilité pratiques pour les applications d'IA générative en Java

1. Présentation

Les applications d'IA générative nécessitent une observabilité comme n'importe quelle autre application. Des techniques d'observabilité spécifiques sont-elles requises pour l'IA générative ?

Dans cet atelier, vous allez créer une application d'IA générative simple. Déployez-la sur Cloud Run. Instrumentez-le avec des fonctionnalités de surveillance et de journalisation essentielles à l'aide des services et produits d'observabilité Google Cloud.

Objectifs de l'atelier

  • Écrire une application qui utilise Vertex AI avec l'éditeur Cloud Shell
  • Stocker le code de votre application dans GitHub
  • Utilisez la gcloud CLI pour déployer le code source de votre application sur Cloud Run.
  • Ajouter des fonctionnalités de surveillance et de journalisation à votre application d'IA générative
  • Utiliser des métriques basées sur les journaux
  • Implémenter la journalisation et la surveillance avec le SDK Open Telemetry
  • Obtenir des insights sur le traitement des données pour une IA responsable

2. Prérequis

Si vous ne possédez pas déjà un compte Google, vous devez en créer un.

3. Configuration du projet

  1. Connectez-vous à la console Google Cloud avec votre compte Google.
  2. Créez un projet ou réutilisez-en un existant. Notez l'ID du projet que vous venez de créer ou de sélectionner.
  3. Activez la facturation pour le projet.
    • Cet atelier devrait vous coûter moins de 5 $.
    • Vous pouvez suivre les étapes à la fin de cet atelier pour supprimer les ressources et éviter ainsi des frais supplémentaires.
    • Les nouveaux utilisateurs peuvent bénéficier d'un essai sans frais pour bénéficier d'un crédit de 300$.
  4. Vérifiez que la facturation est activée dans Mes projets de la section Facturation Cloud
      .
    • Si votre nouveau projet indique Billing is disabled dans la colonne Billing account :
      1. Cliquez sur les trois points dans la colonne Actions.
      2. Cliquez sur Modifier la facturation.
      3. Sélectionnez le compte de facturation que vous souhaitez utiliser.
    • Si vous participez à un événement en direct, le compte s'intitulera probablement Compte de facturation d'essai de Google Cloud Platform.

4. Préparer l'éditeur Cloud Shell

  1. Accédez à l'éditeur Cloud Shell. Si le message suivant s'affiche, vous demandant d'autoriser Cloud Shell à appeler gcloud avec vos identifiants, cliquez sur Autoriser pour continuer.
    Cliquez pour autoriser Cloud Shell.
  2. Ouvrez une fenêtre de terminal.
    1. Cliquez sur le menu hamburger Icône du menu hamburger.
    2. Cliquez sur Terminal
    3. Cliquez sur Nouveau terminal
      Ouvrir un nouveau terminal dans l'éditeur Cloud Shell.
  3. Dans le terminal, configurez votre ID de projet :
    gcloud config set project [PROJECT_ID]
    
    Remplacez [PROJECT_ID] par l'ID de votre projet. Par exemple, si l'ID de votre projet est lab-example-project, la commande sera la suivante :
    gcloud config set project lab-project-id-example
    
     Si le message suivant s'affiche, indiquant que gcloud demande vos identifiants pour l'API GCPI, cliquez sur Autoriser pour continuer.
    Cliquez pour autoriser Cloud Shell.
    Si l'exécution réussit, le message suivant s'affiche :
    Updated property [core/project].
    
    Si le message WARNING s'affiche et que vous êtes invité à Do you want to continue (Y/N)?, cela signifie probablement que vous avez saisi l'ID de projet de manière incorrecte. Appuyez sur N, puis sur Enter, et essayez d'exécuter à nouveau la commande gcloud config set project après avoir trouvé l'ID du projet.
  4. (Facultatif) Si vous ne parvenez pas à trouver l'ID du projet, exécutez la commande suivante pour afficher l'ID de tous vos projets, triés par date de création dans l'ordre décroissant :
    gcloud projects list \
         --format='value(projectId,createTime)' \
         --sort-by=~createTime
    

5. Activer les API Google

Dans le terminal, activez les API Google requises pour cet atelier :

gcloud services enable \
     run.googleapis.com \
     cloudbuild.googleapis.com \
     aiplatform.googleapis.com \
     logging.googleapis.com \
     monitoring.googleapis.com \
     cloudtrace.googleapis.com

L'exécution de cette commande prendra un certain temps. Un message semblable à celui qui suit s'affiche pour vous indiquer que l'opération s'est correctement déroulée :

Operation "operations/acf.p2-73d90d00-47ee-447a-b600" finished successfully.

Si vous recevez un message d'erreur commençant par ERROR: (gcloud.services.enable) HttpError accessing et contenant des informations détaillées sur l'erreur, comme ci-dessous, réessayez d'exécuter la commande après un délai d'une à deux minutes.

"error": {
  "code": 429,
  "message": "Quota exceeded for quota metric 'Mutate requests' and limit 'Mutate requests per minute' of service 'serviceusage.googleapis.com' ...",
  "status": "RESOURCE_EXHAUSTED",
  ...
}

6. Créer une application d'IA générative

Dans cette étape, vous allez écrire le code d'une application simple basée sur les requêtes qui utilise le modèle Gemini pour afficher 10 faits amusants sur un animal de votre choix. Procédez comme suit pour créer le code de l'application.

  1. Dans le terminal, créez le répertoire codelab-o11y :
    mkdir "${HOME}/codelab-o11y"
    
  2. Définissez le répertoire actuel sur codelab-o11y :
    cd "${HOME}/codelab-o11y"
    
  3. Téléchargez le code d'amorçage de l'application Java à l'aide du starter Spring Framework :
    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d javaVersion=17 \
        -d type=maven-project \
        -d bootVersion=3.4.1 -o java-starter.zip
    
  4. Désarchivez le code bootstrap dans le dossier actuel :
    unzip java-starter.zip
    
  5. Supprimez le fichier d'archive du dossier :
    rm java-starter.zip
    
  6. Créez un fichier project.toml pour définir la version de l'environnement d'exécution Java à utiliser lors du déploiement du code sur Cloud Run :
    cat > "${HOME}/codelab-o11y/project.toml" << EOF
    [[build.env]]
        name = "GOOGLE_RUNTIME_VERSION"
        value = "17"
    EOF
    
  7. Ajoutez les dépendances du SDK Google Cloud au fichier pom.xml :
    1. Ajoutez le package Google Cloud Core :
      sed -i 's/<dependencies>/<dependencies>\
      \
              <dependency>\
                  <groupId>com.google.cloud<\/groupId>\
                  <artifactId>google-cloud-core<\/artifactId>\
                  <version>2.49.1<\/version>\
              <\/dependency>\
              /g' "${HOME}/codelab-o11y/pom.xml"
      
    2. Ajoutez le package Google Cloud Vertex AI :
      sed -i 's/<dependencies>/<dependencies>\
      \
              <dependency>\
                  <groupId>com.google.cloud<\/groupId>\
                  <artifactId>google-cloud-vertexai<\/artifactId>\
                  <version>1.16.0<\/version>\
              <\/dependency>\
              /g' "${HOME}/codelab-o11y/pom.xml"
      
  8. Ouvrez le fichier DemoApplication.java dans l'éditeur Cloud Shell :
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/DemoApplication.java"
    
    Un code source structuré du fichier DemoApplication.java devrait maintenant s'afficher dans la fenêtre de l'éditeur au-dessus du terminal. Le code source du fichier doit ressembler à ce qui suit :
    package com.example.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
    
  9. Remplacez le code dans l'éditeur par la version ci-dessous. Pour remplacer le code, supprimez le contenu du fichier, puis copiez le code ci-dessous dans l'éditeur :
    package com.example.demo;
    
    import java.io.IOException;
    import java.util.Collections;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.google.cloud.ServiceOptions;
    import com.google.cloud.vertexai.VertexAI;
    import com.google.cloud.vertexai.api.GenerateContentResponse;
    import com.google.cloud.vertexai.generativeai.GenerativeModel;
    import com.google.cloud.vertexai.generativeai.ResponseHandler;
    
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            String port = System.getenv().getOrDefault("PORT", "8080");
            SpringApplication app = new SpringApplication(DemoApplication.class);
            app.setDefaultProperties(Collections.singletonMap("server.port", port));
            app.run(args);
        }
    }
    
    @RestController
    class HelloController {
        private final String projectId = ServiceOptions.getDefaultProjectId();
        private VertexAI vertexAI;
        private GenerativeModel model;
    
        @PostConstruct
        public void init() {
            vertexAI = new VertexAI(projectId, "us-central1");
            model = new GenerativeModel("gemini-1.5-flash", vertexAI);
        }
    
        @PreDestroy
        public void destroy() {
            vertexAI.close();
        }
    
        @GetMapping("/")
        public String getFacts(@RequestParam(defaultValue = "dog") String animal) throws IOException {
            String prompt = "Give me 10 fun facts about " + animal + ". Return this as html without backticks.";
            GenerateContentResponse response = model.generateContent(prompt);
            return ResponseHandler.getText(response);
        }
    }
    
    Au bout de quelques secondes, l'éditeur Cloud Shell enregistre automatiquement votre code.

Déployer le code de l'application d'IA générative sur Cloud Run

  1. Dans la fenêtre de terminal, exécutez la commande pour déployer le code source de l'application sur Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Si une invite semblable à celle ci-dessous s'affiche, vous informant que la commande va créer un dépôt. Cliquez sur Enter.
    Deploying from source requires an Artifact Registry Docker repository to store built containers.
    A repository named [cloud-run-source-deploy] in region [us-central1] will be created.
    
    Do you want to continue (Y/n)?
    
    Le processus de déploiement peut prendre quelques minutes. Une fois le processus de déploiement terminé, un résultat semblable à celui-ci s'affiche :
    Service [codelab-o11y-service] revision [codelab-o11y-service-00001-t2q] has been deployed and is serving 100 percent of traffic.
    Service URL: https://codelab-o11y-service-12345678901.us-central1.run.app
    
  2. Copiez l'URL du service Cloud Run affichée dans un onglet ou une fenêtre distincts de votre navigateur. Vous pouvez également exécuter la commande suivante dans le terminal pour imprimer l'URL du service, puis cliquer sur l'URL affichée en maintenant la touche Ctrl enfoncée pour l'ouvrir :
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    Lorsque vous ouvrez l'URL, il est possible que l'erreur 500 s'affiche ou que le message suivant s'affiche :
    Sorry, this is just a placeholder...
    
    Cela signifie que le déploiement des services n'est pas terminé. Patientez quelques instants, puis actualisez la page. À la fin, vous verrez un texte commençant par Fun Dog Facts (Anecdotes amusantes sur les chiens) et contenant 10 anecdotes amusantes sur les chiens.

Essayez d'interagir avec l'application pour obtenir des anecdotes amusantes sur différents animaux. Pour ce faire, ajoutez le paramètre animal à l'URL, comme dans ?animal=[ANIMAL], où [ANIMAL] est le nom d'un animal. Par exemple, ajoutez ?animal=cat pour obtenir 10 faits amusants sur les chats ou ?animal=sea turtle pour obtenir 10 faits amusants sur les tortues marines.

7. Auditer vos appels d'API Vertex

L'audit des appels d'API Google permet de répondre à des questions telles que "Qui a appelé une API particulière, où et quand ?". L'audit est important lorsque vous résolvez les problèmes de votre application, examinez la consommation de ressources ou effectuez une analyse forensique logicielle.

Les journaux d'audit vous permettent de suivre les activités d'administration et système, ainsi que d'enregistrer les appels aux opérations d'API "lecture de données" et "écriture de données". Pour auditer les requêtes Vertex AI visant à générer du contenu, vous devez activer les journaux d'audit "Lecture de données" dans la console Cloud.

  1. Cliquez sur le bouton ci-dessous pour ouvrir la page "Journaux d'audit" dans la console Cloud.

  2. Assurez-vous que la page affiche le projet que vous avez créé pour cet atelier. Le projet sélectionné s'affiche en haut à gauche de la page, à droite du menu hamburger :
    Menu déroulant des projets de la console Google Cloud
    Si nécessaire, sélectionnez le bon projet dans la zone de liste déroulante.
  3. Dans le tableau Configuration des journaux d'audit des accès aux données, recherchez le service Vertex AI API dans la colonne "Service", puis cochez la case située à gauche du nom du service pour le sélectionner.
    Sélectionnez l&#39;API Vertex AI.
  4. Dans le panneau d'informations à droite, sélectionnez le type d'audit "Lecture de données".
    Consulter les journaux de lecture des données
  5. Cliquez sur Enregistrer.

Pour générer des journaux d'audit, ouvrez l'URL du service. Actualisez la page tout en modifiant la valeur du paramètre ?animal= pour obtenir différents résultats.

Explorer les journaux d'audit

  1. Cliquez sur le bouton ci-dessous pour ouvrir la page "Explorateur de journaux" dans la console Cloud :

  2. Collez le filtre suivant dans le volet "Requête".
    LOG_ID("cloudaudit.googleapis.com%2Fdata_access") AND
    protoPayload.serviceName="aiplatform.googleapis.com"
    
    Le volet "Requête" est un éditeur situé en haut de la page "Explorateur de journaux" :
    Interroger les journaux d&#39;audit
  3. Cliquez sur Exécuter la requête.
  4. Sélectionnez l'une des entrées du journal d'audit et développez les champs pour inspecter les informations enregistrées dans le journal.
     Vous pouvez consulter des informations sur l'appel d'API Vertex, y compris la méthode et le modèle utilisés. Vous pouvez également voir l'identité de l'invocateur et les autorisations qui ont permis l'appel.

8. Journaliser les interactions avec l'IA générative

Vous ne trouverez pas les paramètres de requête ni les données de réponse de l'API dans les journaux d'audit. Toutefois, ces informations peuvent être importantes pour résoudre les problèmes d'analyse des applications et des workflows. Lors de cette étape, nous comblons cette lacune en ajoutant la journalisation des applications.

L'implémentation utilise Logback avec Spring Boot pour imprimer les journaux d'application sur la sortie standard. Cette méthode utilise la fonctionnalité Cloud Run pour capturer les informations imprimées sur la sortie standard et les ingérer automatiquement dans Cloud Logging. Pour capturer des informations en tant que données structurées, les journaux imprimés doivent être mis en forme en conséquence. Suivez les instructions ci-dessous pour ajouter des fonctionnalités de journalisation structurée à l'application.

  1. Revenez à la fenêtre (ou à l'onglet) "Cloud Shell" de votre navigateur.
  2. Créez et ouvrez un fichier LoggingEventGoogleCloudEncoder.java dans l'éditeur Cloud Shell :
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/LoggingEventGoogleCloudEncoder.java"
    
  3. Copiez et collez le code suivant pour implémenter l'encodeur Logback qui encode le journal en tant que JSON sérialisé suivant le format de journal structuré Google Cloud :
    package com.example.demo;
    
    import static ch.qos.logback.core.CoreConstants.UTF_8_CHARSET;
    
    import java.time.Instant;
    import ch.qos.logback.core.encoder.EncoderBase;
    import ch.qos.logback.classic.Level;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import java.util.HashMap;
    
    import com.google.gson.Gson;
    
    public class LoggingEventGoogleCloudEncoder extends EncoderBase<ILoggingEvent>  {
        private static final byte[] EMPTY_BYTES = new byte[0];
        private final Gson gson = new Gson();
    
        @Override
        public byte[] headerBytes() {
            return EMPTY_BYTES;
        }
    
        @Override
        public byte[] encode(ILoggingEvent e) {
            var timestamp = Instant.ofEpochMilli(e.getTimeStamp());
            var fields = new HashMap<String, Object>() {
                {
                    put("timestamp", timestamp.toString());
                    put("severity", severityFor(e.getLevel()));
                    put("message", e.getMessage());
                }
            };
            var params = e.getKeyValuePairs();
            if (params != null && params.size() > 0) {
                params.forEach(kv -> fields.putIfAbsent(kv.key, kv.value));
            }
            var data = gson.toJson(fields) + "\n";
            return data.getBytes(UTF_8_CHARSET);
        }
    
        @Override
        public byte[] footerBytes() {
            return EMPTY_BYTES;
        }
    
        private static String severityFor(Level level) {
            switch (level.toInt()) {
                case Level.TRACE_INT:
                return "DEBUG";
                case Level.DEBUG_INT:
                return "DEBUG";
                case Level.INFO_INT:
                return "INFO";
                case Level.WARN_INT:
                return "WARNING";
                case Level.ERROR_INT:
                return "ERROR";
                default:
                return "DEFAULT";
            }
        }
    }
    
  4. Créez et ouvrez un fichier logback.xml dans l'éditeur Cloud Shell :
    cloudshell edit "${HOME}/codelab-o11y/src/main/resources/logback.xml"
    
  5. Copiez et collez le code XML suivant pour configurer Logback afin qu'il utilise l'encodeur avec l'appender Logback qui affiche les journaux sur la sortie standard :
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration debug="true">
        <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="com.example.demo.LoggingEventGoogleCloudEncoder"/>
        </appender>
    
        <root level="info">
            <appender-ref ref="Console" />
        </root>
    </configuration>
    
  6. Rouvrez le fichier DemoApplication.java dans l'éditeur Cloud Shell :
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/DemoApplication.java"
    
  7. Remplacez le code de l'éditeur par la version ci-dessous pour enregistrer la requête et la réponse de l'IA générative. Pour remplacer le code, supprimez le contenu du fichier, puis copiez le code ci-dessous dans l'éditeur :
    package com.example.demo;
    
    import java.io.IOException;
    import java.util.Collections;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.google.cloud.ServiceOptions;
    import com.google.cloud.vertexai.VertexAI;
    import com.google.cloud.vertexai.api.GenerateContentResponse;
    import com.google.cloud.vertexai.generativeai.GenerativeModel;
    import com.google.cloud.vertexai.generativeai.ResponseHandler;
    
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            String port = System.getenv().getOrDefault("PORT", "8080");
            SpringApplication app = new SpringApplication(DemoApplication.class);
            app.setDefaultProperties(Collections.singletonMap("server.port", port));
            app.run(args);
        }
    }
    
    @RestController
    class HelloController {
        private final String projectId = ServiceOptions.getDefaultProjectId();
        private VertexAI vertexAI;
        private GenerativeModel model;
        private final Logger LOGGER = LoggerFactory.getLogger(HelloController.class);
    
        @PostConstruct
        public void init() {
            vertexAI = new VertexAI(projectId, "us-central1");
            model = new GenerativeModel("gemini-1.5-flash", vertexAI);
        }
    
        @PreDestroy
        public void destroy() {
            vertexAI.close();
        }
    
        @GetMapping("/")
        public String getFacts(@RequestParam(defaultValue = "dog") String animal) throws IOException {
            String prompt = "Give me 10 fun facts about " + animal + ". Return this as html without backticks.";
            GenerateContentResponse response = model.generateContent(prompt);
            LOGGER.atInfo()
                    .addKeyValue("animal", animal)
                    .addKeyValue("prompt", prompt)
                    .addKeyValue("response", response)
                    .log("Content is generated");
            return ResponseHandler.getText(response);
        }
    }
    

Au bout de quelques secondes, l'éditeur Cloud Shell enregistre automatiquement vos modifications.

Déployer le code de l'application d'IA générative sur Cloud Run

  1. Dans la fenêtre de terminal, exécutez la commande pour déployer le code source de l'application sur Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Si une invite semblable à celle ci-dessous s'affiche, vous informant que la commande va créer un dépôt. Cliquez sur Enter.
    Deploying from source requires an Artifact Registry Docker repository to store built containers.
    A repository named [cloud-run-source-deploy] in region [us-central1] will be created.
    
    Do you want to continue (Y/n)?
    
    Le processus de déploiement peut prendre quelques minutes. Une fois le processus de déploiement terminé, un résultat semblable à celui-ci s'affiche :
    Service [codelab-o11y-service] revision [codelab-o11y-service-00001-t2q] has been deployed and is serving 100 percent of traffic.
    Service URL: https://codelab-o11y-service-12345678901.us-central1.run.app
    
  2. Copiez l'URL du service Cloud Run affichée dans un onglet ou une fenêtre distincts de votre navigateur. Vous pouvez également exécuter la commande suivante dans le terminal pour imprimer l'URL du service, puis cliquer sur l'URL affichée en maintenant la touche Ctrl enfoncée pour l'ouvrir :
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    Lorsque vous ouvrez l'URL, il est possible que l'erreur 500 s'affiche ou que le message suivant s'affiche :
    Sorry, this is just a placeholder...
    
    Cela signifie que le déploiement des services n'est pas terminé. Patientez quelques instants, puis actualisez la page. À la fin, vous verrez un texte commençant par Fun Dog Facts (Anecdotes amusantes sur les chiens) et contenant 10 anecdotes amusantes sur les chiens.

Pour générer des journaux d'application, ouvrez l'URL du service. Actualisez la page tout en modifiant la valeur du paramètre ?animal= pour obtenir différents résultats.
 Pour afficher les journaux d'application, procédez comme suit :

  1. Cliquez sur le bouton ci-dessous pour ouvrir la page "Explorateur de journaux" dans la console Cloud :

  2. Collez le filtre suivant dans le volet "Requête" (n° 2 dans l'interface de l'explorateur de journaux) :
    LOG_ID("run.googleapis.com%2Fstdout") AND
    severity=DEBUG
    
  3. Cliquez sur Exécuter la requête.

Le résultat de la requête affiche les journaux avec la réponse du prompt et de Vertex AI, y compris les notes de sécurité.

9. Compter les interactions avec l'IA générative

Cloud Run écrit des métriques gérées qui peuvent être utilisées pour surveiller les services déployés. Les métriques de surveillance gérées par l'utilisateur offrent un meilleur contrôle sur les données et la fréquence de mise à jour des métriques. Pour implémenter une telle métrique, vous devez écrire un code qui collecte les données et les écrit dans Cloud Monitoring. Consultez l'étape suivante (facultative) pour savoir comment l'implémenter à l'aide du SDK OpenTelemetry.

Cette étape montre une alternative à l'implémentation de la métrique utilisateur dans le code : les métriques basées sur les journaux. Les métriques basées sur les journaux vous permettent de générer des métriques de surveillance à partir des entrées de journal que votre application écrit dans Cloud Logging. Nous allons utiliser les journaux d'application que nous avons implémentés à l'étape précédente pour définir une métrique basée sur les journaux de type compteur. Cette métrique comptabilise le nombre d'appels réussis à l'API Vertex.

  1. Examinez la fenêtre de l'explorateur de journaux que nous avons utilisée à l'étape précédente. Dans le volet "Requête", recherchez le menu déroulant Actions et cliquez dessus pour l'ouvrir. Consultez la capture d'écran ci-dessous pour trouver le menu :
    Barre d&#39;outils des résultats de requête avec le menu déroulant &quot;Actions&quot;
  2. Dans le menu qui s'ouvre, sélectionnez Créer une métrique pour ouvrir le panneau Créer une métrique basée sur les journaux.
  3. Pour configurer une métrique de compteur dans le panneau Créer une métrique basée sur les journaux, procédez comme suit :
    1. Définissez le Type de métrique : sélectionnez Compteur.
    2. Définissez les champs suivants dans la section Détails :
      • Nom de la métrique de journal : définissez le nom sur model_interaction_count. Certaines restrictions en termes de dénomination s'appliquent. Pour en savoir plus, consultez la section Dépannage.
      • Description : saisissez une description de la métrique. Par exemple, Number of log entries capturing successful call to model inference..
      • Unités : laissez ce champ vide ou insérez le chiffre 1.
    3. Laissez les valeurs dans la section Sélection du filtre. Notez que le champ Créer un filtre contient le même filtre que celui que nous avons utilisé pour afficher les journaux d'application.
    4. (Facultatif) Ajoutez un libellé qui permet de comptabiliser le nombre d'appels pour chaque animal. REMARQUE : Ce libellé peut augmenter considérablement la cardinalité de la métrique et n'est pas recommandé pour une utilisation en production :
      1. Cliquez sur Ajouter une étiquette.
      2. Définissez les champs suivants dans la section Libellés :
        • Nom du libellé : définissez le nom sur animal.
        • Description : saisissez la description du libellé. Exemple :Animal parameter
        • Type de libellé : sélectionnez STRING.
        • Nom du champ : saisissez jsonPayload.animal.
        • Expression régulière : laissez ce champ vide.
      3. Cliquez sur Terminé.
    5. Cliquez sur Créer une métrique pour créer la métrique.

Vous pouvez également créer une métrique basée sur les journaux à partir de la page Métriques basées sur les journaux, à l'aide de la commande CLI gcloud logging metrics create ou de la ressource Terraform google_logging_metric.

Pour générer des données de métriques, ouvrez l'URL du service. Actualisez la page ouverte plusieurs fois pour effectuer plusieurs appels au modèle. Comme précédemment, essayez d'utiliser différents animaux dans le paramètre.

Saisissez la requête PromQL pour rechercher les données de métriques basées sur les journaux. Pour saisir une requête PromQL :

  1. Cliquez sur le bouton ci-dessous pour ouvrir la page "Explorateur de métriques" dans la console Cloud :

  2. Dans la barre d'outils du volet de création de requêtes, sélectionnez le bouton nommé < > MQL ou < > PromQL. Pour trouver le bouton, consultez l'image ci-dessous.
    Emplacement du bouton &quot;MQL&quot; dans l&#39;explorateur de métriques
  3. Vérifiez que PromQL est sélectionné dans le bouton d'activation Langage. Le bouton de langage se trouve dans la barre d'outils qui vous permet de mettre en forme votre requête.
  4. Saisissez votre requête dans l'éditeur Requêtes :
    sum(rate(logging_googleapis_com:user_model_interaction_count{monitored_resource="cloud_run_revision"}[${__interval}]))
    
    Pour en savoir plus sur l'utilisation de PromQL, consultez PromQL dans Cloud Monitoring.
  5. Cliquez sur Exécuter la requête. Vous verrez un graphique en courbes semblable à celui de cette capture d'écran :
    Afficher les métriques demandées

    Notez que lorsque le bouton Exécution automatique est activé, le bouton Exécuter la requête ne s'affiche pas.

10. (Facultatif) Utiliser OpenTelemetry pour la surveillance et le traçage

Comme indiqué à l'étape précédente, il est possible d'implémenter des métriques à l'aide du SDK OpenTelemetry (Otel). L'utilisation d'OTel sur des architectures multiservices est une pratique recommandée. Cette étape montre comment ajouter l'instrumentation OTel à une application Spring Boot. Au cours de cette étape, vous allez effectuer les opérations suivantes :

  • Instrumenter une application Spring Boot avec des capacités de traçage automatique
  • Implémenter une métrique de compteur pour surveiller le nombre d'appels de modèle réussis
  • Corréler le traçage avec les journaux d'application

L'architecture recommandée pour les services au niveau du produit consiste à utiliser le collecteur OTel pour collecter et ingérer toutes les données d'observabilité provenant de plusieurs services. Par souci de simplicité, le code de cette étape n'utilise pas le collecteur. Il utilise plutôt des exportations OTel qui écrivent des données directement dans Google Cloud.

Configurer une application Spring Boot avec des composants OTel et le traçage automatique

  1. Revenez à la fenêtre (ou à l'onglet) "Cloud Shell" de votre navigateur.
  2. Dans le terminal, mettez à jour le fichier application.permissions avec des paramètres de configuration supplémentaires :
    cat >> "${HOME}/codelab-o11y/src/main/resources/application.properties" << EOF
    otel.logs.exporter=none
    otel.traces.exporter=google_cloud_trace
    otel.metrics.exporter=google_cloud_monitoring
    otel.resource.attributes.service.name=codelab-o11y-service
    otel.traces.sampler=always_on
    EOF
    
    Ces paramètres définissent l'exportation des données d'observabilité vers Cloud Trace et Cloud Monitoring, et appliquent l'échantillonnage de toutes les traces.
  3. Ajoutez les dépendances OpenTelemetry requises au fichier pom.xml :
    sed -i 's/<dependencies>/<dependencies>\
    \
            <dependency>\
                <groupId>io.opentelemetry.instrumentation<\/groupId>\
                <artifactId>opentelemetry-spring-boot-starter<\/artifactId>\
            <\/dependency>\
            <dependency>\
                <groupId>com.google.cloud.opentelemetry<\/groupId>\
                <artifactId>exporter-auto<\/artifactId>\
                <version>0.33.0-alpha<\/version>\
            <\/dependency>\
            <dependency>\
                <groupId>com.google.cloud.opentelemetry<\/groupId>\
                <artifactId>exporter-trace<\/artifactId>\
                <version>0.33.0<\/version>\
            <\/dependency>\
            <dependency>\
                <groupId>com.google.cloud.opentelemetry<\/groupId>\
                <artifactId>exporter-metrics<\/artifactId>\
                <version>0.33.0<\/version>\
            <\/dependency>\
    /g' "${HOME}/codelab-o11y/pom.xml"
    
  4. Ajoutez la BOM OpenTelemetry au fichier pom.xml :
    sed -i 's/<\/properties>/<\/properties>\
        <dependencyManagement>\
            <dependencies>\
                <dependency>\
                    <groupId>io.opentelemetry.instrumentation<\/groupId>\
                    <artifactId>opentelemetry-instrumentation-bom<\/artifactId>\
                    <version>2.12.0<\/version>\
                    <type>pom<\/type>\
                    <scope>import<\/scope>\
                <\/dependency>\
            <\/dependencies>\
        <\/dependencyManagement>\
    /g' "${HOME}/codelab-o11y/pom.xml"
    
  5. Rouvrez le fichier DemoApplication.java dans l'éditeur Cloud Shell :
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/DemoApplication.java"
    
  6. Remplacez le code actuel par la version qui incrémente une métrique de performances. Pour remplacer le code, supprimez le contenu du fichier, puis copiez le code ci-dessous dans l'éditeur :
    package com.example.demo;
    
    import io.opentelemetry.api.common.AttributeKey;
    import io.opentelemetry.api.common.Attributes;
    import io.opentelemetry.api.OpenTelemetry;
    import io.opentelemetry.api.metrics.LongCounter;
    
    import java.io.IOException;
    import java.util.Collections;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.google.cloud.ServiceOptions;
    import com.google.cloud.vertexai.VertexAI;
    import com.google.cloud.vertexai.api.GenerateContentResponse;
    import com.google.cloud.vertexai.generativeai.GenerativeModel;
    import com.google.cloud.vertexai.generativeai.ResponseHandler;
    
    
    @SpringBootApplication
    public class DemoApplication {
    
        public static void main(String[] args) {
            String port = System.getenv().getOrDefault("PORT", "8080");
            SpringApplication app = new SpringApplication(DemoApplication.class);
            app.setDefaultProperties(Collections.singletonMap("server.port", port));
            app.run(args);
        }
    }
    
    @RestController
    class HelloController {
        private final String projectId = ServiceOptions.getDefaultProjectId();
        private VertexAI vertexAI;
        private GenerativeModel model;
        private final Logger LOGGER = LoggerFactory.getLogger(HelloController.class);
        private static final String INSTRUMENTATION_NAME = "genai-o11y/java/workshop/example";
        private static final AttributeKey<String> ANIMAL = AttributeKey.stringKey("animal");
        private final LongCounter counter;
    
        public HelloController(OpenTelemetry openTelemetry) {
            this.counter = openTelemetry.getMeter(INSTRUMENTATION_NAME)
                    .counterBuilder("model_call_counter")
                    .setDescription("Number of successful model calls")
                    .build();
        }
    
        @PostConstruct
        public void init() {
            vertexAI = new VertexAI(projectId, "us-central1");
            model = new GenerativeModel("gemini-1.5-flash", vertexAI);
        }
    
        @PreDestroy
        public void destroy() {
            vertexAI.close();
        }
    
        @GetMapping("/")
        public String getFacts(@RequestParam(defaultValue = "dog") String animal) throws IOException {
            String prompt = "Give me 10 fun facts about " + animal + ". Return this as html without backticks.";
            GenerateContentResponse response = model.generateContent(prompt);
            LOGGER.atInfo()
                    .addKeyValue("animal", animal)
                    .addKeyValue("prompt", prompt)
                    .addKeyValue("response", response)
                    .log("Content is generated");
            counter.add(1, Attributes.of(ANIMAL, animal));
            return ResponseHandler.getText(response);
        }
    }
    
  7. Rouvrez le fichier LoggingEventGoogleCloudEncoder.java dans l'éditeur Cloud Shell :
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/LoggingEventGoogleCloudEncoder.java"
    
  8. Remplacez le code actuel par la version qui ajoute des attributs de trace aux journaux écrits. L'ajout des attributs permet de corréler les journaux avec les bonnes portées de trace. Pour remplacer le code, supprimez le contenu du fichier, puis copiez le code ci-dessous dans l'éditeur :
    package com.example.demo;
    
    import static ch.qos.logback.core.CoreConstants.UTF_8_CHARSET;
    
    import java.time.Instant;
    import java.util.HashMap;
    
    import ch.qos.logback.core.encoder.EncoderBase;
    import ch.qos.logback.classic.Level;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import com.google.cloud.ServiceOptions;
    import io.opentelemetry.api.trace.Span;
    import io.opentelemetry.api.trace.SpanContext;
    import io.opentelemetry.context.Context;
    
    import com.google.gson.Gson;
    
    
    public class LoggingEventGoogleCloudEncoder extends EncoderBase<ILoggingEvent>  {
        private static final byte[] EMPTY_BYTES = new byte[0];
        private final Gson gson;
        private final String projectId;
        private final String tracePrefix;
    
    
        public LoggingEventGoogleCloudEncoder() {
            this.gson = new Gson();
            this.projectId = lookUpProjectId();
            this.tracePrefix = "projects/" + (projectId == null ? "" : projectId) + "/traces/";
        }
    
        private static String lookUpProjectId() {
            return ServiceOptions.getDefaultProjectId();
        }
    
        @Override
        public byte[] headerBytes() {
            return EMPTY_BYTES;
        }
    
        @Override
        public byte[] encode(ILoggingEvent e) {
            var timestamp = Instant.ofEpochMilli(e.getTimeStamp());
            var fields = new HashMap<String, Object>() {
                {
                    put("timestamp", timestamp.toString());
                    put("severity", severityFor(e.getLevel()));
                    put("message", e.getMessage());
                    SpanContext context = Span.fromContext(Context.current()).getSpanContext();
                    if (context.isValid()) {
                        put("logging.googleapis.com/trace", tracePrefix + context.getTraceId());
                        put("logging.googleapis.com/spanId", context.getSpanId());
                        put("logging.googleapis.com/trace_sampled", Boolean.toString(context.isSampled()));
                    }
                }
            };
            var params = e.getKeyValuePairs();
            if (params != null && params.size() > 0) {
                params.forEach(kv -> fields.putIfAbsent(kv.key, kv.value));
            }
            var data = gson.toJson(fields) + "\n";
            return data.getBytes(UTF_8_CHARSET);
        }
    
        @Override
        public byte[] footerBytes() {
            return EMPTY_BYTES;
        }
    
        private static String severityFor(Level level) {
            switch (level.toInt()) {
                case Level.TRACE_INT:
                return "DEBUG";
                case Level.DEBUG_INT:
                return "DEBUG";
                case Level.INFO_INT:
                return "INFO";
                case Level.WARN_INT:
                return "WARNING";
                case Level.ERROR_INT:
                return "ERROR";
                default:
                return "DEFAULT";
            }
        }
    }
    

Au bout de quelques secondes, l'éditeur Cloud Shell enregistre automatiquement vos modifications.

Déployer le code de l'application d'IA générative sur Cloud Run

  1. Dans la fenêtre de terminal, exécutez la commande pour déployer le code source de l'application sur Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Si une invite semblable à celle ci-dessous s'affiche, vous informant que la commande va créer un dépôt. Cliquez sur Enter.
    Deploying from source requires an Artifact Registry Docker repository to store built containers.
    A repository named [cloud-run-source-deploy] in region [us-central1] will be created.
    
    Do you want to continue (Y/n)?
    
    Le processus de déploiement peut prendre quelques minutes. Une fois le processus de déploiement terminé, un résultat semblable à celui-ci s'affiche :
    Service [codelab-o11y-service] revision [codelab-o11y-service-00001-t2q] has been deployed and is serving 100 percent of traffic.
    Service URL: https://codelab-o11y-service-12345678901.us-central1.run.app
    
  2. Copiez l'URL du service Cloud Run affichée dans un onglet ou une fenêtre distincts de votre navigateur. Vous pouvez également exécuter la commande suivante dans le terminal pour imprimer l'URL du service, puis cliquer sur l'URL affichée en maintenant la touche Ctrl enfoncée pour l'ouvrir :
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    Lorsque vous ouvrez l'URL, il est possible que l'erreur 500 s'affiche ou que le message suivant s'affiche :
    Sorry, this is just a placeholder...
    
    Cela signifie que le déploiement des services n'est pas terminé. Patientez quelques instants, puis actualisez la page. À la fin, vous verrez un texte commençant par Fun Dog Facts (Anecdotes amusantes sur les chiens) et contenant 10 anecdotes amusantes sur les chiens.

Pour générer des données de télémétrie, ouvrez l'URL du service. Actualisez la page tout en modifiant la valeur du paramètre ?animal= pour obtenir différents résultats.

Explorer les traces d'application

  1. Cliquez sur le bouton ci-dessous pour ouvrir la page "Explorateur Trace" dans la console Cloud :

  2. Sélectionnez l'une des traces les plus récentes. Vous devriez voir cinq ou six étendues semblables à celles de la capture d'écran ci-dessous.
    Vue du segment d&#39;application dans l&#39;explorateur Trace
  3. Recherchez le span qui trace l'appel au gestionnaire d'événements (la méthode fun_facts). Il s'agit de la dernière étendue portant le nom /.
  4. Dans le volet Détails des traces, sélectionnez Journaux et événements. Vous verrez les journaux d'application qui correspondent à cette étendue spécifique. La corrélation est détectée à l'aide des ID de trace et de délai dans la trace et dans le journal. Vous devriez voir le journal d'application qui a écrit le prompt et la réponse de l'API Vertex.

Explorer la métrique de compteur

  1. Cliquez sur le bouton ci-dessous pour ouvrir la page "Explorateur de métriques" dans la console Cloud :

  2. Dans la barre d'outils du volet de création de requêtes, sélectionnez le bouton nommé < > MQL ou < > PromQL. Pour trouver le bouton, consultez l'image ci-dessous.
    Emplacement du bouton &quot;MQL&quot; dans l&#39;explorateur de métriques
  3. Vérifiez que PromQL est sélectionné dans le bouton d'activation Langage. Le bouton de langage se trouve dans la barre d'outils qui vous permet de mettre en forme votre requête.
  4. Saisissez votre requête dans l'éditeur Requêtes :
    sum(rate(workload_googleapis_com:model_call_counter{monitored_resource="generic_task"}[${__interval}]))
    
  5. Cliquez sur Exécuter la requête.Lorsque l'option Exécution automatique est activée, le bouton Exécuter la requête ne s'affiche pas.

11. (Facultatif) Informations sensibles masquées dans les journaux

À l'étape 10, nous avons consigné des informations sur l'interaction de l'application avec le modèle Gemini. Ces informations incluaient le nom de l'animal, la requête réelle et la réponse du modèle. Bien que le stockage de ces informations dans le journal soit généralement sûr, ce n'est pas le cas dans de nombreux autres scénarios. La requête peut inclure des informations personnelles ou sensibles qu'un utilisateur ne souhaite pas voir stockées. Pour résoudre ce problème, vous pouvez obscurcir les données sensibles écrites dans Cloud Logging. Pour minimiser les modifications de code, nous vous recommandons la solution suivante.

  1. Créer un sujet Pub/Sub pour stocker les entrées de journal entrantes
  2. Créez un récepteur de journaux qui redirige les journaux ingérés vers un sujet Pub/Sub.
  3. Créez un pipeline Dataflow qui modifie les journaux redirigés vers un sujet Pub/Sub en procédant comme suit :
    1. Lire une entrée de journal à partir du sujet Pub/Sub
    2. Inspecter la charge utile de l'entrée pour détecter des informations sensibles à l'aide de l'API d'inspection DLP
    3. Masquez les informations sensibles dans la charge utile à l'aide de l'une des méthodes de masquage DLP.
    4. Écrire l'entrée de journal obscurcie dans Cloud Logging
  4. Déployer le pipeline

12. (Facultatif) Effectuer un nettoyage

Pour éviter tout risque de facturation des ressources et des API utilisées dans l'atelier de programmation, il est recommandé de les nettoyer une fois l'atelier terminé. Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour l'atelier de programmation.

  1. Pour supprimer le projet, exécutez la commande de suppression de projet dans le terminal :
    PROJECT_ID=$(gcloud config get-value project)
    gcloud projects delete ${PROJECT_ID} --quiet
    
    La suppression de votre projet Cloud arrête la facturation de toutes les ressources et API utilisées dans ce projet. Le message suivant doit s'afficher, où PROJECT_ID correspond à l'ID de votre projet :
    Deleted [https://cloudresourcemanager.googleapis.com/v1/projects/PROJECT_ID].
    
    You can undo this operation for a limited period by running the command below.
        $ gcloud projects undelete PROJECT_ID
    
    See https://cloud.google.com/resource-manager/docs/creating-managing-projects for information on shutting down projects.
    
  2. (Facultatif) Si vous recevez un message d'erreur, consultez l'étape 5 pour trouver l'ID de projet que vous avez utilisé pendant l'atelier. Remplacez-le dans la commande de la première instruction. Par exemple, si l'ID de votre projet est lab-example-project, la commande sera la suivante :
    gcloud projects delete lab-project-id-example --quiet
    

13. Félicitations

Dans cet atelier, vous avez créé une application d'IA générative qui utilise le modèle Gemini pour faire des prédictions. Nous avons instrumenté l'application avec des fonctionnalités de surveillance et de journalisation de base. Vous avez déployé l'application et les modifications du code source sur Cloud Run. Ensuite, vous utiliserez les produits Google Cloud Observability pour suivre les performances de l'application et vous assurer de sa fiabilité.

Si vous souhaitez participer à une étude sur l'expérience utilisateur (UX) pour améliorer les produits que vous avez utilisés aujourd'hui, inscrivez-vous ici.

Voici quelques options pour continuer à vous former :