פיתוח אפליקציה לשחזור תמונות באמצעות Genkit Go ו-Nano Banana Pro

1. מבוא

ב-Codelab הזה ניצור את GlowUp, כלי לשחזור תמונות. אפליקציית GlowUp משתמשת ב-AI כדי לשחזר תמונות ישנות, פגומות או בשחור-לבן, וליצור תמונות צבעוניות באיכות גבוהה ברזולוציית 4K. אתם יכולים להשתמש בכלי הזה כדי לתת חיים חדשים לתמונות משפחתיות, או אפילו להתאים אותו לשחזור של איורים, ציורים, תמונות או סוגים אחרים של תמונות שניזוקו.

תשתמשו ב-Genkit Go כדי להטמיע את הלוגיקה של האפליקציה, וב-Gemini 3 Pro Image (שנקרא גם Nano Banana Pro) בתור המודל לעיבוד התמונות.

דרישות מוקדמות

  • ידע בסיסי בשפת התכנות Go
  • ידע בסיסי במסוף Google Cloud

מה תלמדו

  • איך מפתחים אפליקציות Genkit ב-Go
  • מושגים בסיסיים ב-Genkit כמו רצפי פעולות, תוספים והנחיות
  • איך כותבים הנחיות באמצעות תבניות Handlebar
  • איך מקבלים נתוני תמונות מתשובות של מודלים

הדרישות

אפשר להשתתף בסדנה הזו באופן מלא ב-Google Cloud Shell, שכולל את כל התלות הנדרשת (ה-CLI של gcloud, עורך קוד, Go, ‏ Gemini CLI) מותקנת מראש.

לחלופין, אם אתם מעדיפים לעבוד על המחשב שלכם, תצטרכו את הדברים הבאים:

  • ערכת הכלים של Go (גרסה 1.24 ואילך)
  • ‫Node.js גרסה 20 ואילך (ל-genkit CLI)
  • טרמינל עם gcloud CLI מותקן
  • סביבת פיתוח משולבת (IDE) לעריכת הקוד, כמו VS Code או דומה
  • מומלץ: סוכן לכתיבת קוד כמו Gemini CLI או Antigravity

טכנולוגיות מרכזיות

כאן אפשר למצוא מידע נוסף על הטכנולוגיות שבהן נשתמש:

  • ‫Gemini Nano Banana Pro (Gemini 3 Pro Image): המודל שמפעיל את תהליך השחזור שלנו
  • ‫Genkit Go: ערכת הכלים שלנו לניהול קריאות למודלים

2. הגדרת הסביבה

אתם יכולים להריץ את ה-Lab הזה מהמחשב שלכם או לגמרי בענן. גם אם תחליטו להריץ את הקוד באופן מקומי, תצטרכו פרויקט בענן ב-Google Cloud כדי לפרוס את הרכיבים בצד השרת בענן. בקטע הבא מוסבר איך ליצור את מרכז הבקרה הזה.

הגדרת סביבה בקצב אישי

  1. נכנסים ל-מסוף Google Cloud ויוצרים פרויקט חדש או משתמשים בפרויקט קיים. אם עדיין אין לכם חשבון Gmail או Google Workspace, אתם צריכים ליצור חשבון.

295004821bab6a87.png

37d264871000675d.png

96d86d3d5655cdbe.png

  • שם הפרויקט הוא השם המוצג של הפרויקט הזה למשתתפים. זו מחרוזת תווים שלא נמצאת בשימוש ב-Google APIs. תמיד אפשר לעדכן את המיקום.
  • מזהה הפרויקט הוא ייחודי לכל הפרויקטים ב-Google Cloud, והוא קבוע (אי אפשר לשנות אותו אחרי שהוא מוגדר). מסוף Cloud יוצר באופן אוטומטי מחרוזת ייחודית, ובדרך כלל לא צריך לדעת מה היא. ברוב ה-Codelabs, תצטרכו להפנות למזהה הפרויקט (בדרך כלל מסומן כ-PROJECT_ID). אם אתם לא אוהבים את המזהה שנוצר, אתם יכולים ליצור מזהה אקראי אחר. אפשר גם לנסות שם משתמש משלכם ולבדוק אם הוא זמין. אי אפשר לשנות את ההגדרה הזו אחרי השלב הזה, והיא נשארת לאורך הפרויקט.
  • לידיעתכם, יש ערך שלישי, מספר פרויקט, שחלק מממשקי ה-API משתמשים בו. מידע נוסף על שלושת הערכים האלה מופיע במאמרי העזרה.
  1. בשלב הבא, תצטרכו להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבי Cloud או בממשקי API של Cloud. השלמת ה-codelab הזה לא תעלה לכם הרבה, אם בכלל. כדי להשבית את המשאבים ולמנוע חיובים נוספים אחרי שתסיימו את המדריך הזה, תוכלו למחוק את המשאבים שיצרתם או למחוק את הפרויקט. משתמשים חדשים ב-Google Cloud זכאים לתוכנית תקופת ניסיון בחינם בשווי 300$.

מפעילים את Cloud Shell

אפשר להפעיל את Google Cloud מרחוק מהמחשב הנייד, אבל ב-Codelab הזה נשתמש ב-Google Cloud Shell, סביבת שורת פקודה שפועלת בענן.

ב-מסוף Google Cloud, לוחצים על סמל Cloud Shell בסרגל הכלים שבפינה הימנית העליונה:

הפעלת Cloud Shell

יחלפו כמה רגעים עד שההקצאה והחיבור לסביבת העבודה יושלמו. בסיום התהליך, אמור להופיע משהו כזה:

צילום מסך של טרמינל Google Cloud Shell שבו מוצג שהסביבה מחוברת

המכונה הווירטואלית הזו כוללת את כל הכלים שדרושים למפתחים. יש בה ספריית בית בנפח מתמיד של 5GB והיא פועלת ב-Google Cloud, מה שמשפר מאוד את הביצועים והאימות ברשת. אפשר לבצע את כל העבודה ב-codelab הזה בדפדפן. לא צריך להתקין שום דבר.

3. הגדרת הפרויקט

יצירת הפרויקט

קודם כל, צריך ליצור ספרייה חדשה לפרויקט ולאתחל את מודול Go. בטרמינל, מריצים את הפקודות הבאות:

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

התקנת Genkit CLI

עכשיו צריך להתקין את Genkit CLI. כך מקבלים גישה לכלים מקומיים למפתחים, כולל ממשק המשתמש למפתחים. בחלון הטרמינל, מקלידים:

curl -sL cli.genkit.dev | bash

הגדרת משתני סביבה

מוודאים שהגדרתם את פרטי הכניסה הנכונים ל-Google Cloud. כשמשתמשים במודלים בגרסת טרום-השקה של Gemini 3 Pro (אחד מהם הוא Nano Banana Pro), המיקום חייב להיות global.

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

מריצים את הפקודה הבאה במצב מעטפת כדי להפעיל את Vertex AI API בפרויקט הנוכחי:

gcloud services enable aiplatform.googleapis.com

אם אתם מריצים את הפקודה מהמחשב המקומי (לא ב-Cloud Shell), תצטרכו לבצע אימות באמצעות הפקודה gcloud:

gcloud auth application-default login

4. יצירת אפליקציית Genkit הראשונה

‫Genkit הוא מסגרת קוד פתוח שנועדה לעזור למפתחים ליצור, לפרוס ולנטר אפליקציות מבוססות-AI שמוכנות לייצור. בקטע הזה ניצור אפליקציית Hello World פשוטה כדי שתכירו את המסגרת לפני שנעמיק בלוגיקה של שחזור התמונות.

הסברים על המונחים ב-Genkit

לפני שמתחילים לעבוד עם Genkit, חשוב להבין כמה מונחים מרכזיים:

  • פלאגינים: משמשים להרחבת היכולות של Genkit. בין היתר, באמצעות פלאגינים אתם רושמים מודלים של AI כדי להפעיל את האפליקציה שלכם.
  • תהליכי עבודה: הרכיב הארכיטקטוני העיקרי של Genkit. תהליך טיפוסי מקבל קלט, מעבד אותו ומחזיר פלט. לא תמיד צריך להשתמש במודל, אבל ברוב המקרים תשתמשו במודלים בתוך התהליכים.
  • הנחיות: תבניות של אינטראקציות שמאוחסנות בפורמט dotprompt (נשמרות כקבצי *.prompt). הם מכילים לא רק את ההוראות למודל, אלא גם הגדרות כמו שם המודל, פרמטרים של המודל, קלט ופלט.

יצירת קשר עם מודלים של AI

‫Genkit משתמש בתוספים כדי לקשר את הקוד שלכם לספקי מודלים. יש תוספים לכל ספקי המודלים הגדולים, כולל Google, ‏ Anthropic ו-OpenAI. אפשר גם להשתמש בתוספים כדי להתחבר למודלים מקומיים (למשל באמצעות Ollama) או כדי להרחיב את היכולות של Genkit (למשל להתחבר לשרתי MCP).

כדי לגשת למודלים של Google, צריך להשתמש בתוסף googlegenai. הוא תומך בשני סוגים של שרתים עורפיים:

  • ‫Google AI: הכי טוב ליצירת אב טיפוס. משתמש במפתח API.
  • ‫Vertex AI (Google Cloud): מומלץ לשימוש בסביבת הייצור. שימוש במזהה הפרויקט ובמיקום.

ב-Codelab הזה נשתמש באימות של Vertex AI בהתייחס לפרויקט שיצרתם בתחילת ה-Lab.

יצירת תהליך של הודעת פתיחה

לפני שנעמיק בתהליך של שיפור התמונות, נבנה תהליך בסיסי כדי להכיר את המושגים ולוודא שההגדרה שלנו פועלת בצורה תקינה.

‫Flow היא פונקציה מיוחדת של Genkit שעוטפת את הלוגיקה של ה-AI כדי לספק:

  • קלטים ופלטים בטוחים מבחינת סוג: הגדרת סכימות באמצעות מבני נתונים (structs) של Go לצורך אימות סטטי ואימות בזמן ריצה
  • תמיכה בסטרימינג: סטרימינג של תשובות חלקיות או נתונים מותאמים אישית
  • שילוב של ממשק משתמש למפתחים: בדיקה וניפוי באגים של תהליכים באמצעות עקבות ויזואליות
  • פריסה קלה: אפשר לפרוס כנקודות קצה של HTTP בכל פלטפורמה

פותחים את סביבת הפיתוח המשולבת ויוצרים קובץ main.go בספריית הפרויקט. אם משתמשים ב-Cloud Shell, אפשר להשתמש בפקודה הבאה:

cloudshell edit main.go

לאחר מכן מוסיפים את הקוד הבא:

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()
}

שומרים את הקובץ ואז מריצים את הפקודה go mod tidy כדי לעדכן את התלויות:

go mod tidy

הקוד שלמעלה מאתחל את Genkit באמצעות הפלאגין VertexAI, מגדיר תהליך בשם greeter ואז ממתין לנצח ב-<-ctx.Done(). אנחנו מעכבים את ההפעלה כדי שהתוכנית לא תסתיים באופן מיידי, כי אנחנו לא נותנים לה הוראות להפעיל את התהליך.

המשמעות היא שאם תריצו את התוכנית הזו כמו שהיא עכשיו, היא לא תעשה הרבה בעצמה – צריך להפעיל את רצף הפעולות בצורה כלשהי. בזמן פיתוח, אפשר להשתמש בכלי genkit CLI כדי לפתח את התהליך ולבצע בו אופטימיזציה. באפליקציית ייצור אמיתית, נשתמש בשרת אינטרנט או באפליקציית CLI כדי לעטוף את התהליך הזה.

פיתחנו את genkit CLI כדי לעזור לנו לבדוק מודלים, הנחיות, תהליכים ורכיבים אחרים של חבילת Genkit. בנוסף, יש לו תמיכה מלאה בנתוני מעקב, וזה מאוד נוח כשרוצים להבין איך האפליקציה פועלת מאחורי הקלעים. כדי להפעיל את תהליך ההצטרפות באמצעות genkit CLI, מריצים את הפקודה הבאה:

genkit start -- go run main.go

פעולה זו תתחיל הרצה של נקודות הקצה של Telemetry API ושל ממשק המשתמש למפתחים. אתם אמורים לראות משהו כזה:

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

אם פותחים את localhost:4000 בדפדפן כדי להפעיל את ממשק המשתמש למפתחים. יוצג מסך כמו זה:

93d9bd9e1bd6627d.png

כדאי להקדיש זמן כדי להכיר את ממשק המשתמש למפתחים. אפשר להפעיל את תהליך ההצטרפות פעם אחת רק כדי לראות איך הוא עובד.

שימוש בתבניות של הנחיות

אפשר להגדיר את ההנחיות בהקוד קשיח בקריאות למודל כמו בדוגמה הקודמת, אבל גישה טובה יותר היא ליצור תבניות הנחיות כדי לאחסן את כל ההנחיות של האפליקציה בצורה מרכזית. כך קל יותר למצוא אותם כשמבצעים תחזוקה של הקוד, וגם אפשר להתנסות בהנחיות בנפרד מהזרימות.

כדי להגדיר תבניות של הנחיות, Genkit משתמש בפורמט dotprompt בקוד פתוח, שנשמר כקובצי *.prompt. קובץ .prompt מורכב משני חלקים:

  1. Frontmatter: בלוק YAML שמגדיר את המודל, את הפרמטרים של המודל ואת סכימות הקלט והפלט
  2. גוף: גוף הפרומפט עצמו, שאפשר ליצור לו תבנית באמצעות תחביר handlebars. לדוגמה, אם מגדירים קלט בשם variable-name, אפשר להפנות אליו בגוף הבקשה בתור {{variable-name}}.

מבנה הספריות של הפרויקט ייראה כך:

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

בואו נראה דוגמה. קודם יוצרים את התיקייה לאחסון ההנחיות:

mkdir -p prompts

אחר כך יוצרים קובץ greeter.prompt:

cloudshell edit prompts/greeter.prompt

ומכניסים את התוכן הבא:

greeter.prompt

ec9fc82a98604123.png

ההנחיה הזו מדגימה כמה מהתכונות של שפת התבניות. קודם כול, מציינים את המודל vertexai/gemini-2.5-flash בחלק העליון של הקובץ. כדי לציין מודל, צריך להשתמש במינוח שמוגדר במסמכי התיעוד של הפלאגין הרלוונטי.

בקטע config אפשר להגדיר את הפרמטרים של המודל. אנחנו משתמשים ברמת אקראיות 1.9 כדי לאפשר למודל להיות יצירתי יותר. רמת אקראיות נעה בין 0 (תוצאות עקביות יותר) ל-2 (תוצאות יצירתיות יותר). הפרמטר הזה ופרמטרים אחרים של המודל מתפרסמים בדרך כלל בגיליון המודל. לדוגמה, הנה גיליון המודל של gemini-2.5-flash.

בקטע הקלט אפשר לציין ארגומנטים להנחיה. במקרה הזה, אנחנו מגדירים את השם כמחרוזת. אחרי ה-frontmatter מופיע גוף ההנחיה. הבלוק הראשון כאן מגדיר את הנחיית המערכת, והבלוק השני הוא הנחיית המשתמש. השילוב של כל האלמנטים האלה מאפשר ליצור טכניקות הנחיה חזקות מאוד. כאן אפשר לעיין במסמכי התיעוד המלאים של dotprompt.

עכשיו נתאים את התהליך greeter כדי להשתמש בהנחיה שיצרנו:

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()
}

כדאי לנסות להריץ את התהליך משורת הפקודה, עם שם ובלי שם, כדי לראות את ההבדל. הפעלה ללא שם:

genkit flow:run greeter

פלט לדוגמה:

$ 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!"

עם שם:

genkit flow:run greeter '"Daniela"'

פלט לדוגמה:

$ genkit flow:run greeter '"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!"

אפשר לראות שהתבנית פעלה כמצופה. אנחנו מוכנים לקחת את הפרויקט הזה לשלב הבא.

5. שחזור תמונות באמצעות Nano Banana Pro

ההנחיה לשחזור

אחרי שהכרתם את אבני הבניין של Genkit, הגיע הזמן ליצור את ההנחיה לשחזור התמונה.

יוצרים קובץ בשם glowup.prompt בספריית ההנחיות ומדביקים בו את התוכן הבא:

glowup.prompt

fd0f1551466c8138.png

ההנחיה לשחזור פועלת לפי אותו דפוס כמו ההנחיה שלנו לשיחה עם עוזר דיגיטלי, עם כמה הבדלים בולטים:

  • גודל התמונה: המאפיין imageSize של imageConfig הוא פרמטר ספציפי למודל Nano Banana Pro. הוא מאפשר לנו לציין את גודל הפלט כ-1K, ‏ 2K או 4K.
  • קלט מדיה: אנחנו משתמשים בתבנית {{ media }} כדי להוסיף את התמונה להנחיה של המשתמש. הטכניקה הזו מאפשרת לנו לשלוח למודל הנחיות מולטי-מודאליות (טקסט + תמונה).

אפשר לבדוק את ההנחיה הזו בממשק המשתמש למפתחים. אתם יכולים לשנות את ההנחיה כדי לראות איך זה משפיע על הפלט.

תהליך שחזור התמונות

אחרי שההנחיה לשחזור מוכנה, אפשר להתחיל לבנות את אפליקציית ה-CLI. מחליפים את התוכן של main.go בקוד שלמטה:

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
}

מבנה הקוד דומה מאוד לזרימת העבודה של greeter, אבל הפעם הוא מותאם לפעולה כאפליקציית CLI עצמאית. במקום לחסום את הפעולה לתמיד, המערכת תפעיל את התהליך glowUp, תפענח את הפלט ותשמור את הנתונים הבינאריים שיתקבלו בדיסק.

צריך לפענח את הפלט כי המודל מחזיר את נתוני התמונה בפורמט הבא:

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

אפשר לראות בפונקציה decode שאנחנו משתמשים במניפולציה של מחרוזות כדי לפצל את סוג ה-MIME ואת חלקי התמונה המקודדים. לאחר מכן אנחנו משתמשים בפונקציות mime.ExtensionByType ו-base64.DecodeString כדי לחלץ את המידע שדרוש לנו לשמירת הקובץ.

בדיקת תהליך השחזור

עכשיו הגיע הזמן להריץ את התהליך הזה עם תמונה אמיתית. אם אין לכם תמונות ישנות שצריך לשחזר, אתם יכולים לנסות עם התמונה הזו שזמינה בדומיין ציבורי ואוחזרה מהאתר של ספריית הקונגרס:

f0fc83a81e88052a.png

הנה קישור ישיר לתמונה כדי להעביר אותה לתהליך: 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

וכאן מופיע הפלט המשוחזר והצבוע:

5ed7bfcf6d26313c.png

הצלחת!

6. פריסת glowUp כשירות אינטרנט

אם במקום אפליקציית שורת פקודה אתם רוצים לחשוף את התהליכים שלכם בשירות אינטרנט, Genkit מספק דרך נוחה להמיר תהליכים לנקודות קצה באמצעות מתאם genkit.Handler. הקוד שלמטה רושם את רכיב ה-Flow‏ glowUp כנקודת קצה של glowUp באמצעות genkit.Handler.

אפשר לחשוף אותם באמצעות שרת HTTP רגיל כמו שאתם עושים בדרך כלל באמצעות חבילת http מהספרייה הרגילה, אבל Genkit מספק גם פלאגין server שעוזר עם חלק מהקוד הנפוץ של השרת, כמו טיפול באותות כיבוי בצורה תקינה.

מחליפים את התוכן של main.go בקוד שבהמשך כדי ליצור את שרת האינטרנט.

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)
        }
}

אפשר להריץ את השירות הזה באופן מקומי או בענן. כדי להריץ את הקובץ באופן מקומי, אפשר להריץ אותו ישירות כי התוכנית הושלמה, ולכן לא צריך להפעיל אותה באמצעות ממשק שורת הפקודה (CLI) של Genkit. לדוגמה:

go run main.go

מכיוון שזו פעולת חסימה, צריך להפעיל טרמינל שני כדי לבדוק אותה. הדרך הכי מהירה היא להשתמש בפקודה curl:

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

מכיוון שזו תגובת שרת, צריך לפענח את התמונה בקידוד base64:

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

פריסת שירות האינטרנט ב-Cloud Run

זה בסדר שהשרת הזה "פועל במחשב שלי", אבל בעולם אידיאלי היינו פורסים אותו במקום אחר שנגיש לכל המשתמשים שלנו. אחת הדרכים הנוחות ביותר לפרוס שירותים כאלה היא באמצעות התכונה 'פריסה ממקור' של Cloud Run. בעזרת התכונה הזו, אתם לא צריכים אפילו לבנות את מאגר התגים בעצמכם – הכול אוטומטי.

כדי לפרוס את השירות הזה ב-Cloud Run באמצעות פריסה ממקור, מריצים את הפקודה הבאה (מחליפים את מזהה הפרויקט במזהה שלכם):

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

הפריסה עשויה להימשך כמה דקות. אחרי שמסיימים, אפשר לבדוק את נקודת הקצה על ידי שליחת בקשה נוספת דרך 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

שוב, צריך לפענח את התמונה בקידוד base64:

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

עכשיו יש לנו שרת אינטרנט לשחזור תמונות שפועל באופן מלא.

אופציונלי: תכנות בשיטת Vibe coding באפליקציית לקוח

לכתוב קוד באופן ידני זה כיף, אבל זה יכול להיות גם מאתגר אם אף פעם לא עשיתם פרויקט מסוג מסוים. למזלנו, היום יש לנו סוכני תכנות שיכולים לעזור לנו לזרז את התהליך.

בשלב הזה, במקום לכתוב את הקוד בעצמנו, נבקש מ-Gemini CLI (או מסוכן התכנות המועדף עליכם) לבצע את העבודה בשבילנו. משתמשים בהנחיה הבאה:

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

יכול להיות שהסוכן יצטרך לארגן מחדש את הקבצים, כי אי אפשר להשתמש בשתי פונקציות בשם main באותו מודול. צופים בה בזמן שהיא פועלת ומכוונים אותה ליישום הנכון, ומספקים לה את קוד השרת או קטעי קוד אם צריך.

ניקוי אחרי שיעור Lab

7. סיכום

מעולה! יצרתם בהצלחה אפליקציה לשחזור תמונות ברמת דיוק גבוהה באמצעות Genkit ו-Nano Banana Pro,

ב-codelab הזה למדתם איך:

  • הגדרת הסביבה לפיתוח אפליקציות Genkit Go
  • יצירת הנחיות מרובות מצבים באמצעות dotprompt
  • יצירת תהליכי עבודה ב-Genkit באמצעות תבניות של הנחיות
  • שימוש ב-Nano Banana Pro לעיבוד תמונות
  • אריזת תהליכי עבודה של Genkit כאפליקציות לשורת פקודה ושירותי אינטרנט
  • פריסת אפליקציות Genkit ב-Cloud Run

אחרי שמסיימים את הבדיקה, חשוב לנקות את הסביבה.

השלבים הבאים

אתם יכולים להמשיך את מסע הלמידה שלכם ולעיין ב-codelabs נוספים בפלטפורמה הזו, או לשפר את glowUp בעצמכם.

אם אתם רוצים לקבל רעיונות לשיפורים, אתם יכולים לנסות:

  • הפעלת glowUp CLI כדי לשחזר קבצים מקומיים
  • יצירת קצה קדמי לשירות האינטרנט GlowUp
  • זיהוי אוטומטי של סוג ה-MIME מתוך הנתונים
  • לבצע סוגים אחרים של עיבוד תמונה (מתמונה לציור, מציור לתמונה, מסקיצה לאומנות סופית וכו'
  • יצירה או שיפור של הלקוח (אולי בניית אפליקציה לסמארטפון?)

האפשרויות הן אינסופיות! אם הרעיון של יציאה לדרך לבד נראה לכם קצת מפחיד, תמיד תוכלו להיעזר בסוכן קוד כמו Gemini CLI או Antigravity. אם אתם רוצים לעשות עוד דברים עם Genkit, תוכלו להשתמש בסוכני קידוד שמשולבים עם שרת Genkit MCP כדי להקל על עצמכם.

לבסוף, אם רוצים לגשת לקוד המלא של מאגר המידע הזה, אפשר למצוא אותו כאן.

שיהיה בהצלחה!