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

1. Omówienie

Aplikacje korzystające z generatywnej AI wymagają możliwości obserwacji jak każda inna aplikacja. Czy w przypadku generatywnej AI są wymagane specjalne techniki obserwowalności?

W tym module utworzysz prostą aplikację wykorzystującą generatywną AI. Wdróż ją w Cloud Run. Dodaj do niego niezbędne funkcje monitorowania i logowania, korzystając z usług i produktów dostrzegalności Google Cloud.

Czego się nauczysz

  • Dodawanie funkcji monitorowania i logowania do aplikacji z generatywną AI
  • Korzystanie ze wskaźników opartych na logach
  • Implementacja logowania i monitorowania za pomocą pakietu Open Telemetry SDK
  • Informacje o odpowiedzialnym przetwarzaniu danych przez AI

2. Wymagania wstępne

Jeśli nie masz jeszcze konta Google, utwórz nowe konto.

3. Konfigurowanie projektu

  1. Zaloguj się w konsoli Google Cloud za pomocą konta Google.
  2. Utwórz nowy projekt lub użyj istniejącego. Zapisz identyfikator projektu, który został właśnie utworzony lub wybrany.
  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 nowego projektu widać wartość Billing is disabled:
      1. Kliknij 3 kropki w kolumnie Actions.
      2. Kliknij Zmień rozliczenia.
      3. Wybierz konto rozliczeniowe, którego chcesz użyć.
    • Jeśli uczestniczysz w wydarzeniu na żywo, konto będzie prawdopodobnie nazywać się Próbne konto rozliczeniowe Google Cloud Platform.

4. Przygotowanie edytora Cloud Shell

  1. Otwórz Edytor Cloud Shell. Jeśli pojawi się to pytanie o autoryzację Cloud Shell do wywoływania gcloud za pomocą Twoich danych logowania, kliknij Autoryzuj, aby kontynuować.
    Kliknij, aby autoryzować Cloud Shell
  2. Otwórz okno terminala
    1. Kliknij menu z 3 kreskami 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 np. 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, że gcloud prosi o Twoje dane logowania do interfejsu GCPI API, kliknij Autoryzuj, aby kontynuować.
    Kliknij, aby autoryzować Cloud Shell
    Po pomyślnym wykonaniu tej czynności powinien wyświetlić się komunikat:
    Updated property [core/project].
    
    Jeśli widzisz WARNING i pojawia się pytanie Do you want to continue (Y/N)?, prawdopodobnie nieprawidłowo wpisano identyfikator projektu. Gdy znajdziesz prawidłowy identyfikator projektu, naciśnij N, a potem Enter i spróbuj ponownie uruchomić polecenie gcloud config set project.
  4. (Opcjonalnie) Jeśli masz problem ze znalezieniem identyfikatora projektu, uruchom to polecenie, aby wyświetlić identyfikatory wszystkich swoich 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 module:

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 efekcie wyświetli się komunikat o udanym przeprowadzeniu operacji podobny do tego:

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

Jeśli pojawi się komunikat o błędzie rozpoczynający się od ERROR: (gcloud.services.enable) HttpError accessing i zawierający informacje o błędzie podobne do tych poniżej, powtórz 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

Na tym etapie napiszesz kod prostej aplikacji działającej na podstawie żądań, która korzysta z modelu Gemini, aby wyświetlać 10 ciekawych faktów o wybranym przez Ciebie zwierzęciu. Aby utworzyć kod aplikacji, wykonaj te czynności.

  1. Utwórz w terminalu katalog codelab-o11y:
    mkdir "${HOME}/codelab-o11y"
    
  2. Zmień bieżący katalog na codelab-o11y:
    cd "${HOME}/codelab-o11y"
    
  3. Pobierz kod bootstrap aplikacji w Javie za pomocą narzędzia startowego frameworku Spring:
    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. Wypakuj kod bootstrap 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 określić wersję środowiska wykonawczego Java, która ma być 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 Google Cloud SDK do pliku pom.xml:
    1. Dodaj pakiet 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. 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 pojawić się szkielet kodu źródłowego pliku DemoApplication.java. Kod źródłowy pliku będzie wyglądał mniej więcej tak:
    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ą widoczną poniżej. Aby zastąpić kod, usuń zawartość pliku, a potem skopiuj do edytora kod poniżej:
    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 kod.

Wdrażanie kodu aplikacji generatywnej AI w Cloud Run

  1. W oknie terminala uruchom polecenie, aby wdrożyć kod źródłowy aplikacji do Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Jeśli zobaczysz taki komunikat, oznacza to, ż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 na osobną kartę lub do osobnego okna w przeglądarce. Możesz też uruchomić to polecenie w terminalu, aby wydrukować adres URL usługi, a potem 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. Zaczekaj chwilę i odśwież stronę. Na końcu zobaczysz tekst zaczynający się od słów Ciekawostki o psach i zawierający 10 ciekawostek o tych zwierzętach.

Spróbuj wejść w interakcję z aplikacją, aby dowiedzieć się ciekawych faktów o różnych zwierzętach. Aby to zrobić, dodaj parametr animal do adresu URL, 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

Audyt wywołań interfejsu Google API pozwala uzyskać odpowiedzi na pytania w rodzaju „kto, gdzie i kiedy wywołał dany interfejs API?”. Weryfikacja jest ważna podczas rozwiązywania problemów z aplikacją, badania wykorzystania zasobów lub przeprowadzania analizy kryminalistycznej oprogramowania.

Logi kontrolne umożliwiają śledzenie działań administracyjnych i systemowych, a także rejestrowanie wywołań operacji interfejsu API „odczytywanie danych” i „zapisywanie danych”. Aby sprawdzić żą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ę Dzienniki kontrolne w konsoli Google Cloud

  2. Upewnij się, że na stronie wybrany jest projekt utworzony na potrzeby tego laboratorium. Wybrany projekt jest widoczny w lewym górnym rogu strony obok menu hamburgera:
    Menu projektu w konsoli Google Cloud
    W razie potrzeby wybierz odpowiedni projekt z menu.
  3. W tabeli Konfiguracja logów kontrolnych dostępu do danych w kolumnie Usługa znajdź usługę Vertex AI API i kliknij pole wyboru po lewej stronie jej nazwy.
    Wybierz interfejs Vertex AI API
  4. W panelu informacyjnym po prawej stronie wybierz typ audytu „Odczyt danych”.
    Sprawdzanie dzienników odczytu danych
  5. Kliknij Zapisz.

Aby wygenerować dzienniki kontrolne, otwórz adres URL usługi. Aby uzyskać inne wyniki, odśwież stronę, zmieniając wartość parametru ?animal=.

Poznawanie dzienników kontrolnych

  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:
    Zapytania do dziennikó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 zobaczyć szczegóły wywołania interfejsu Vertex API, w tym użytą metodę i model. Możesz też zobaczyć tożsamość wywołującego i uprawnienia autoryzujące wywołanie.

8. Rejestrowanie interakcji z generatywną AI

W dziennikach kontrolnych nie ma parametrów żądania ani danych odpowiedzi interfejsu API. Te informacje mogą jednak być ważne w przypadku problemów z analizą aplikacji i przepływu pracy. W tym kroku wypełniamy tę lukę, dodając rejestrowanie aplikacji.

Implementacja używa Logback z Spring Boot do drukowania logów aplikacji na standardowe wyjście. Ta metoda wykorzystuje możliwość Cloud Run do przechwytywania informacji drukowanych na standardowym wyjściu i automatycznego przesyłania ich do Cloud Logging. Aby rejestrowane dane były uporządkowane, wydrukowane dzienniki należy odpowiednio sformatować. Aby dodać do aplikacji funkcję uporządkowanych logów, postępuj zgodnie z instrukcjami podanymi poniżej.

  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 zaimplementować koder Logback, który koduje log w postaci ciągu znaków JSON zgodnie ze strukturyzowanym formatem logów 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. Aby skonfigurować Logback do korzystania z kodera z załącznikiem Logback, który drukuje dzienniki na standardowe wyjście, skopiuj i wklej ten kod XML:
    <?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. Aby rejestrować żądania i odpowiedzi Gen AI, zastąp kod w edytorze wersją widoczną poniżej. Aby zastąpić kod, usuń zawartość pliku, a potem skopiuj do edytora kod poniżej:
    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 zapisuje zmiany.

Wdrażanie kodu aplikacji generatywnej AI w Cloud Run

  1. W oknie terminala uruchom polecenie, aby wdrożyć kod źródłowy aplikacji do Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Jeśli zobaczysz taki komunikat, oznacza to, ż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 na osobną kartę lub do osobnego okna w przeglądarce. Możesz też uruchomić to polecenie w terminalu, aby wydrukować adres URL usługi, a potem 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. Zaczekaj chwilę i odśwież stronę. Na końcu zobaczysz tekst zaczynający się od słów Ciekawostki o psach i zawierający 10 ciekawostek o tych zwierzętach.

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

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

  2. Wklej ten filtr w panelu Zapytanie (pozycja 2 w 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. Liczenie interakcji z generatywną AI

Cloud Run zapisuje dane zarządzane, których można używać do monitorowania wdrożonych usług. Dane monitorowania zarządzane przez użytkowników zapewniają większą kontrolę nad danymi i częstotliwością ich aktualizacji. Wdrożenie takiej metryki wymaga napisania kodu, który zbiera dane i zapisują je w Cloud Monitoring. Aby dowiedzieć się, jak zaimplementować tę funkcję za pomocą pakietu OpenTelemetry SDK, przejdź do następnego (opcjonalnego) kroku.

Ten krok pokazuje alternatywę dla implementowania danych o użytkownikach w kodzie – dane oparte na logach. Dane oparte na logach umożliwiają generowanie danych monitorowania na podstawie wpisów logów zapisywanych przez aplikację w Cloud Logging. Do zdefiniowania danych typu licznik na podstawie dzienników aplikacji użyjemy dzienników aplikacji zaimplementowanych w poprzednim kroku. Dane zliczają 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 odszukaj menu Działania i kliknij je, aby je otworzyć. Menu znajdziesz na zrzucie ekranu poniżej:
    Pasek narzędzi wyników zapytania z menu Działania
  2. W otwartym menu kliknij 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 dane oparte na logach:
    1. Ustaw Typ wskaźnika: wybierz Licznik.
    2. W sekcji Szczegóły ustaw te pola:
    3. Pozostaw wartości w sekcji Wybór filtra. Pamiętaj, że pole Filtrowanie wersji zawiera ten sam filtr, który został użyty do wyświetlenia dzienników aplikacji.
    4. (Opcjonalnie) Dodaj etykietę, która pomoże zliczać liczbę połączeń dla każdego zwierzęcia. UWAGA: ta etykieta może znacznie zwiększyć liczebność danych i nie jest zalecana do użytku w produkcji:
      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.
        • Wyrażenie regularne: pozostaw puste.
      3. Kliknij przycisk Gotowe.
    5. Aby utworzyć dane, kliknij Utwórz dane.

Możesz też utworzyć wskaźnik oparty na logach na stronie Wskaźniki oparte na logach, używając gcloud logging metrics create polecenia wiersza poleceń lub google_logging_metric zasobu Terraform.

Aby wygenerować dane, otwórz adres URL usługi. Odśwież otwartą stronę kilka razy, aby wykonać kilka wywołań modelu. Podobnie jak wcześniej, spróbuj użyć w tym 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ę Eksplorator danych w konsoli Google Cloud:

  2. Na pasku narzędzi w panelu kreatora zapytań kliknij przycisk o nazwie < > MQL lub < > PromQL. Lokalizacja przycisku – patrz obraz poniżej.
    Lokalizacja przycisku MQL w narzędziu Metrics Explorer
  3. Sprawdź, czy w opcji Język wybrana jest opcja PromQL. Przełącznik języka znajduje się na tej samej belce narzędzi, która umożliwia formatowanie zapytania.
  4. Wpisz zapytanie w edytorze Zapytania:
    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. Zobaczysz wykres liniowy podobny do tego:
    Wyświetlanie danych zapytań

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

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

Jak wspomnieliśmy w poprzednim kroku, dane można implementować za pomocą pakietu OpenTelemetry (Otel) SDK. Zalecane jest używanie usługi OTel w architekturach wielousługowych. Ten krok pokazuje dodawanie pomiarów OTel do aplikacji Spring Boot. Na tym etapie wykonasz te czynności:

  • Dodawanie do aplikacji Spring Boot możliwości automatycznego śledzenia
  • Wdrożenie licznika do monitorowania liczby udanych wywołań modelu
  • Korelacja śledzenia z logami aplikacji

Zalecana architektura usług na poziomie usługi polega na użyciu kolektora Otel do zbierania i przetwarzania wszystkich danych dotyczących możliwości obserwacji z wielu usług. Ze względu na prostotę kod na tym etapie nie korzysta z kolekcjonera. 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 do niego 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 definiują eksportowanie danych o obserwowalności do Cloud Trace i Cloud Monitoring oraz wymuszają próbkowanie wszystkich śladów.
  3. Dodaj do pliku pom.xml wymagane zależności OpenTelemetry:
    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 plik BOM OpenTelemetry 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 dane o skuteczności. Aby zastąpić kod, usuń zawartość pliku, a potem skopiuj do edytora kod poniżej:
    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 zapisanych dzienników. Dodanie atrybutów umożliwia powiązanie logów z odpowiednimi przedziałami śledzenia. Aby zastąpić kod, usuń zawartość pliku, a potem skopiuj do edytora kod poniżej:
    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 zapisuje zmiany.

Wdrażanie kodu aplikacji generatywnej AI w Cloud Run

  1. W oknie terminala uruchom polecenie, aby wdrożyć kod źródłowy aplikacji do Cloud Run.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    Jeśli zobaczysz taki komunikat, oznacza to, ż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 na osobną kartę lub do osobnego okna w przeglądarce. Możesz też uruchomić to polecenie w terminalu, aby wydrukować adres URL usługi, a potem 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. Zaczekaj chwilę i odśwież stronę. Na końcu zobaczysz tekst zaczynający się od słów Ciekawostki o psach i zawierający 10 ciekawostek o tych zwierzętach.

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

Analiza śladów aplikacji

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

  2. Wybierz jeden z najnowszych śladów. Powinieneś/powinnaś zobaczyć 5 lub 6 elementów, które wyglądają jak na zrzucie ekranu poniżej.
    Przebieg aplikacji w Eksploratorze śledzonych zasobów
  3. Znajdź blok kodu, który śledzi wywołanie do metody obsługi zdarzenia (metoda fun_facts). Będzie to ostatni element o nazwie /.
  4. W panelu Szczegóły śledzenia kliknij Logi i zdarzenia. Zobaczysz logi aplikacji powiązane z tym konkretnym spanem. Korelacja jest wykrywana na podstawie identyfikatorów śledzenia i spanu w logu czasu. Powinieneś zobaczyć log aplikacji, który zawiera prompt i odpowiedź interfejsu Vertex API.

Wskaźnik licznika

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

  2. Na pasku narzędzi w panelu kreatora zapytań kliknij przycisk o nazwie < > MQL lub < > PromQL. Lokalizacja przycisku – patrz obraz poniżej.
    Lokalizacja przycisku MQL w narzędziu Metrics Explorer
  3. Sprawdź, czy w opcji Język wybrana jest opcja PromQL. Przełącznik języka znajduje się na tej samej belce narzędzi, która umożliwia formatowanie zapytania.
  4. Wpisz zapytanie w edytorze Zapytania:
    sum(rate(workload_googleapis_com:model_call_counter{monitored_resource="generic_task"}[${__interval}]))
    
  5. Kliknij Uruchom zapytanie.Gdy włączony jest przełącznik Automatyczne uruchamianie, przycisk Uruchom zapytanie nie jest widoczny.

11. (Opcjonalnie) Zaciemnione informacje poufne z logów

W kroku 10 odnotowaliśmy informacje o interakcjach aplikacji z modelem Gemini. Informacje te obejmowały nazwę zwierzęcia, prompt i odpowiedź modelu. Przechowywanie tych informacji w dzienniku powinno być bezpieczne, ale niekoniecznie w wielu innych przypadkach. Prośba może zawierać dane osobowe lub wrażliwe, których użytkownik nie chce przechowywać. Aby to rozwiązać, możesz zafałszować dane wrażliwe zapisywane w Cloud Logging. Aby zminimalizować modyfikacje kodu, zalecamy zastosowanie tego rozwiązania.

  1. Tworzenie tematu Pub/Sub do przechowywania przychodzących wpisów w logach
  2. Utwórz ujście logów, które przekierowuje pozyskane logi do tematu Pub/Sub.
  3. Utwórz potok Dataflow, który modyfikuje dzienniki przekierowywane do tematu Pub/Sub. Aby to zrobić:
    1. Czytanie wpisu w logu z tematu PubSub
    2. Sprawdzanie danych wejściowych rekordu pod kątem informacji poufnych za pomocą interfejsu DLP inspection API
    3. Usuń informacje poufne z ładunku za pomocą jednej z metod zapobiegania utracie danych.
    4. Zapisywanie zafałszowanego wpisu w pliku logów w Cloud Logging
  4. Wdrażanie potoku

12. (Opcjonalnie) Czyszczenie

Aby uniknąć opłat za zasoby i interfejsy API używane w tym samouczku, zalecamy wyczyszczenie po jego ukończeniu. Najprostszym sposobem na uniknięcie płatności jest usunięcie projektu utworzonego na potrzeby tego samouczka.

  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 Cloud powoduje zaprzestanie naliczania opłat za wszystkie zasoby i interfejsy API używane w tym projekcie. Powinien pojawić się komunikat, w którym PROJECT_ID będzie identyfikatorem 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, w kroku 5 sprawdź, jak znaleźć identyfikator projektu użyty w tym module. Zastąp nim polecenie w pierwszej instrukcji. Jeśli np. 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 do prognozowania korzysta z modela Gemini. W aplikacji zostały też zaimplementowane podstawowe funkcje monitorowania i logowania. Wdrożysz aplikację i zmiany z kodu źródłowego w Cloud Run. Następnie możesz używać 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 (UX), aby ulepszyć produkty, z których korzystasz, zarejestruj się tutaj.

Oto kilka opcji dalszej nauki: