Crea un'app di restauro fotografico utilizzando Genkit Go e Nano Banana Pro

1. Introduzione

In questo codelab, creeremo GlowUp, uno strumento di restauro delle foto. GlowUp utilizza l'AI per ripristinare foto vecchie, danneggiate o in bianco e nero, creando immagini a colori di alta qualità in 4K. Puoi utilizzare questo strumento per dare una nuova vita alle tue foto di famiglia o persino adattarlo per ripristinare illustrazioni, disegni, dipinti o altre forme di immagini danneggiate.

Utilizzerai Genkit Go per implementare la logica dell'applicazione e Gemini 3 Pro Image (noto anche come Nano Banana Pro) come modello per elaborare le foto.

Prerequisiti

  • Conoscenza di base del linguaggio di programmazione Go
  • Conoscenza di base della console Google Cloud

Obiettivi didattici

  • Come sviluppare applicazioni Genkit in Go
  • Concetti di base di Genkit come flussi, plug-in e prompt
  • Come scrivere prompt con i modelli Handlebars
  • Come ottenere i dati delle immagini dalle risposte del modello

Che cosa ti serve

Questo workshop può essere svolto interamente in Google Cloud Shell, che include tutte le dipendenze necessarie (gcloud CLI, editor di codice, Go, Gemini CLI) preinstallate.

In alternativa, se preferisci lavorare sulla tua macchina, avrai bisogno di quanto segue:

  • La toolchain Go (versione 1.24 o successive)
  • Node.js v20 o versioni successive (per la CLI genkit)
  • Un terminale con l'interfaccia a riga di comando gcloud installata
  • Un IDE per modificare il codice, ad esempio VS Code o simili
  • Consigliato:un agente di programmazione come Gemini CLI o Antigravity

Tecnologie chiave

Qui puoi trovare maggiori informazioni sulle tecnologie che utilizzeremo:

  • Gemini Nano Banana Pro (Gemini 3 Pro Image): il modello che alimenta la nostra procedura di restauro
  • Genkit Go: il nostro toolkit per orchestrare le chiamate ai modelli

2. Configurazione dell'ambiente

Scegli una delle seguenti opzioni: Configurazione dell'ambiente autonomo se vuoi eseguire questo codelab sulla tua macchina oppure Avvia Cloud Shell se vuoi eseguire questo codelab interamente nel cloud.

Configurazione dell'ambiente autonomo

  1. Accedi alla console Google Cloud e crea un nuovo progetto o riutilizzane uno esistente. Se non hai ancora un account Gmail o Google Workspace, devi crearne uno.

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • Il nome del progetto è il nome visualizzato per i partecipanti a questo progetto. È una stringa di caratteri non utilizzata dalle API di Google. Puoi sempre aggiornarlo.
  • L'ID progetto è univoco in tutti i progetti Google Cloud ed è immutabile (non può essere modificato dopo l'impostazione). La console Cloud genera automaticamente una stringa univoca, di solito non ti interessa di cosa si tratta. Nella maggior parte dei codelab, dovrai fare riferimento all'ID progetto (in genere identificato come PROJECT_ID). Se l'ID generato non ti piace, puoi generarne un altro casuale. In alternativa, puoi provare a crearne uno e vedere se è disponibile. Non può essere modificato dopo questo passaggio e rimane per tutta la durata del progetto.
  • Per tua informazione, esiste un terzo valore, un numero di progetto, utilizzato da alcune API. Scopri di più su tutti e tre questi valori nella documentazione.
  1. Successivamente, devi abilitare la fatturazione in Cloud Console per utilizzare le risorse/API Cloud. Completare questo codelab non costa molto, se non nulla. Per arrestare le risorse ed evitare addebiti oltre a quelli previsti in questo tutorial, puoi eliminare le risorse che hai creato o il progetto. I nuovi utenti di Google Cloud possono beneficiare del programma prova senza costi di 300$.

Avvia Cloud Shell

Sebbene Google Cloud possa essere gestito da remoto dal tuo laptop, in questo codelab utilizzerai Google Cloud Shell, un ambiente a riga di comando in esecuzione nel cloud.

Nella console Google Cloud, fai clic sull'icona di Cloud Shell nella barra degli strumenti in alto a destra:

Attiva Cloud Shell

Bastano pochi istanti per eseguire il provisioning e connettersi all'ambiente. Al termine, dovresti vedere un risultato simile a questo:

Screenshot del terminale Google Cloud Shell che mostra che l'ambiente è connesso

Questa macchina virtuale è caricata con tutti gli strumenti per sviluppatori di cui avrai bisogno. Offre una home directory permanente da 5 GB e viene eseguita su Google Cloud, migliorando notevolmente le prestazioni e l'autenticazione della rete. Tutto il lavoro in questo codelab può essere svolto all'interno di un browser. Non devi installare nulla.

3. Configurazione del progetto

Crea il progetto

Innanzitutto, dobbiamo creare una nuova directory per il progetto e inizializzare il modulo Go. Nel terminale, esegui questi comandi:

mkdir -p glowup && cd glowup
go mod init glowup

Installa l'interfaccia a riga di comando Genkit

Ora dobbiamo installare la CLI Genkit. In questo modo, puoi accedere agli strumenti per sviluppatori locali, inclusa la UI per sviluppatori. Nella finestra del terminale, digita:

curl -sL cli.genkit.dev | bash

Configura le variabili di ambiente

Assicurati di aver configurato le credenziali Google Cloud corrette. Sostituisci your-project-id con l'ID progetto effettivo. La posizione deve essere global quando si utilizzano i modelli di anteprima di Gemini 3 Pro (Nano Banana Pro è uno di questi).

export GOOGLE_CLOUD_PROJECT=$(gcloud config get project)
export GOOGLE_CLOUD_LOCATION=global

Esegui questo comando in modalità shell per abilitare l'API Vertex AI:

gcloud services enable aiplatform.googleapis.com

Se esegui l'operazione dalla tua macchina locale (non su Cloud Shell), devi autenticarti con il comando gcloud:

gcloud auth application-default login

4. Crea la tua prima applicazione Genkit

Genkit è un framework open source progettato per aiutare gli sviluppatori a creare, eseguire il deployment e monitorare applicazioni basate sull'AI pronte per la produzione. In questa sezione creeremo una semplice applicazione "Hello World" per aiutarti a familiarizzare con il framework prima di approfondire la logica di restauro delle foto.

Terminologia di Genkit

Prima di iniziare a lavorare con Genkit, è importante comprendere alcuni termini chiave:

  • Plug-in:utilizzati per estendere le funzionalità di Genkit. Tra le altre cose, è tramite i plug-in che registri i modelli di AI per potenziare la tua applicazione.
  • Flussi:il componente architettonico principale di Genkit. Un flusso tipico prende un input, lo elabora e restituisce un output. Non è necessario utilizzare un modello, ma la maggior parte delle volte utilizzerai i modelli all'interno dei flussi.
  • Prompt:modelli di interazione archiviati nel formato dotprompt (salvati come file *.prompt). Questi non contengono solo le istruzioni del modello, ma anche configurazioni come nome, parametri, input e output del modello.

Connessione con modelli di AI

Genkit utilizza i plug-in per connettere il tuo codice ai fornitori di modelli. Esistono plug-in per tutti i principali fornitori di modelli, tra cui Google, Anthropic e OpenAI. Puoi anche utilizzare i plug-in per connetterti a modelli locali (ad es. utilizzando Ollama) o per estendere le funzionalità di Genkit (ad es. connettersi ai server MCP).

Per accedere ai modelli Google, devi utilizzare il plug-in googlegenai. Supporta due backend:

  • Google AI: ideale per la prototipazione. Utilizza una chiave API.
  • Vertex AI (Google Cloud): consigliato per la produzione. Utilizza l'ID progetto e la località.

In questo codelab utilizzeremo l'autenticazione Vertex AI facendo riferimento al progetto che hai creato all'inizio del lab.

Creare un flusso di saluto

Prima di approfondire il flusso di restauro delle foto Glow Up, creiamo un flusso di base per acquisire familiarità con i concetti e assicurarci che la configurazione funzioni correttamente.

Un flusso è una funzione Genkit speciale che racchiude la logica AI per fornire:

  • Input e output type-safe:definisci gli schemi utilizzando le struct Go per la convalida statica e in fase di runtime
  • Supporto dello streaming:trasmetti risposte parziali o dati personalizzati
  • Integrazione dell'interfaccia utente per sviluppatori:testa ed esegui il debug dei flussi con tracce visive
  • Deployment semplice:esegui il deployment come endpoint HTTP su qualsiasi piattaforma

Apri l'IDE e crea un file main.go nella directory del progetto. Se utilizzi cloudshell, puoi utilizzare il seguente comando:

cloudshell edit main.go

Quindi aggiungi il seguente codice:

main.go

package main

import (
        "context"
        "log"
        "os"
        "os/signal"
        "syscall"

        "github.com/firebase/genkit/go/genkit"
        "github.com/firebase/genkit/go/ai"
        "github.com/firebase/genkit/go/plugins/googlegenai"
)

func main() {
        ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
        defer cancel()

        // Initialize Genkit with the Vertex AI plugin
        g := genkit.Init(ctx,
                genkit.WithPlugins(&googlegenai.VertexAI{}),
        )

        // Define the greeter flow
        genkit.DefineFlow(g, "greeter", func(ctx context.Context, name string) (string, error) {
                text, err := genkit.GenerateText(ctx, g,
                        ai.WithModelName("vertexai/gemini-2.5-pro"),
                        ai.WithPrompt("Say a warm and creative hello to %s", name),
                )
                if err != nil {
                        return "", err
                }

                return text, nil
        })

        // Register the flow here in the next steps
        log.Println("GlowUp initialized. Ready for flows.")
        <-ctx.Done()
}

Salva il file ed esegui go mod tidy per aggiornare le dipendenze:

go mod tidy

Il codice riportato sopra inizializza Genkit con il plug-in VertexAI, definisce un flusso denominato "greeter" e poi attende per sempre su <-ctx.Done(). Stiamo sospendendo l'esecuzione in modo che questo programma non termini immediatamente, in quanto non gli stiamo dando istruzioni per eseguire effettivamente il flusso.

Ciò significa che se esegui questo programma così com'è ora, non farà molto da solo: dobbiamo richiamare il flusso in qualche modo. In un'applicazione di produzione reale, questo flusso verrebbe inserito in un server web o in un'app CLI, ma durante lo sviluppo possiamo utilizzare la CLI genkit per sviluppare e ottimizzare il flusso.

La CLI genkit è stata sviluppata per aiutarci a testare modelli, prompt, flussi e altri componenti dello stack Genkit. Supporta inoltre completamente le tracce, il che è molto comodo quando vuoi capire come funziona l'applicazione internamente. Per avviare il flusso di saluto utilizzando la CLI genkit, esegui questo comando:

genkit start -- go run main.go

Verranno avviati gli endpoint dell'API Telemetry e dell'interfaccia utente per sviluppatori. Il risultato dovrebbe essere simile a questo:

$ genkit start -- go run main.go
Telemetry API running on http://localhost:4033
Project root: /home/daniela/glowup
Genkit Developer UI: http://localhost:4000

Se apri localhost:4000 nel browser per avviare la UI di sviluppo. Dovresti visualizzare una schermata simile a questa:

93d9bd9e1bd6627d.png

Prenditi tutto il tempo necessario per esplorare la UI per sviluppatori. Forse attiva il flusso "Greeter" una volta per vedere come funziona.

Utilizzo dei modelli di prompt

Anche se puoi codificare i prompt nelle chiamate di modelli come abbiamo fatto nell'esempio precedente, un approccio migliore sarebbe quello di creare modelli di prompt per archiviare tutti i prompt della nostra applicazione in modo centralizzato. In questo modo, non solo è più facile trovarli durante la manutenzione del codice, ma puoi anche sperimentare con i prompt indipendentemente dai flussi.

Per definire i modelli di prompt, Genkit utilizza il formato open source dotprompt, salvato come file *.prompt. Un file .prompt è composto da due parti:

  1. Frontmatter:un blocco YAML che definisce il modello, i parametri del modello e gli schemi di input e output
  2. Corpo: il corpo del prompt stesso, che può essere creato utilizzando la sintassi "handlebars". Ad esempio, se definisci un input denominato variable-name, puoi farvi riferimento nel corpo come {{variable-name}}.

La struttura di directory del progetto sarà simile alla seguente:

glowup/
 ├── main.go        
 └── prompts/
      └── greeter.prompt

Vediamo come funziona in pratica. Per prima cosa, crea la cartella in cui archiviare i prompt:

mkdir -p prompts

Poi crea un file greeter.prompt:

cloudshell edit prompts/greeter.prompt

e inserisci i seguenti contenuti:

greeter.prompt

ec9fc82a98604123.png

Questo prompt mostra alcune delle funzionalità del linguaggio del modello. Innanzitutto, specifichiamo il modello vertexai/gemini-2.5-flash nel frontespizio. Per specificare un modello, devi utilizzare la nomenclatura definita nella documentazione del rispettivo plug-in.

La sezione di configurazione consente di configurare i parametri del modello. Utilizziamo la temperatura 1.9 per consentire al modello di essere più creativo. La temperatura varia da 0 (output più coerenti) a 2 (output più creativi). Questo e altri parametri del modello vengono solitamente pubblicati nel foglio del modello. Ad esempio, ecco la scheda del modello per gemini-2.5-flash.

La sezione di input consente di specificare gli argomenti per il prompt. In questo caso, definiamo il nome come stringa. Dopo la parte introduttiva, c'è il corpo del prompt. Il primo blocco definisce il prompt di sistema, mentre il secondo è il prompt dell'utente. Tutti questi elementi insieme consentono di utilizzare tecniche di prompt molto efficaci. Puoi consultare la documentazione completa per dotprompt qui.

Ora adattiamo il flusso greeter per utilizzare il prompt che abbiamo appena creato:

main.go

package main

import (
        "context"
        "os"
        "os/signal"
        "syscall"

        "github.com/firebase/genkit/go/ai"
        "github.com/firebase/genkit/go/genkit"
        "github.com/firebase/genkit/go/plugins/googlegenai"
)

func main() {
        ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
        defer cancel()

        // Initialize Genkit with the Vertex AI plugin
        g := genkit.Init(ctx,
                genkit.WithPlugins(&googlegenai.VertexAI{}),
                genkit.WithDefaultModel("vertexai/gemini-2.5-flash"),
        )

        // Define the greeter flow
        genkit.DefineFlow(g, "greeter", func(ctx context.Context, name *string) (string, error) {
                prompt := genkit.LookupPrompt(g, "greeter")

                input := map[string]any{
                        "name": name,
                }

                resp, err := prompt.Execute(ctx, ai.WithInput(input))
                if err != nil {
                        return "", err
                }
                return resp.Text(), nil
        })

        <-ctx.Done()
}

Prova a eseguire il flusso dalla riga di comando, con e senza un nome, per vedere la differenza. Esecuzione senza nome:

genkit flow:run greeter

Output di esempio:

$ genkit flow:run greeter
Telemetry API running on http://localhost:4035
Running '/flow/greeter' (stream=false)...
Result:
"Hello there, absolutely delightful human!\n\nThe very moment your message arrived, the day instantly sparkled a little brighter. It's not just nice, it's genuinely **wonderful** to meet you!\n\nMay your entire day be filled with unexpected pockets of joy, effortless triumphs, and all the happiness you truly deserve! We're thrilled to have you here!"

E con un nome:

genkit flow:run greeter '{"name":"Daniela"}'

Output di esempio:

$ genkit flow:run greeter '{"name":"Daniela"}'
Telemetry API running on http://localhost:4035
Running '/flow/greeter' (stream=false)...
Result:
"Well hello there, Daniela! What a truly beautiful name, and what an absolute pleasure it is to meet you!\n\nRight from this moment, I just know your day is going to be brimming with positive energy and wonderful surprises. May it be filled with brilliant ideas, joyful moments, and the delightful realization that you're an amazing person doing incredible things. So glad you're here!"

Puoi vedere che il modello ha funzionato come previsto. Siamo pronti a portare questo progetto al livello successivo.

5. Ripristino delle immagini con Nano Banana Pro

Il prompt di ripristino

Ora che hai familiarità con i componenti di base di Genkit, è il momento di creare il prompt per il restauro delle foto.

Crea un file denominato glowup.prompt nella directory dei prompt e incolla i seguenti contenuti:

glowup.prompt

fd0f1551466c8138.png

Il prompt di ripristino segue lo stesso pattern del prompt "di saluto", con solo un paio di differenze significative:

  • Dimensioni immagine:la proprietà imageSize di imageConfig è un parametro specifico del modello Nano Banana Pro. Consente di specificare le dimensioni dell'output come 1K, 2K o 4K.
  • Input multimediale:utilizziamo il modello {{ media }} per inserire la foto nel prompt dell'utente. Questa tecnica ci consente di inviare prompt multimodali al modello (testo + immagine).

Puoi testare questo prompt nell'interfaccia utente per sviluppatori. Sentiti libero di modificarlo per vedere come influisce sull'output.

Flusso di ripristino delle immagini

Con il prompt di ripristino pronto, ora creiamo la nostra applicazione CLI. Sostituisci i contenuti di main.go con il codice che segue:

main.go

package main

import (
        "context"
        "encoding/base64"
        "errors"
        "flag"
        "fmt"
        "log"
        "mime"
        "os"
        "os/signal"
        "strings"
        "syscall"

        "github.com/firebase/genkit/go/ai"
        "github.com/firebase/genkit/go/genkit"
        "github.com/firebase/genkit/go/plugins/googlegenai"
)

func main() {
        url := flag.String("url", "", "url of the image to restore")
        contentType := flag.String("contentType", "image/jpeg", "content type of the image (default: image/jpeg)")
        flag.Parse()

        ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
        defer cancel()

        // Initialize Genkit with the Vertex AI plugin
        g := genkit.Init(ctx,
                genkit.WithPlugins(&googlegenai.VertexAI{}),
        )

        // Input schema for the glowUp flow
        type Input struct {
                URL         string `json:"url,omitempty"`
                ContentType string `json:"contentType,omitempty"`
        }

        glowup := genkit.DefineFlow(g, "glowUp", func(ctx context.Context, input Input) (string, error) {
                // 1. Retrieve prompt
                prompt := genkit.LookupPrompt(g, "glowup")
                if prompt == nil {
                        return "", errors.New("prompt 'glowup' not found")
                }

                resp, err := prompt.Execute(ctx, ai.WithInput(input))
                if err != nil {
                        return "", fmt.Errorf("generation failed: %w", err)
                }

                return resp.Media(), nil
        })

        // triggers the flow and returns the encoded response from the model
        out, err := glowup.Run(ctx, Input{URL: *url, ContentType: *contentType})
        if err != nil {
                log.Fatalln(err)
        }

        // decodes image data and returns the appropriate file extension
        data, ext, err := decode(out)
        if err != nil {
                log.Fatalln(err)
        }

        // writes restored file to disk
        filename := "restored" + ext
        if err := os.WriteFile(filename, data, 0644); err != nil {
                log.Fatalln(err)
        }
}

// decode returns the decoded data and the file extension appropriate for the mime type
func decode(text string) ([]byte, string, error) {
        if !strings.HasPrefix(text, "data:") {
                return nil, "", errors.New("unsupported enconding format")
        }
        text = strings.TrimPrefix(text, "data:")
        parts := strings.Split(text, ";base64,")

        mimeType := parts[0]
        decoded, err := base64.StdEncoding.DecodeString(parts[1])
        if err != nil {
                return nil, "", err
        }

        ext, err := mime.ExtensionsByType(mimeType)
        if err != nil {
                return nil, "", err
        }

        return decoded, ext[0], nil
}

La struttura del codice è molto simile al flusso greeter, ma questa volta è adattato per essere eseguito come applicazione CLI autonoma. Anziché bloccare per sempre, eseguirà il flusso glowUp, decodificherà l'output e salverà i dati binari risultanti su disco.

Dobbiamo decodificare l'output perché il modello restituisce i dati dell'immagine nel seguente formato:

data:<mime type>;base64,<base64 encoded image>

Puoi notare nella funzione decode che utilizziamo la manipolazione delle stringhe per dividere il tipo MIME e le parti dell'immagine codificata. Poi utilizziamo le funzioni mime.ExtensionByType e base64.DecodeString per estrarre le informazioni necessarie per salvare il file.

Testare il flusso di ripristino

Ora è finalmente il momento di eseguire questo flusso con un'immagine reale. Se non hai a portata di mano vecchie foto che necessitano di restauro, puoi provare con questa immagine di dominio pubblico recuperata dal sito web della Library of Congress:

f0fc83a81e88052a.png

Ecco il link diretto alla fotografia da passare al flusso: https://tile.loc.gov/storage-services/service/pnp/fsa/8c01000/8c01700/8c01765v.jpg

export IMAGE_URL="https://tile.loc.gov/storage-services/service/pnp/fsa/8c01000/8c01700/8c01765v.jpg"
go run main.go --url $IMAGE_URL

Ecco l'output ripristinato e colorizzato:

5ed7bfcf6d26313c.png

Operazione riuscita.

6. Deployment di glowUp come servizio web

Se invece di un'applicazione a riga di comando vuoi esporre i tuoi flussi in un servizio web, Genkit offre un modo pratico per convertire i flussi in endpoint utilizzando l'adattatore genkit.Handler. Il codice riportato di seguito registra il flusso glowUp come endpoint glowUp utilizzando genkit.Handler.

Puoi esporli con un normale server HTTP come faresti normalmente utilizzando il pacchetto http della libreria standard, ma Genkit fornisce anche un plug-in server che aiuta con alcuni dei boilerplate comuni del server, come la gestione corretta dei segnali di arresto.

Sostituisci i contenuti di main.go con il codice riportato di seguito per creare il server web.

package main

import (
        "context"
        "errors"
        "fmt"
        "log"
        "net/http"
        "os"

        "github.com/firebase/genkit/go/ai"
        "github.com/firebase/genkit/go/genkit"
        "github.com/firebase/genkit/go/plugins/googlegenai"
        "github.com/firebase/genkit/go/plugins/server"
)

func main() {
        ctx := context.Background()

        PORT := os.Getenv("PORT")
        if PORT == "" {
                PORT = "8080"
        }

        listenAddr := ":" + PORT

        // Initialize Genkit with the Vertex AI plugin
        g := genkit.Init(ctx,
                genkit.WithPlugins(&googlegenai.VertexAI{}),
        )

        // Input schema for the glowUp flow
        type Input struct {
                URL         string `json:"url,omitempty"`
                ContentType string `json:"contentType,omitempty"`
        }

        genkit.DefineFlow(g, "glowUp", func(ctx context.Context, input Input) (string, error) {
                prompt := genkit.LookupPrompt(g, "glowup")
                if prompt == nil {
                        return "", errors.New("prompt 'glowup' not found")
                }

                resp, err := prompt.Execute(ctx, ai.WithInput(input))
                if err != nil {
                        return "", fmt.Errorf("generation failed: %w", err)
                }

                return resp.Media(), nil
        })

        log.Printf("GlowUp Flow Server started. Listening on %s", listenAddr)
        mux := http.NewServeMux()
        for _, flow := range genkit.ListFlows(g) {
                mux.HandleFunc("POST /"+flow.Name(), genkit.Handler(flow))
        }

        if err := server.Start(ctx, listenAddr, mux); err != nil {
                // Check if the error is due to context cancellation
                if ctx.Err() != nil {
                        log.Println("GlowUp server shutting down gracefully...")
                        return
                }
                log.Fatal(err)
        }
}

Puoi eseguire questo servizio localmente o nel cloud. Per l'esecuzione in locale, puoi eseguire il file direttamente perché il programma è ora completo, quindi non è necessario avviarlo tramite la CLI genkit. Ad esempio:

go run main.go

Poiché si tratta di un'operazione di blocco, devi avviare un secondo terminale per testarla. Il modo più rapido è utilizzare il comando curl:

curl -sS -X POST http://localhost:8080/glowUp \
     -H "Content-Type: application/json" \
     -d '{"data":{"url": $IMAGE_URL, "contentType":"image/jpeg"}}' \
     > result.json

Poiché si tratta di una risposta del server, dobbiamo decodificare l'immagine base64:

cat result.json | jq -r '.result' | awk -F ',' '{print $2}' | base64 -d > restored.png

Esegui il deployment del servizio web in Cloud Run

Va bene che questo server "funzioni sulla mia macchina", ma nel mondo ideale lo implementeremmo in un'altra posizione accessibile a tutti i nostri utenti. Uno dei modi più comodi per eseguire il deployment di servizi come questo è utilizzare la funzionalità di deployment da origine di Cloud Run. Con questa funzionalità non devi nemmeno creare il container, tutto è automatizzato.

Per eseguire il deployment di questo servizio su Cloud Run utilizzando il deployment dall'origine, esegui il seguente comando (sostituisci l'ID progetto con il tuo):

gcloud run deploy glowup --source . --region us-central1 --no-allow-unauthenticated --set-env-vars GOOGLE_GENAI_USE_VERTEXAI=True,GOOGLE_CLOUD_LOCATION=$GOOGLE_CLOUD_LOCATION,GOOGLE_CLOUD_PROJECT=$GOOGLE_CLOUD_PROJECT

Il completamento del deployment può richiedere alcuni minuti. Una volta terminato, puoi testare l'endpoint inviando un'altra richiesta tramite curl:

GLOWUP_URL=$(gcloud run services describe glowup --region us-central1 --format='value(status.url)')

curl -X POST "$GLOWUP_URL/glowUp" \
     -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
     -H "Content-Type: application/json" \
     -d "{\"data\":{\"url\":\"$IMAGE_URL\", \"contentType\":\"image/jpeg\"} }" \
     > result.json

Anche in questo caso, dobbiamo decodificare l'immagine base64:

cat result.json | jq -r '.result' | awk -F ',' '{print $2}' | base64 -d > restored_cloudrun.png

Ora abbiamo un server web di restauro delle foto completamente funzionante.

(Facoltativo) "Vibe coding" di un'applicazione client

Scrivere codice manualmente è divertente, ma può anche essere difficile se non hai mai realizzato un determinato tipo di progetto. Fortunatamente, oggi abbiamo agenti di programmazione che possono aiutarci ad accelerare il processo.

In questo passaggio, invece di scrivere il codice, chiederemo a Gemini CLI (o al tuo agente di programmazione preferito) di fare il lavoro per noi. Utilizza il seguente prompt:

GlowUp is a photo restoration service that takes a restoration request as input and returns a restored picture as output. Your task is to create a client application that uses this server to process image urls and save a restored file locally.

TODO:
- Write a CLI application that receives three arguments: an url (required), content type (optional, defaults to image/jpeg) and addr (server address, optional, defaults to localhost:8080)
- The CLI should send a POST request to the /glowUp endpoint in the server with the body '{"data":{"url": <url>, "contentType": <contentType>} }'
- The server response should be parsed by stripping the "data:" prefix. The remainder will have the format <mimeType>;base64,<encoded imageData>
- Extract the mime type and convert to a file extension using the mime package
- Extract the image data and decode it using the base64 package
Save a file named "restored" + the detected file extension

Acceptance Criteria:
- The client builds successfully
- Use the client to restore the image https://tile.loc.gov/storage-services/service/pnp/fsa/8c01000/8c01700/8c01765v.jpg
- Check that restored.png exists after processing the image above

L'agente potrebbe dover riorganizzare un po' i file, in quanto non possiamo avere due funzioni "main" nello stesso modulo. Osserva come funziona e indirizzalo verso l'implementazione corretta fornendogli il codice o gli snippet del server, se necessario.

Pulizia dopo il lab

7. Conclusione

Complimenti! Hai creato correttamente un'app di restauro di foto ad alta fedeltà utilizzando Genkit e Nano Banana Pro.

In questo codelab hai imparato a:

  • Configura l'ambiente per sviluppare applicazioni Genkit Go
  • Creare prompt multimodali con dotprompt
  • Creare flussi Genkit utilizzando i modelli di prompt
  • Utilizzare Nano Banana Pro per elaborare le immagini
  • Pacchettizzare i flussi Genkit come applicazioni a riga di comando e servizi web
  • Esegui il deployment delle applicazioni Genkit in Cloud Run

Al termine del test, ricordati di pulire l'ambiente.

Passaggi successivi

Puoi continuare il tuo percorso di apprendimento esplorando altri codelab su questa piattaforma o apportando miglioramenti a glowUp in autonomia.

Se hai bisogno di idee per i miglioramenti, puoi provare a:

  • Attiva la CLI glowUp per ripristinare i file locali
  • Crea un frontend per il servizio web GlowUp
  • Rileva automaticamente il tipo MIME dai dati
  • Esegui altri tipi di elaborazione delle immagini (da foto a disegno, da disegno a foto, da schizzo a opera d'arte finale e così via).
  • Creare o migliorare il client (magari creare un'app per smartphone?)

Le possibilità sono infinite. Se l'idea di procedere in autonomia ti spaventa un po', puoi sempre avvalerti dell'aiuto di un agente di programmazione come Gemini CLI o Antigravity. Se vuoi fare di più con Genkit, gli agenti di programmazione abbinati al server Genkit MCP possono semplificarti la vita.

Infine, se vuoi accedere al codice completo di questo repository, puoi trovarlo qui.

Buona programmazione!