Praktyczne techniki obserwowalności w przypadku aplikacji generatywnej AI w języku Java

1. Przegląd

Aplikacje generatywnej AI wymagają możliwości obserwacji tak samo jak inne aplikacje. Czy w przypadku generatywnej AI wymagane są specjalne techniki obserwacji?

W tym module utworzysz prostą aplikację generatywnej AI. wdrożyć go w Cloud Run; Skonfiguruj w niej podstawowe funkcje monitorowania i logowania za pomocą usług i produktów Google Cloud do obserwacji.

Czego się nauczysz

  • Napisz aplikację, która korzysta z Vertex AI, za pomocą edytora Cloud Shell
  • Przechowywanie kodu aplikacji w GitHubie
  • Wdrażanie kodu źródłowego aplikacji w Cloud Run za pomocą gcloud CLI
  • Dodawanie funkcji monitorowania i logowania do aplikacji generatywnej AI
  • Korzystanie ze wskaźników opartych na logach
  • Implementowanie logowania i monitorowania za pomocą pakietu Open Telemetry SDK
  • Uzyskiwanie informacji o odpowiedzialnym postępowaniu z danymi AI

2. Wymagania wstępne

Jeśli nie masz jeszcze konta Google, musisz utworzyć nowe konto.

3. Konfigurowanie projektu

  1. Zaloguj się w konsoli Google Cloud za pomocą konta Google.
  2. Utwórz nowy projekt lub użyj już istniejącego. Zapisz identyfikator utworzonego lub wybranego projektu.
  3. Włącz płatności w projekcie.
  4. Sprawdź, czy płatności są włączone w sekcji Moje projekty w Rozliczeniach usługi Google Cloud
      .
    • Jeśli w kolumnie Billing account przy nowym projekcie widnieje symbol Billing is disabled:
      1. Kliknij 3 kropki w kolumnie Actions.
      2. Kliknij Zmień płatności.
      3. Wybierz konto rozliczeniowe, którego chcesz używać.
    • Jeśli uczestniczysz w wydarzeniu na żywo, konto prawdopodobnie będzie się nazywać Próbne konto rozliczeniowe Google Cloud Platform.

4. Przygotowywanie edytora Cloud Shell

  1. Otwórz edytor Cloud Shell. Jeśli pojawi się komunikat z prośbą o autoryzację Cloud Shell do wywoływania gcloud przy użyciu Twoich danych logowania, kliknij Autoryzuj, aby kontynuować.
    Kliknij, aby uwierzytelnić się w Cloud Shell
  2. Otwórz okno terminala.
    1. Kliknij menu Ikona menu z 3 kreskami.
    2. Kliknij Terminal.
    3. Kliknij Nowy terminal
      Otwieranie nowego terminala w edytorze Cloud Shell.
  3. W terminalu skonfiguruj identyfikator projektu:
    gcloud config set project [PROJECT_ID]
    
    Zastąp [PROJECT_ID] identyfikatorem projektu. Jeśli na przykład identyfikator projektu to lab-example-project, polecenie będzie wyglądać tak:
    gcloud config set project lab-project-id-example
    
    Jeśli pojawi się komunikat z informacją, że narzędzie gcloud prosi o Twoje dane logowania do interfejsu GCPI API, kliknij Autoryzuj, aby kontynuować.
    Kliknij, aby uwierzytelnić się w Cloud Shell
    Po pomyślnym wykonaniu operacji powinien pojawić się ten komunikat:
    Updated property [core/project].
    
    Jeśli widzisz symbol WARNING i pojawia się pytanie Do you want to continue (Y/N)?, prawdopodobnie identyfikator projektu został wpisany nieprawidłowo. Naciśnij N, a potem Enter i spróbuj ponownie uruchomić polecenie gcloud config set project po znalezieniu prawidłowego identyfikatora projektu.
  4. (Opcjonalnie) Jeśli masz problem ze znalezieniem identyfikatora projektu, uruchom to polecenie, aby wyświetlić identyfikatory wszystkich projektów posortowane według czasu utworzenia w kolejności malejącej:
    gcloud projects list \
         --format='value(projectId,createTime)' \
         --sort-by=~createTime
    

5. Włączanie interfejsów API Google

W terminalu włącz interfejsy API Google wymagane w tym laboratorium:

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

Wykonanie tego polecenia może trochę potrwać. W końcu wyświetli się komunikat o powodzeniu podobny do tego:

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

Jeśli pojawi się komunikat o błędzie zaczynający się od ERROR: (gcloud.services.enable) HttpError accessing i zawierający szczegóły błędu, takie jak poniżej, spróbuj ponownie wykonać polecenie po 1–2 minutach.

"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. Tworzenie aplikacji generatywnej AI

W tym kroku napiszesz kod prostej aplikacji opartej na żądaniach, która używa modelu Gemini do wyświetlania 10 ciekawostek o wybranym przez Ciebie zwierzęciu. Aby utworzyć kod aplikacji, wykonaj te czynności:

  1. W terminalu utwórz katalog codelab-o11y:
    mkdir "${HOME}/codelab-o11y"
    
  2. Zmień bieżący katalog na codelab-o11y:
    cd "${HOME}/codelab-o11y"
    
  3. Pobierz kod początkowy aplikacji w Javie za pomocą narzędzia Spring Framework Starter:
    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. Rozpakuj kod bootstrapa do bieżącego folderu:
    unzip java-starter.zip
    
  5. Usuń plik archiwum z folderu:
    rm java-starter.zip
    
  6. Utwórz plik project.toml, aby zdefiniować wersję środowiska wykonawczego Java, która będzie używana podczas wdrażania kodu w Cloud Run:
    cat > "${HOME}/codelab-o11y/project.toml" << EOF
    [[build.env]]
        name = "GOOGLE_RUNTIME_VERSION"
        value = "17"
    EOF
    
  7. Dodaj zależności pakietu SDK Google Cloud do pliku pom.xml:
    1. Dodaj pakiet podstawowy Google Cloud:
      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. Dodaj pakiet 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. Otwórz plik DemoApplication.java w edytorze Cloud Shell:
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/DemoApplication.java"
    
    W oknie edytora nad terminalem powinien się teraz pojawić szkieletowy kod źródłowy pliku DemoApplication.java. Kod źródłowy pliku będzie podobny do tego:
    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. Zastąp kod w edytorze wersją pokazaną poniżej. Aby zastąpić kod, usuń zawartość pliku, a następnie skopiuj poniższy kod do edytora:
    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);
        }
    }
    
    Po kilku sekundach edytor Cloud Shell automatycznie zapisze Twój kod.

Wdrażanie kodu aplikacji Gen AI w Cloud Run

  1. W oknie terminala uruchom polecenie, aby wdrożyć kod źródłowy aplikacji w Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Jeśli zobaczysz poniższy komunikat informujący, że polecenie utworzy nowe repozytorium. Kliknij 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)?
    
    Proces wdrażania może potrwać kilka minut. Po zakończeniu procesu wdrażania zobaczysz dane wyjściowe podobne do tych:
    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. Skopiuj wyświetlony adres URL usługi Cloud Run do osobnej karty lub okna w przeglądarce. Możesz też uruchomić to polecenie w terminalu, aby wydrukować adres URL usługi, a następnie kliknąć wyświetlony adres URL, przytrzymując klawisz Ctrl, aby go otworzyć:
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    Po otwarciu adresu URL może pojawić się błąd 500 lub komunikat:
    Sorry, this is just a placeholder...
    
    Oznacza to, że usługi nie zostały wdrożone. Odczekaj chwilę i odśwież stronę. Na końcu zobaczysz tekst zaczynający się od Ciekawostki o psach i zawierający 10 ciekawostek o psach.

Spróbuj wejść w interakcję z aplikacją, aby poznać ciekawe fakty o różnych zwierzętach. Aby to zrobić, dodaj do adresu URL parametr animal, np. ?animal=[ANIMAL], gdzie [ANIMAL] to nazwa zwierzęcia. Na przykład dodaj ?animal=cat, aby uzyskać 10 ciekawostek o kotach, lub ?animal=sea turtle, aby uzyskać 10 ciekawostek o żółwiach morskich.

7. Sprawdzanie wywołań interfejsu Vertex API

Kontrolowanie wywołań interfejsów API Google pozwala odpowiedzieć na pytania takie jak „Kto, gdzie i kiedy wywołał konkretny interfejs API?”. Audyt jest ważny podczas rozwiązywania problemów z aplikacją, badania zużycia zasobów lub przeprowadzania analizy kryminalistycznej oprogramowania.

Logi kontrolne umożliwiają śledzenie działań administracyjnych i systemowych, a także rejestrowanie wywołań operacji API „odczyt danych” i „zapis danych”. Aby kontrolować żądania Vertex AI dotyczące generowania treści, musisz włączyć logi kontrolne „Odczyt danych” w konsoli Cloud.

  1. Kliknij przycisk poniżej, aby otworzyć stronę Logi kontrolne w konsoli Cloud.

  2. Sprawdź, czy na stronie jest wybrany projekt utworzony na potrzeby tego modułu. Wybrany projekt jest widoczny w lewym górnym rogu strony, po prawej stronie menu z 3 paskami:
    Menu projektu w konsoli Google Cloud
    W razie potrzeby wybierz właściwy projekt z pola kombi.
  3. W tabeli Konfiguracja logów kontrolnych dostępu do danych w kolumnie Usługa znajdź usługę Vertex AI API i wybierz ją, zaznaczając pole wyboru po lewej stronie nazwy usługi.
    Wybierz Vertex AI API
  4. W panelu informacji po prawej stronie wybierz typ audytu „Odczyt danych”.
    Sprawdzanie logów odczytu danych
  5. Kliknij Zapisz.

Aby wygenerować logi kontrolne, otwórz adres URL usługi. Odśwież stronę, zmieniając wartość parametru ?animal=, aby uzyskać różne wyniki.

Przeglądaj logi kontrolne

  1. Kliknij przycisk poniżej, aby otworzyć stronę Eksplorator logów w konsoli Google Cloud:

  2. Wklej ten filtr w panelu Zapytanie.
    LOG_ID("cloudaudit.googleapis.com%2Fdata_access") AND
    protoPayload.serviceName="aiplatform.googleapis.com"
    
    Panel Zapytanie to edytor znajdujący się u góry strony Eksplorator logów:
    Wykonywanie zapytań dotyczących logów kontrolnych
  3. Kliknij Uruchom zapytanie.
  4. Wybierz jeden z wpisów w dzienniku kontrolnym i rozwiń pola, aby sprawdzić informacje zarejestrowane w dzienniku.
     Możesz wyświetlać szczegóły wywołania interfejsu Vertex API, w tym metodę i użyty model. Możesz też sprawdzić tożsamość osoby wywołującej funkcję i uprawnienia, które zezwoliły na wywołanie.

8. Rejestrowanie interakcji z generatywną AI

W dziennikach kontrolnych nie znajdziesz parametrów żądań do interfejsu API ani danych odpowiedzi. Te informacje mogą być jednak ważne przy rozwiązywaniu problemów z aplikacjami i analizowaniu przepływów pracy. W tym kroku wypełnimy tę lukę, dodając rejestrowanie aplikacji.

Implementacja korzysta z Logbacka z Spring Boot, aby drukować logi aplikacji na standardowe wyjście. Ta metoda wykorzystuje możliwość Cloud Run do przechwytywania informacji drukowanych na standardowe wyjście i automatycznego przesyłania ich do Cloud Logging. Aby rejestrować informacje jako dane strukturalne, wydrukowane dzienniki powinny być odpowiednio sformatowane. Aby dodać do aplikacji funkcję logowania strukturalnego, wykonaj te instrukcje.

  1. Wróć do okna (lub karty) „Cloud Shell” w przeglądarce.
  2. Utwórz i otwórz nowy plik LoggingEventGoogleCloudEncoder.java w edytorze Cloud Shell:
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/LoggingEventGoogleCloudEncoder.java"
    
  3. Skopiuj i wklej ten kod, aby wdrożyć koder Logback, który koduje log w postaci ciągu JSON zgodnego z formatem logów strukturalnych 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. Utwórz i otwórz nowy plik logback.xml w edytorze Cloud Shell:
    cloudshell edit "${HOME}/codelab-o11y/src/main/resources/logback.xml"
    
  5. Skopiuj i wklej poniższy kod XML, aby skonfigurować Logback do używania kodera z dołączonym do Logback modułem dołączania, który drukuje logi na standardowe wyjście:
    <?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. Ponownie otwórz plik DemoApplication.java w edytorze Cloud Shell:
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/DemoApplication.java"
    
  7. Zastąp kod w edytorze wersją pokazaną poniżej, aby rejestrować żądania i odpowiedzi dotyczące generatywnej AI. Aby zastąpić kod, usuń zawartość pliku, a następnie skopiuj poniższy kod do edytora:
    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);
        }
    }
    

Po kilku sekundach Edytor Cloud Shell automatycznie zapisze zmiany.

Wdrażanie kodu aplikacji Gen AI w Cloud Run

  1. W oknie terminala uruchom polecenie, aby wdrożyć kod źródłowy aplikacji w Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Jeśli zobaczysz poniższy komunikat informujący, że polecenie utworzy nowe repozytorium. Kliknij 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)?
    
    Proces wdrażania może potrwać kilka minut. Po zakończeniu procesu wdrażania zobaczysz dane wyjściowe podobne do tych:
    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. Skopiuj wyświetlony adres URL usługi Cloud Run do osobnej karty lub okna w przeglądarce. Możesz też uruchomić to polecenie w terminalu, aby wydrukować adres URL usługi, a następnie kliknąć wyświetlony adres URL, przytrzymując klawisz Ctrl, aby go otworzyć:
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    Po otwarciu adresu URL może pojawić się błąd 500 lub komunikat:
    Sorry, this is just a placeholder...
    
    Oznacza to, że usługi nie zostały wdrożone. Odczekaj chwilę i odśwież stronę. Na końcu zobaczysz tekst zaczynający się od Ciekawostki o psach i zawierający 10 ciekawostek o psach.

Aby wygenerować dzienniki aplikacji, otwórz adres URL usługi. Odśwież stronę, zmieniając wartość parametru ?animal=, aby uzyskać różne wyniki.
 Aby wyświetlić logi aplikacji:

  1. Kliknij przycisk poniżej, aby otworzyć stronę Eksplorator logów w konsoli Cloud:

  2. Wklej ten filtr w panelu Zapytanie (nr 2 na interfejsie eksploratora logów):
    LOG_ID("run.googleapis.com%2Fstdout") AND
    severity=DEBUG
    
  3. Kliknij Uruchom zapytanie.

Wynik zapytania zawiera logi z promptem i odpowiedzią Vertex AI, w tym oceny bezpieczeństwa.

9. Zliczanie interakcji z generatywną AI

Cloud Run zapisuje zarządzane dane, których można używać do monitorowania wdrożonych usług. Dane monitorowania zarządzane przez użytkownika zapewniają większą kontrolę nad danymi i częstotliwością aktualizacji danych. Aby wdrożyć taki rodzaj danych, musisz napisać kod, który zbiera dane i zapisuje je w Cloud Monitoring. W następnym (opcjonalnym) kroku znajdziesz informacje o tym, jak zaimplementować to rozwiązanie za pomocą pakietu SDK OpenTelemetry.

Ten krok pokazuje alternatywę dla implementowania w kodzie danych o użytkownikach – dane oparte na logach. Wskaźniki oparte na logach umożliwiają generowanie wskaźników monitorowania na podstawie wpisów logu, które aplikacja zapisuje w Cloud Logging. Aby zdefiniować wskaźnik oparty na logach typu licznik, użyjemy logów aplikacji, które zostały zaimplementowane w poprzednim kroku. Dane będą zliczać liczbę udanych wywołań interfejsu Vertex API.

  1. Spójrz na okno Eksploratora logów, którego użyliśmy w poprzednim kroku. W panelu Zapytanie znajdź menu Działania i kliknij je, aby otworzyć. Menu znajdziesz na zrzucie ekranu poniżej:
    Pasek narzędzi wyników zapytania z menu Działania
  2. W otwartym menu wybierz Utwórz wskaźnik, aby otworzyć panel Utwórz wskaźnik oparty na logach.
  3. Aby skonfigurować nowy wskaźnik licznika w panelu Utwórz wskaźnik oparty na logach, wykonaj te czynności:
    1. Ustaw Typ wskaźnika: wybierz Licznik.
    2. W sekcji Szczegóły ustaw te pola:
      • Nazwa wskaźnika logu: ustaw nazwę na model_interaction_count. Obowiązują pewne ograniczenia dotyczące nazewnictwa. Więcej informacji znajdziesz w sekcji Rozwiązywanie problemów.
      • Opis: wpisz opis danych. Na przykład Number of log entries capturing successful call to model inference.
      • Units (Jednostki): pozostaw puste pole lub wstaw cyfrę 1.
    3. Pozostaw wartości w sekcji Wybór filtra. Zwróć uwagę, że w polu Build filter (Utwórz filtr) znajduje się ten sam filtr, którego użyliśmy do wyświetlenia logów aplikacji.
    4. (Opcjonalnie) Dodaj etykietę, która pomoże Ci zliczyć liczbę połączeń dotyczących każdego zwierzęcia. UWAGA: ta etykieta może znacznie zwiększyć liczebność danych i nie jest zalecana do użycia w środowisku produkcyjnym:
      1. Kliknij Dodaj etykietę.
      2. W sekcji Etykiety ustaw te pola:
        • Nazwa etykiety: ustaw nazwę na animal.
        • Opis: wpisz opis etykiety. Na przykład: Animal parameter.
        • Typ etykiety: kliknij STRING.
        • Nazwa pola: wpisz jsonPayload.animal.
        • Regular expression (Wyrażenie regularne): pozostaw puste.
      3. Kliknij przycisk Gotowe.
    5. Aby utworzyć dane, kliknij Utwórz dane.

Wskaźnik oparty na logach możesz też utworzyć na stronie Wskaźniki oparte na logach za pomocą gcloud logging metrics create polecenia CLI lub google_logging_metric zasobu Terraform.

Aby wygenerować dane o metrykach, otwórz adres URL usługi. Odśwież otwartą stronę kilka razy, aby wykonać wiele wywołań modelu. Podobnie jak wcześniej, spróbuj użyć w parametrze różnych zwierząt.

Wpisz zapytanie PromQL, aby wyszukać dane wskaźnika opartego na logach. Aby wpisać zapytanie PromQL:

  1. Kliknij przycisk poniżej, aby otworzyć stronę Metrics Explorer w konsoli Cloud:

  2. Na pasku narzędzi w panelu narzędzia do tworzenia zapytań kliknij przycisk o nazwie < > MQL lub < > PromQL. Lokalizację przycisku znajdziesz na ilustracji poniżej.
    Lokalizacja przycisku MQL w narzędziu Metrics Explorer
  3. Sprawdź, czy w przełączniku Język wybrana jest opcja PromQL. Przełącznik języka znajduje się na tym samym pasku narzędzi, który umożliwia formatowanie zapytania.
  4. Wpisz zapytanie w edytorze Zapytań:
    sum(rate(logging_googleapis_com:user_model_interaction_count{monitored_resource="cloud_run_revision"}[${__interval}]))
    
     Więcej informacji o używaniu PromQL znajdziesz w artykule PromQL w Cloud Monitoring.
  5. Kliknij Uruchom zapytanie. Wyświetli się wykres liniowy podobny do tego na zrzucie ekranu:
    Wyświetlanie danych z zapytań

    Pamiętaj, że gdy włączony jest przełącznik Automatyczne uruchamianie, nie jest widoczny przycisk Uruchom zapytanie.

10. (Opcjonalnie) Używanie OpenTelemetry do monitorowania i śledzenia

Jak wspomnieliśmy w poprzednim kroku, dane można wdrażać za pomocą pakietu OpenTelemetry (Otel) SDK. Korzystanie z OTel w architekturach wielousługowych jest zalecaną praktyką. W tym kroku pokazujemy, jak dodać instrumentację OTel do aplikacji Spring Boot. Na tym etapie wykonasz te czynności:

  • Dodawanie do aplikacji Spring Boot kodu umożliwiającego automatyczne śledzenie
  • Wdrożenie wskaźnika licznika do monitorowania liczby udanych wywołań modelu
  • Korelacja śledzenia z logami aplikacji

Zalecana architektura usług na poziomie produktu polega na używaniu kolektora OTel do zbierania i przetwarzania wszystkich danych dotyczących obserwacji z wielu usług. Kod w tym kroku nie używa kolektora, aby uprościć przykład. Zamiast tego używa eksportów OTel, które zapisują dane bezpośrednio w Google Cloud.

Konfigurowanie aplikacji Spring Boot z komponentami OTel i automatycznym śledzeniem

  1. Wróć do okna (lub karty) „Cloud Shell” w przeglądarce.
  2. W terminalu zaktualizuj plik application.permissions, dodając dodatkowe parametry konfiguracji:
    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
    
    Te parametry określają eksportowanie danych dostrzegalności do Cloud Trace i Cloud Monitoring oraz wymuszają próbkowanie wszystkich śladów.
  3. Dodaj wymagane zależności OpenTelemetry do pliku 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. Dodaj OpenTelemetry BOM do pliku 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. Ponownie otwórz plik DemoApplication.java w edytorze Cloud Shell:
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/DemoApplication.java"
    
  6. Zastąp bieżący kod wersją, która zwiększa wartość wskaźnika skuteczności. Aby zastąpić kod, usuń zawartość pliku, a następnie skopiuj poniższy kod do edytora:
    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. Ponownie otwórz plik LoggingEventGoogleCloudEncoder.java w edytorze Cloud Shell:
    cloudshell edit "${HOME}/codelab-o11y/src/main/java/com/example/demo/LoggingEventGoogleCloudEncoder.java"
    
  8. Zastąp bieżący kod wersją, która dodaje atrybuty śledzenia do zapisywanych logów. Dodanie atrybutów umożliwia powiązanie logów z odpowiednimi zakresami śledzenia. Aby zastąpić kod, usuń zawartość pliku, a następnie skopiuj poniższy kod do edytora:
    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";
            }
        }
    }
    

Po kilku sekundach Edytor Cloud Shell automatycznie zapisze zmiany.

Wdrażanie kodu aplikacji Gen AI w Cloud Run

  1. W oknie terminala uruchom polecenie, aby wdrożyć kod źródłowy aplikacji w Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Jeśli zobaczysz poniższy komunikat informujący, że polecenie utworzy nowe repozytorium. Kliknij 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)?
    
    Proces wdrażania może potrwać kilka minut. Po zakończeniu procesu wdrażania zobaczysz dane wyjściowe podobne do tych:
    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. Skopiuj wyświetlony adres URL usługi Cloud Run do osobnej karty lub okna w przeglądarce. Możesz też uruchomić to polecenie w terminalu, aby wydrukować adres URL usługi, a następnie kliknąć wyświetlony adres URL, przytrzymując klawisz Ctrl, aby go otworzyć:
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    Po otwarciu adresu URL może pojawić się błąd 500 lub komunikat:
    Sorry, this is just a placeholder...
    
    Oznacza to, że usługi nie zostały wdrożone. Odczekaj chwilę i odśwież stronę. Na końcu zobaczysz tekst zaczynający się od Ciekawostki o psach i zawierający 10 ciekawostek o psach.

Aby wygenerować dane telemetryczne, otwórz adres URL usługi. Odśwież stronę, zmieniając wartość parametru ?animal=, aby uzyskać różne wyniki.

Przeglądanie logów czasu z aplikacji

  1. Kliknij przycisk poniżej, aby otworzyć stronę Eksplorator logów czasu w konsoli Cloud:

  2. Wybierz jeden z najnowszych śladów. Powinno się wyświetlić 5 lub 6 zakresów podobnych do tych na zrzucie ekranu poniżej.
    Widok zakresu aplikacji w Eksploratorze logów czasu
  3. Znajdź zakres, który śledzi wywołanie procedury obsługi zdarzeń (metoda fun_facts). Będzie to ostatni zakres o nazwie /.
  4. W panelu Szczegóły śledzenia kliknij Logi i zdarzenia. Wyświetlą się logi aplikacji powiązane z tym konkretnym spanem. Korelacja jest wykrywana na podstawie identyfikatorów śladu i spanu w śladzie i w logu. Powinien wyświetlić się dziennik aplikacji, która zapisała prompt, oraz odpowiedź interfejsu Vertex API.

Sprawdzanie wskaźnika licznika

  1. Kliknij przycisk poniżej, aby otworzyć stronę Metrics Explorer w konsoli Cloud:

  2. Na pasku narzędzi w panelu narzędzia do tworzenia zapytań kliknij przycisk o nazwie < > MQL lub < > PromQL. Lokalizację przycisku znajdziesz na ilustracji poniżej.
    Lokalizacja przycisku MQL w narzędziu Metrics Explorer
  3. Sprawdź, czy w przełączniku Język wybrana jest opcja PromQL. Przełącznik języka znajduje się na tym samym pasku narzędzi, który umożliwia formatowanie zapytania.
  4. Wpisz zapytanie w edytorze Zapytań:
    sum(rate(workload_googleapis_com:model_call_counter{monitored_resource="generic_task"}[${__interval}]))
    
  5. Kliknij Uruchom zapytanie.Jeśli przełącznik Uruchamiaj automatycznie jest włączony, przycisk Uruchom zapytanie nie jest widoczny.

11. (Opcjonalnie) Zaciemnianie informacji poufnych w logach

W kroku 10 zapisaliśmy informacje o interakcji aplikacji z modelem Gemini. Te informacje obejmowały nazwę zwierzęcia, rzeczywisty prompt i odpowiedź modelu. Przechowywanie tych informacji w logu powinno być bezpieczne, ale w wielu innych przypadkach nie jest to prawdą. Prompt może zawierać dane osobowe lub inne informacje poufne, których użytkownik nie chce przechowywać. Aby rozwiązać ten problem, możesz zaciemnić dane wrażliwe zapisywane w Cloud Logging. Aby zminimalizować zmiany w kodzie, zalecamy to rozwiązanie.

  1. Utwórz temat Pub/Sub do przechowywania przychodzących wpisów dziennika.
  2. Utwórz ujście logów, które przekierowuje pozyskane logi do tematu Pub/Sub.
  3. Utwórz potok Dataflow, który będzie modyfikować logi przekierowane do tematu PubSub, wykonując te czynności:
    1. Odczytywanie wpisu logu z tematu PubSub
    2. Sprawdzanie ładunku wpisu pod kątem informacji poufnych za pomocą interfejsu DLP Inspection API
    3. Usuń informacje poufne z ładunku za pomocą jednej z metod usuwania danych DLP.
    4. Zapisywanie zaciemnionego wpisu logu w Cloud Logging
  4. Wdrażanie potoku

12. (Opcjonalnie) Zwalnianie miejsca

Aby uniknąć ryzyka naliczenia opłat za zasoby i interfejsy API użyte w tym module, po jego ukończeniu zalecamy wyczyszczenie zasobów. Najprostszym sposobem na uniknięcie płatności jest usunięcie projektu utworzonego w ramach tego laboratorium.

  1. Aby usunąć projekt, uruchom w terminalu polecenie usuwania projektu:
    PROJECT_ID=$(gcloud config get-value project)
    gcloud projects delete ${PROJECT_ID} --quiet
    
    Usunięcie projektu w chmurze spowoduje zaprzestanie naliczania opłat za wszystkie zasoby i interfejsy API wykorzystywane w ramach tego projektu. Powinien pojawić się ten komunikat, w którym PROJECT_ID będzie identyfikatorem Twojego projektu:
    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. (Opcjonalnie) Jeśli pojawi się błąd, zapoznaj się z krokiem 5, aby znaleźć identyfikator projektu użyty podczas modułu. Zastąp nim polecenie w pierwszej instrukcji. Jeśli na przykład identyfikator projektu to lab-example-project, polecenie będzie wyglądać tak:
    gcloud projects delete lab-project-id-example --quiet
    

13. Gratulacje

W tym module udało Ci się utworzyć aplikację generatywnej AI, która korzysta z modelu Gemini do generowania prognoz. oraz wyposażyliśmy aplikację w niezbędne funkcje monitorowania i logowania. Aplikacja i zmiany z kodu źródłowego zostały wdrożone w Cloud Run. Następnie używasz usług Google Cloud Observability do śledzenia wydajności aplikacji, aby mieć pewność, że jest ona niezawodna.

Jeśli chcesz wziąć udział w badaniu wrażeń użytkowników, aby pomóc nam ulepszyć usługi, z których korzystasz, zarejestruj się tutaj.

Oto kilka opcji, które pomogą Ci w dalszej nauce: