تکنیک‌های مشاهده‌پذیری عملی برای کاربرد هوش مصنوعی در Go

1. بررسی اجمالی

برنامه‌های هوش مصنوعی ژنرال مانند سایر برنامه‌ها به قابلیت مشاهده نیاز دارند. آیا تکنیک های مشاهده پذیری خاصی برای هوش مصنوعی مولد نیاز است؟

در این آزمایشگاه، یک اپلیکیشن ساده Gen AI ایجاد خواهید کرد. آن را در Cloud Run مستقر کنید. و با استفاده از سرویس‌ها و محصولات مشاهده‌پذیری Google Cloud، آن را با قابلیت‌های نظارت و گزارش‌گیری ضروری تنظیم کنید.

آنچه خواهید آموخت

  • برنامه ای بنویسید که از Vertex AI با Cloud Shell Editor استفاده کند
  • کد برنامه خود را در GitHub ذخیره کنید
  • از gcloud CLI برای استقرار کد منبع برنامه خود در Cloud Run استفاده کنید
  • قابلیت های نظارت و گزارش را به برنامه Gen AI خود اضافه کنید
  • استفاده از متریک های مبتنی بر گزارش
  • پیاده سازی گزارش و نظارت با Open Telemetry SDK
  • بینشی در مورد مدیریت مسئول داده های هوش مصنوعی به دست آورید

2. پیش نیازها

اگر قبلاً یک حساب Google ندارید، باید یک حساب جدید ایجاد کنید .

3. راه اندازی پروژه

  1. با حساب Google خود وارد Google Cloud Console شوید.
  2. یک پروژه جدید ایجاد کنید یا استفاده مجدد از یک پروژه موجود را انتخاب کنید. شناسه پروژه پروژه ای که به تازگی ایجاد یا انتخاب کرده اید را یادداشت کنید.
  3. فعال کردن صورتحساب برای پروژه
    • تکمیل این آزمایشگاه باید کمتر از 5 دلار هزینه صورت حساب داشته باشد.
    • برای جلوگیری از هزینه های بیشتر، می توانید مراحل انتهای این آزمایشگاه را برای حذف منابع دنبال کنید.
    • کاربران جدید واجد شرایط استفاده از نسخه آزمایشی رایگان 300 دلاری هستند.
  4. تأیید اینکه صورت‌حساب در پروژه‌های من در صورت‌حساب ابری فعال است
    • اگر پروژه جدید شما می گوید که صورتحساب در ستون Billing account Billing is disabled :
      1. روی سه نقطه در ستون Actions کلیک کنید
      2. روی تغییر صورتحساب کلیک کنید
      3. حساب صورتحساب مورد نظر برای استفاده را انتخاب کنید
    • اگر در یک رویداد زنده شرکت می‌کنید، احتمالاً این حساب Google Cloud Platform Trial Billing Account نامیده می‌شود

4. Cloud Shell Editor را آماده کنید

  1. به Cloud Shell Editor بروید. اگر پیام زیر از شما خواسته شد که درخواست اجازه دادن به پوسته ابری برای تماس با gcloud با اعتبار شما را دارد، برای ادامه روی تأیید کلیک کنید.
    برای تأیید Cloud Shell کلیک کنید
  2. پنجره ترمینال را باز کنید
    1. روی منوی همبرگر کلیک کنید نماد منوی همبرگر
    2. روی ترمینال کلیک کنید
    3. روی New Terminal کلیک کنید
      ترمینال جدید را در Cloud Shell Editor باز کنید
  3. در ترمینال، شناسه پروژه خود را پیکربندی کنید:
    gcloud config set project [PROJECT_ID]
    
    [PROJECT_ID] را با شناسه پروژه خود جایگزین کنید. به عنوان مثال، اگر ID پروژه شما lab-example-project باشد، دستور به صورت زیر خواهد بود:
    gcloud config set project lab-project-id-example
    
    اگر پیام زیر از شما خواسته شد که می گوید gcloud اعتبار شما را به GCPI API درخواست می کند، برای ادامه روی تأیید کلیک کنید.
    برای تأیید Cloud Shell کلیک کنید
    در اجرای موفقیت آمیز باید پیام زیر را مشاهده کنید:
    Updated property [core/project].
    
    اگر یک WARNING مشاهده کردید و از شما پرسیده شد Do you want to continue (Y/N)? ، پس احتمالاً شناسه پروژه را اشتباه وارد کرده اید. N را فشار دهید، Enter فشار دهید و پس از یافتن شناسه پروژه صحیح، دوباره دستور gcloud config set project را اجرا کنید.
  4. (اختیاری) اگر برای پیدا کردن شناسه پروژه با مشکل مواجه هستید، دستور زیر را اجرا کنید تا شناسه پروژه همه پروژه‌های خود را بر اساس زمان ایجاد به ترتیب نزولی مرتب کنید:
    gcloud projects list \
         --format='value(projectId,createTime)' \
         --sort-by=~createTime
    

5. Google API ها را فعال کنید

در ترمینال، API های Google را که برای این آزمایشگاه مورد نیاز است فعال کنید:

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

تکمیل این دستور کمی طول می کشد. در نهایت، پیام موفقیت آمیزی مشابه این را تولید می کند:

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

اگر پیام خطایی دریافت کردید که با ERROR: (gcloud.services.enable) HttpError accessing جزئیات خطا و حاوی جزئیات خطا مانند زیر است، پس از 1 تا 2 دقیقه تاخیر دوباره دستور را امتحان کنید.

"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. یک برنامه Gen AI Go ایجاد کنید

در این مرحله شما یک کد از برنامه ساده مبتنی بر درخواست را می نویسید که از مدل Gemini برای نشان دادن 10 واقعیت سرگرم کننده در مورد حیوان مورد نظر شما استفاده می کند. برای ایجاد کد برنامه مراحل زیر را انجام دهید.

  1. در ترمینال، دایرکتوری codelab-o11y را ایجاد کنید:
    mkdir ~/codelab-o11y
    
  2. دایرکتوری فعلی را به codelab-o11y تغییر دهید:
    cd ~/codelab-o11y
    
  3. ماژول های Go را راه اندازی کنید:
    go mod init codelab
    
  4. Vertex AI SDK for Go را نصب کنید:
    go get cloud.google.com/go/vertexai/genai
    
  5. برای دریافت شناسه پروژه فعلی، کتابخانه فراداده را برای Go نصب کنید:
    go get cloud.google.com/go/compute/metadata
    
  6. یک فایل setup.go ایجاد کنید و فایل را در Cloud Shell Editor باز کنید:
    cloudshell edit setup.go
    
    برای میزبانی کد اولیه استفاده خواهد شد. یک فایل خالی جدید با نام setup.go در پنجره ویرایشگر ظاهر می شود.
  7. کد زیر را کپی کرده و در فایل setup.go باز شده قرار دهید:
    package main
    
    import (
        "context"
        "os"
    
        "cloud.google.com/go/compute/metadata"
    )
    
    func projectID(ctx context.Context) (string, error) {
        var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")
        if projectID == "" {
               return metadata.ProjectIDWithContext(ctx)
        }
        return projectID, nil
    }
    
  8. به پنجره ترمینال برگردید و دستور زیر را برای ایجاد و باز کردن فایل main.go در Cloud Shell Editor اجرا کنید:
    cloudshell edit main.go
    
    اکنون باید یک فایل خالی در پنجره ویرایشگر بالای ترمینال ظاهر شود. صفحه نمایش شما شبیه به شکل زیر خواهد بود:
    نمایش Cloud Shell Editor پس از شروع ویرایش main.go
  9. کد زیر را کپی کرده و در فایل main.go باز شده قرار دهید:
    package main
    
    import (
        "context"
        "fmt"
        "net/http"
        "os"
    
        "cloud.google.com/go/vertexai/genai"
    )
    
    var model *genai.GenerativeModel
    
    func main() {
        ctx := context.Background()
        projectID, err := projectID(ctx)
        if err != nil {
            return
        }
    
        var client *genai.Client
        client, err = genai.NewClient(ctx, projectID, "us-central1")
        if err != nil {
            return
        }
        defer client.Close()
           model = client.GenerativeModel("gemini-1.5-flash-001")
           http.HandleFunc("/", Handler)
           port := os.Getenv("PORT")
        if port == "" {
            port = "8080"
        }
        if err := http.ListenAndServe(":"+port, nil); err != nil {
            return
        }
    }
    
    func Handler(w http.ResponseWriter, r *http.Request) {
        animal := r.URL.Query().Get("animal")
        if animal == "" {
            animal = "dog"
        }
    
        prompt := fmt.Sprintf("Give me 10 fun facts about %s. Return the results as HTML without markdown backticks.", animal)
        resp, err := model.GenerateContent(r.Context(), genai.Text(prompt))
        if err != nil {
            w.WriteHeader(http.StatusTooManyRequests)
            return
        }
    
        if len(resp.Candidates) > 0 && len(resp.Candidates[0].Content.Parts) > 0 {
            htmlContent := resp.Candidates[0].Content.Parts[0]
            w.Header().Set("Content-Type", "text/html; charset=utf-8")
            fmt.Fprint(w, htmlContent)
        }
    }
    
    پس از چند ثانیه، Cloud Shell Editor کد شما را به صورت خودکار ذخیره می کند.

کد برنامه Gen AI را در Cloud Run مستقر کنید

  1. در پنجره ترمینال دستور استقرار کد منبع برنامه را در Cloud Run اجرا کنید.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    اگر دستور زیر را مشاهده کردید، به شما اطلاع می دهد که این دستور یک مخزن جدید ایجاد می کند. روی 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)?
    
    فرآیند استقرار ممکن است تا چند دقیقه طول بکشد. پس از تکمیل فرآیند استقرار، خروجی هایی مانند زیر را مشاهده خواهید کرد:
    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. URL نمایش داده شده سرویس Cloud Run را در یک برگه یا پنجره جداگانه در مرورگر خود کپی کنید. همچنین، دستور زیر را در ترمینال اجرا کنید تا URL سرویس چاپ شود و در حالی که کلید Ctrl را نگه داشته اید، روی URL نشان داده شده کلیک کنید تا URL باز شود:
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    وقتی URL باز می شود، ممکن است خطای 500 دریافت کنید یا این پیام را ببینید:
    Sorry, this is just a placeholder...
    
    این بدان معنی است که سرویس ها استقرار خود را به پایان نرسانده اند. چند لحظه صبر کنید و صفحه را رفرش کنید. در پایان متنی خواهید دید که با Fun Dog Facts شروع می شود و حاوی 10 واقعیت سرگرم کننده در مورد سگ است.

سعی کنید با برنامه تعامل داشته باشید تا حقایق جالبی در مورد حیوانات مختلف به دست آورید. برای انجام این کار، پارامتر animal را به URL اضافه کنید، مانند ?animal=[ANIMAL] که در آن [ANIMAL] نام حیوان است. به عنوان مثال، ?animal=cat برای دریافت 10 واقعیت سرگرم کننده در مورد گربه ها یا ?animal=sea turtle برای دریافت 10 واقعیت سرگرم کننده در مورد لاک پشت های دریایی اضافه کنید.

7. تماس های Vertex API خود را بررسی کنید

ممیزی تماس‌های Google API پاسخ‌هایی را به سوالاتی مانند «چه کسی یک API خاص، کجا و چه زمانی را فراخوانی می‌کند؟» ارائه می‌دهد. ممیزی زمانی مهم است که برنامه خود را عیب یابی می کنید، مصرف منابع را بررسی می کنید یا تجزیه و تحلیل قانونی نرم افزاری را انجام می دهید.

گزارش‌های حسابرسی به شما امکان می‌دهند فعالیت‌های اداری و سیستمی را ردیابی کنید و همچنین تماس‌های مربوط به عملیات API "خواندن داده" و "نوشتن داده" را ثبت کنید. برای بررسی درخواست‌های Vertex AI برای تولید محتوا، باید گزارش‌های حسابرسی "Data Read" را در کنسول Cloud فعال کنید .

  1. روی دکمه زیر کلیک کنید تا صفحه Audit Logs در کنسول Cloud باز شود

  2. مطمئن شوید که صفحه پروژه ای را که برای این آزمایشگاه ایجاد کرده اید انتخاب کرده باشد. پروژه انتخاب شده در گوشه سمت چپ بالای صفحه سمت راست از منوی همبرگر نشان داده شده است:
    کشویی پروژه Google Cloud Console
    در صورت لزوم، پروژه صحیح را از جعبه ترکیبی انتخاب کنید.
  3. در جدول پیکربندی گزارش های حسابرسی داده دسترسی ، در ستون Service، سرویس Vertex AI API را پیدا کنید و با انتخاب کادر انتخاب سمت چپ از نام سرویس، سرویس را انتخاب کنید.
    Vertex AI API را انتخاب کنید
  4. در پانل اطلاعات سمت راست، نوع حسابرسی "Data Read" را انتخاب کنید.
    گزارش های خواندن داده ها را بررسی کنید
  5. روی ذخیره کلیک کنید.

برای ایجاد گزارش های حسابرسی URL سرویس را باز کنید. با تغییر مقدار پارامتر ?animal= صفحه را بازخوانی کنید تا نتایج متفاوتی دریافت کنید.

گزارش های حسابرسی را کاوش کنید

  1. روی دکمه زیر کلیک کنید تا صفحه Logs Explorer در کنسول Cloud باز شود:

  2. فیلتر زیر را در قسمت Query قرار دهید.
    LOG_ID("cloudaudit.googleapis.com%2Fdata_access") AND
    protoPayload.serviceName="aiplatform.googleapis.com"
    
    پنجره Query ویرایشگری است که در نزدیکی بالای صفحه Logs Explorer قرار دارد:
    استعلام گزارش های حسابرسی
  3. روی Run query کلیک کنید.
  4. یکی از ورودی های گزارش حسابرسی را انتخاب کنید و فیلدها را برای بازرسی اطلاعات ثبت شده در گزارش گسترش دهید.
    شما می توانید جزئیات تماس API Vertex از جمله روش و مدل استفاده شده را مشاهده کنید. همچنین می‌توانید هویت فراخوان و مجوزهایی را که به تماس اجازه می‌دهند، ببینید.

8. تعاملات با Gen AI را ثبت کنید

شما پارامترهای درخواست API یا داده‌های پاسخ را در گزارش‌های حسابرسی پیدا نمی‌کنید. با این حال، این اطلاعات می تواند برای عیب یابی برنامه و تجزیه و تحلیل گردش کار مهم باشد. در این مرحله ما این شکاف را با اضافه کردن لاگ برنامه برطرف می کنیم. گزارش‌گیری از بسته استاندارد Go log/slog برای نوشتن گزارش‌های ساختاریافته استفاده می‌کند. بسته log/slog نمی‌داند که گزارش‌ها را در Google Cloud بنویسد. از نوشتن در خروجی استاندارد پشتیبانی می کند. با این حال، Cloud Run ویژگی هایی را دارد که اطلاعات چاپ شده در خروجی استاندارد را ضبط می کند و به طور خودکار آن را در Cloud Logging وارد می کند. برای ثبت صحیح گزارش‌های ساخت‌یافته، گزارش چاپی باید بر اساس آن قالب‌بندی شود. دستورالعمل های زیر را دنبال کنید تا قابلیت های ثبت ساختار یافته را به برنامه Go خود اضافه کنید.

  1. به پنجره (یا برگه) "Cloud Shell" در مرورگر خود بازگردید.
  2. در ترمینال، setup.go دوباره باز کنید:
    cloudshell edit ~/codelab-o11y/setup.go
    
  3. کد را با نسخه ای که لاگ را تنظیم می کند جایگزین کنید. برای جایگزینی کد، محتوای فایل را حذف کنید و سپس کد زیر را کپی کرده و در ویرایشگر قرار دهید:
    package main
    
    import (
    	"context"
    	"os"
    	"log/slog"
    	"cloud.google.com/go/compute/metadata"
    )
    
    func projectID(ctx context.Context) (string, error) {
        var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")
        if projectID == "" {
               return metadata.ProjectIDWithContext(ctx)
        }
        return projectID, nil
    }
    
    func setupLogging() {
        opts := &slog.HandlerOptions{
            Level: slog.LevelDebug,
            ReplaceAttr: func(group []string, a slog.Attr) slog.Attr {
                switch a.Key {
                case slog.LevelKey:
                    a.Key = "severity"
                    if level := a.Value.Any().(slog.Level); level == slog.LevelWarn {
                        a.Value = slog.StringValue("WARNING")
                    }
                case slog.MessageKey:
                    a.Key = "message"
                case slog.TimeKey:
                    a.Key = "timestamp"
                }
                return a
            },
        }
        jsonHandler := slog.NewJSONHandler(os.Stdout, opts)
        slog.SetDefault(slog.New(jsonHandler))
    }
    
  4. به ترمینال برگردید و main.go دوباره باز کنید:
    cloudshell edit ~/codelab-o11y/main.go
    
  5. کد برنامه را با نسخه ای جایگزین کنید که تعامل با مدل را ثبت می کند. برای جایگزینی کد، محتوای فایل را حذف کنید و سپس کد زیر را کپی کرده و در ویرایشگر قرار دهید:
    package main
    
    import (
        "context"
        "fmt"
        "net/http"
        "os"
    
        "encoding/json"
        "log/slog"
    
        "cloud.google.com/go/vertexai/genai"
    )
    
    var model *genai.GenerativeModel
    
    func main() {
        ctx := context.Background()
        projectID, err := projectID(ctx)
        if err != nil {
            return
        }
    
        setupLogging()
    
        var client *genai.Client
        client, err = genai.NewClient(ctx, projectID, "us-central1")
        if err != nil {
            slog.ErrorContext(ctx, "Failed to marshal response to JSON", slog.Any("error", err))
            os.Exit(1)
        }
        defer client.Close()
        model = client.GenerativeModel("gemini-1.5-flash-001")
        http.HandleFunc("/", Handler)
        port := os.Getenv("PORT")
        if port == "" {
            port = "8080"
        }
        if err := http.ListenAndServe(":"+port, nil); err != nil {
            slog.ErrorContext(ctx, "Failed to start the server", slog.Any("error", err))
            os.Exit(1)
        }
    }
    
    func Handler(w http.ResponseWriter, r *http.Request) {
        animal := r.URL.Query().Get("animal")
        if animal == "" {
            animal = "dog"
        }
    
        prompt := fmt.Sprintf("Give me 10 fun facts about %s. Return the results as HTML without markdown backticks.", animal)
        resp, err := model.GenerateContent(r.Context(), genai.Text(prompt))
        if err != nil {
            w.WriteHeader(http.StatusTooManyRequests)
            return
        }
    
        jsonBytes, err := json.Marshal(resp)
        if err != nil {
            slog.Error("Failed to marshal response to JSON", slog.Any("error", err))
        } else {
            slog.DebugContext(r.Context(), "content is generated", slog.String("animal", animal),
                slog.String("prompt", prompt), slog.String("response", string(jsonBytes)))
        }
    
        if len(resp.Candidates) > 0 && len(resp.Candidates[0].Content.Parts) > 0 {
            htmlContent := resp.Candidates[0].Content.Parts[0]
            w.Header().Set("Content-Type", "text/html; charset=utf-8")
            fmt.Fprint(w, htmlContent)
        }
    }
    

گزارش‌گیری طوری پیکربندی شده است که گزارش‌ها را در stdout چاپ کند، جایی که توسط عامل گزارش Cloud Run جمع‌آوری می‌شود و به طور ناهمزمان در Cloud Logging وارد می‌شود. تابع main() برای تنظیم گزارش ساختاری استاندارد Go برای استفاده از طرح JSON که از دستورالعمل‌های قالب‌بندی ساخت‌یافته پیروی می‌کند، اصلاح شده است. تمام عبارات return آن با کدی جایگزین می‌شود که گزارش‌های خطا را قبل از خروج می‌نویسد. تابع Handler() برای نوشتن گزارش ساختار یافته هنگام دریافت پاسخ از فراخوانی Vertex AI API ابزاری است. گزارش پارامتر حیوانی درخواست و اعلان و پاسخ مدل را ضبط می کند.

پس از چند ثانیه، Cloud Shell Editor تغییرات شما را به صورت خودکار ذخیره می کند.

کد برنامه Gen AI را در Cloud Run مستقر کنید

  1. در پنجره ترمینال دستور استقرار کد منبع برنامه را در Cloud Run اجرا کنید.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    اگر دستور زیر را مشاهده کردید، به شما اطلاع می دهد که این دستور یک مخزن جدید ایجاد می کند. روی 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)?
    
    فرآیند استقرار ممکن است تا چند دقیقه طول بکشد. پس از تکمیل فرآیند استقرار، خروجی هایی مانند زیر را مشاهده خواهید کرد:
    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. URL نمایش داده شده سرویس Cloud Run را در یک برگه یا پنجره جداگانه در مرورگر خود کپی کنید. همچنین، دستور زیر را در ترمینال اجرا کنید تا URL سرویس چاپ شود و در حالی که کلید Ctrl را نگه داشته اید، روی URL نشان داده شده کلیک کنید تا URL باز شود:
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    وقتی URL باز می شود، ممکن است خطای 500 دریافت کنید یا این پیام را ببینید:
    Sorry, this is just a placeholder...
    
    این بدان معنی است که سرویس ها استقرار خود را به پایان نرسانده اند. چند لحظه صبر کنید و صفحه را رفرش کنید. در پایان متنی خواهید دید که با Fun Dog Facts شروع می شود و حاوی 10 واقعیت سرگرم کننده در مورد سگ است.

برای ایجاد گزارش برنامه، URL سرویس را باز کنید. با تغییر مقدار پارامتر ?animal= صفحه را بازخوانی کنید تا نتایج متفاوتی دریافت کنید.
برای مشاهده لاگ های برنامه به صورت زیر عمل کنید:

  1. روی دکمه زیر کلیک کنید تا صفحه کاوشگر Logs در کنسول Cloud باز شود:

  2. فیلتر زیر را در قسمت Query (#2 در رابط کاربری Log explorer ) قرار دهید:
    LOG_ID("run.googleapis.com%2Fstdout") AND
    severity=DEBUG
    
  3. روی Run query کلیک کنید.

نتیجه پرس و جو گزارش‌هایی را با پاسخ سریع و Vertex AI از جمله رتبه‌بندی ایمنی نشان می‌دهد.

9. تعداد تعاملات با Gen AI

Cloud Run معیارهای مدیریت شده را می نویسد که می تواند برای نظارت بر سرویس های مستقر شده استفاده شود. معیارهای نظارت مدیریت شده توسط کاربر کنترل بیشتری بر روی داده ها و تعداد دفعات به روز رسانی متریک فراهم می کند. برای پیاده سازی چنین معیاری نیاز به نوشتن کدی است که داده ها را جمع آوری کرده و در Cloud Monitoring می نویسد. مرحله بعدی (اختیاری) را برای نحوه اجرای آن با استفاده از OpenTelemetry SDK ببینید.

این مرحله جایگزینی برای پیاده سازی متریک کاربر در معیارهای مبتنی بر گزارش کد را نشان می دهد. معیارهای مبتنی بر گزارش به شما امکان می دهد معیارهای نظارتی را از ورودی های گزارشی که برنامه شما در Cloud Logging می نویسد ایجاد کنید. ما از گزارش‌های برنامه‌ای که در مرحله قبل پیاده‌سازی کردیم برای تعریف یک متریک مبتنی بر گزارش از نوع شمارنده استفاده خواهیم کرد. این معیار تعداد تماس های موفق با Vertex API را می شمارد.

  1. به پنجره کاوشگر Logs که در مرحله قبل استفاده کردیم نگاه کنید. در زیر پنجره Query منوی کشویی Actions را پیدا کرده و روی آن کلیک کنید تا باز شود. برای پیدا کردن منو، اسکرین شات زیر را ببینید:
    نوار ابزار نتایج جستجو با منوی کشویی Actions
  2. در منوی باز شده، Create Metric را انتخاب کنید تا پنل Create log-based metric باز شود.
  3. این مراحل را برای پیکربندی یک متریک شمارنده جدید در پانل متریک ایجاد گزارش مبتنی بر گزارش دنبال کنید:
    1. نوع متریک را تنظیم کنید: شمارنده را انتخاب کنید.
    2. فیلدهای زیر را در قسمت جزئیات تنظیم کنید:
      • نام متریک گزارش : نام را روی model_interaction_count تنظیم کنید. برخی از محدودیت های نامگذاری اعمال می شود. برای جزئیات بیشتر به عیب یابی محدودیت های نامگذاری مراجعه کنید.
      • توضیحات : توضیحاتی را برای متریک وارد کنید. به عنوان مثال، Number of log entries capturing successful call to model inference.
      • واحدها : این قسمت را خالی بگذارید یا رقم 1 را وارد کنید.
    3. مقادیر را در قسمت Filter selection بگذارید. توجه داشته باشید که فیلد Build filter دارای همان فیلتری است که برای دیدن گزارش های برنامه استفاده کردیم.
    4. (اختیاری) برچسبی اضافه کنید که به شمارش تعدادی تماس برای هر حیوان کمک می کند. توجه: این برچسب پتانسیل بالایی برای افزایش کاردینالیته متریک دارد و برای استفاده در تولید توصیه نمی شود:
      1. روی افزودن برچسب کلیک کنید.
      2. فیلدهای زیر را در قسمت Labels تنظیم کنید:
        • نام برچسب : نام را روی animal قرار دهید.
        • توضیحات : توضیحات برچسب را وارد کنید. به عنوان مثال، Animal parameter .
        • نوع برچسب : STRING انتخاب کنید.
        • نام فیلد : jsonPayload.animal را تایپ کنید.
        • عبارت منظم : آن را خالی بگذارید.
      3. روی Done کلیک کنید
    5. برای ایجاد متریک روی Create Metric کلیک کنید.

همچنین می‌توانید از صفحه معیارهای Log-based Metrics مبتنی بر گزارش، با استفاده از gcloud logging metrics create دستور ایجاد CLI یا با منبع Terraform google_logging_metric ایجاد کنید.

برای تولید داده های متریک، URL سرویس را باز کنید. صفحه باز شده را چندین بار بازخوانی کنید تا چندین تماس با مدل برقرار شود. سعی کنید مانند قبل از حیوانات مختلف در پارامتر استفاده کنید.

برای جستجوی داده‌های متریک مبتنی بر گزارش، کوئری PromQL را وارد کنید. برای وارد کردن یک کوئری PromQL، موارد زیر را انجام دهید:

  1. روی دکمه زیر کلیک کنید تا صفحه Metrics explorer در کنسول Cloud باز شود:

  2. در نوار ابزار پنجره query-builder، دکمه ای را انتخاب کنید که نام آن < > MQL یا < > PromQL است. برای دیدن مکان دکمه به تصویر زیر مراجعه کنید.
    مکان دکمه MQL در Metrics Explorer
  3. بررسی کنید که PromQL در تغییر زبان انتخاب شده باشد. تغییر زبان در همان نوار ابزار قرار دارد که به شما امکان می دهد پرس و جو خود را قالب بندی کنید.
  4. درخواست خود را در ویرایشگر Queries وارد کنید:
    sum(rate(logging_googleapis_com:user_model_interaction_count{monitored_resource="cloud_run_revision"}[${__interval}]))
    
    برای اطلاعات بیشتر در مورد استفاده از PromQL، به PromQL در Cloud Monitoring مراجعه کنید.
  5. روی Run Query کلیک کنید. نمودار خطی مشابه این اسکرین شات را خواهید دید:
    نمایش سنجه های درخواست شده

    توجه داشته باشید که وقتی کلید اجرای خودکار فعال است، دکمه Run Query نشان داده نمی‌شود.

10. (اختیاری) از تله متری باز برای نظارت و ردیابی استفاده کنید

همانطور که در مرحله قبل ذکر شد، امکان پیاده‌سازی معیارها با استفاده از OpenTelemetry (Otel) SDK وجود دارد. استفاده از OTel در معماری های میکرو سرویس یک اقدام توصیه شده است. این مرحله موارد زیر را شرح می دهد:

  • راه اندازی اجزای OTel برای پشتیبانی از ردیابی و نظارت بر برنامه
  • پر کردن پیکربندی OTel با ابرداده منابع محیط Cloud Run
  • کاربرد فلاسک ابزار دقیق با قابلیت ردیابی خودکار
  • پیاده سازی یک متریک شمارنده برای نظارت بر تعدادی از تماس های مدل موفق
  • ردیابی را با گزارش های برنامه مرتبط کنید

معماری پیشنهادی برای خدمات در سطح محصول استفاده از جمع‌آورنده OTel برای جمع‌آوری و دریافت تمام داده‌های مشاهده‌پذیری برای یک یا چند سرویس است. کد در این مرحله به خاطر سادگی از کلکتور استفاده نمی کند. در عوض از صادرات OTel استفاده می کند که داده ها را مستقیماً در Google Cloud می نویسد.

اجزای OTel را برای ردیابی و نظارت متریک تنظیم کنید

  1. به پنجره (یا برگه) "Cloud Shell" در مرورگر خود بازگردید.
  2. در ترمینال، setup.go دوباره باز کنید:
    cloudshell edit ~/codelab-o11y/setup.go
    
  3. کد را با نسخه ای که ردیابی OpenTelemetry و مجموعه متریک را مقداردهی اولیه می کند جایگزین کنید. برای جایگزینی کد، محتوای فایل را حذف کنید و سپس کد زیر را کپی کرده و در ویرایشگر قرار دهید:
    package main
    
    import (
        "context"
        "errors"
        "fmt"
        "net/http"
        "os"
    
        "log/slog"
    
        "go.opentelemetry.io/contrib/detectors/gcp"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/contrib/propagators/autoprop"
        "go.opentelemetry.io/otel"
        sdkmetric "go.opentelemetry.io/otel/sdk/metric"
        "go.opentelemetry.io/otel/sdk/resource"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
        "go.opentelemetry.io/otel/trace"
    
        cloudmetric "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric"
        cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
    
        "cloud.google.com/go/compute/metadata"
    )
    
    var (
        projID string
    )
    
    func projectID(ctx context.Context) (string, error) {
        var projectID = os.Getenv("GOOGLE_CLOUD_PROJECT")
        if projectID == "" {
            return metadata.ProjectIDWithContext(ctx)
        }
        return projectID, nil
    }
    
    func setupLogging() {
        opts := &slog.HandlerOptions{
            Level: slog.LevelDebug,
            ReplaceAttr: func(group []string, a slog.Attr) slog.Attr {
                switch a.Key {
                case slog.LevelKey:
                    a.Key = "severity"
                    if level := a.Value.Any().(slog.Level); level == slog.LevelWarn {
                        a.Value = slog.StringValue("WARNING")
                    }
                case slog.MessageKey:
                    a.Key = "message"
                case slog.TimeKey:
                    a.Key = "timestamp"
                }
                return a
            },
        }
        jsonHandler := slog.NewJSONHandler(os.Stdout, opts)
        instrumentedHandler := handlerWithSpanContext(jsonHandler)
        slog.SetDefault(slog.New(instrumentedHandler))
    }
    
    type spanContextLogHandler struct {
        slog.Handler
    }
    
    func handlerWithSpanContext(handler slog.Handler) *spanContextLogHandler {
        return &spanContextLogHandler{Handler: handler}
    }
    
    func (t *spanContextLogHandler) Handle(ctx context.Context, record slog.Record) error {
        if s := trace.SpanContextFromContext(ctx); s.IsValid() {
            trace := fmt.Sprintf("projects/%s/traces/%s", projID, s.TraceID())
            record.AddAttrs(
                slog.Any("logging.googleapis.com/trace", trace),
            )
            record.AddAttrs(
                slog.Any("logging.googleapis.com/spanId", s.SpanID()),
            )
            record.AddAttrs(
                slog.Bool("logging.googleapis.com/trace_sampled", s.TraceFlags().IsSampled()),
            )
        }
        return t.Handler.Handle(ctx, record)
    }
    
    func setupTelemetry(ctx context.Context) (shutdown func(context.Context) error, err error) {
        var shutdownFuncs []func(context.Context) error
        shutdown = func(ctx context.Context) error {
            var err error
            for _, fn := range shutdownFuncs {
                err = errors.Join(err, fn(ctx))
            }
            shutdownFuncs = nil
            return err
        }
    
        projID, err = projectID(ctx)
        if err != nil {
            err = errors.Join(err, shutdown(ctx))
            return
        }
    
        res, err2 := resource.New(
            ctx,
            resource.WithDetectors(gcp.NewDetector()),
            resource.WithTelemetrySDK(),
            resource.WithAttributes(semconv.ServiceNameKey.String(os.Getenv("K_SERVICE"))),
        )
        if err2 != nil {
            err = errors.Join(err2, shutdown(ctx))
            return
        }
    
        otel.SetTextMapPropagator(autoprop.NewTextMapPropagator())
    
        texporter, err2 := cloudtrace.New(cloudtrace.WithProjectID(projID))
        if err2 != nil {
            err = errors.Join(err2, shutdown(ctx))
            return
        }
        tp := sdktrace.NewTracerProvider(
            sdktrace.WithSampler(sdktrace.AlwaysSample()),
            sdktrace.WithResource(res),
            sdktrace.WithBatcher(texporter))
        shutdownFuncs = append(shutdownFuncs, tp.Shutdown)
        otel.SetTracerProvider(tp)
    
        mexporter, err2 := cloudmetric.New(cloudmetric.WithProjectID(projID))
        if err2 != nil {
            err = errors.Join(err2, shutdown(ctx))
            return
        }
        mp := sdkmetric.NewMeterProvider(
            sdkmetric.WithReader(sdkmetric.NewPeriodicReader(mexporter)),
            sdkmetric.WithResource(res),
        )
        shutdownFuncs = append(shutdownFuncs, mp.Shutdown)
        otel.SetMeterProvider(mp)
    
        return shutdown, nil
    }
    
    func registerHttpHandler(route string, handleFn http.HandlerFunc) {
        instrumentedHandler := otelhttp.NewHandler(otelhttp.WithRouteTag(route, handleFn), route)
        http.Handle(route, instrumentedHandler)
    }
    
  4. به ترمینال برگردید و دستور زیر را برای به روز رسانی تعاریف ماژول Go در فایل go.mod اجرا کنید:
    go mod tidy
    
  5. به ترمینال برگردید و main.go دوباره باز کنید:
    cloudshell edit ~/codelab-o11y/main.go
    
  6. کد فعلی را با نسخه ای جایگزین کنید که ردیابی HTTP را ابزار می کند و متریک عملکرد را می نویسد. برای جایگزینی کد، محتوای فایل را حذف کنید و سپس کد زیر را کپی کرده و در ویرایشگر قرار دهید:
    package main
    
    import (
        "context"
        "errors"
        "fmt"
        "net/http"
        "os"
    
        "encoding/json"
        "log/slog"
    
        "cloud.google.com/go/vertexai/genai"
    
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        "go.opentelemetry.io/otel/metric"
    )
    
    var model *genai.GenerativeModel
    var counter metric.Int64Counter
    
    const scopeName = "genai-o11y/go/workshop/example"
    
    func main() {
        ctx := context.Background()
        projectID, err := projectID(ctx)
        if err != nil {
            return
        }
    
        setupLogging()
        shutdown, err := setupTelemetry(ctx)
        if err != nil {
            slog.ErrorContext(ctx, "error setting up OpenTelemetry", slog.Any("error", err))
            os.Exit(1)
        }
        meter := otel.Meter(scopeName)
        counter, err = meter.Int64Counter("model_call_counter")
        if err != nil {
            slog.ErrorContext(ctx, "error setting up OpenTelemetry", slog.Any("error", err))
            os.Exit(1)
        }
    
        var client *genai.Client
        client, err = genai.NewClient(ctx, projectID, "us-central1")
        if err != nil {
            slog.ErrorContext(ctx, "Failed to marshal response to JSON", slog.Any("error", err))
            os.Exit(1)
        }
        defer client.Close()
        model = client.GenerativeModel("gemini-1.5-flash-001")
    
        registerHttpHandler("/", Handler)
    
        port := os.Getenv("PORT")
        if port == "" {
            port = "8080"
        }
    
        if err = errors.Join(http.ListenAndServe(":"+port, nil), shutdown(ctx)); err != nil {
            slog.ErrorContext(ctx, "Failed to start the server", slog.Any("error", err))
            os.Exit(1)
        }
    }
    
    func Handler(w http.ResponseWriter, r *http.Request) {
        animal := r.URL.Query().Get("animal")
        if animal == "" {
            animal = "dog"
        }
    
        prompt := fmt.Sprintf("Give me 10 fun facts about %s. Return the results as HTML without markdown backticks.", animal)
        resp, err := model.GenerateContent(r.Context(), genai.Text(prompt))
        if err != nil {
            w.WriteHeader(http.StatusTooManyRequests)
            return
        }
        jsonBytes, err := json.Marshal(resp)
        if err != nil {
            slog.ErrorContext(r.Context(), "Failed to marshal response to JSON", slog.Any("error", err))
        } else {
            slog.DebugContext(r.Context(), "content is generated", slog.String("animal", animal),
                slog.String("prompt", prompt), slog.String("response", string(jsonBytes)))
        }
        if len(resp.Candidates) > 0 && len(resp.Candidates[0].Content.Parts) > 0 {
            clabels := []attribute.KeyValue{attribute.Key("animal").String(animal)}
            counter.Add(r.Context(), 1, metric.WithAttributes(clabels...))
            htmlContent := resp.Candidates[0].Content.Parts[0]
            w.Header().Set("Content-Type", "text/html; charset=utf-8")
            fmt.Fprint(w, htmlContent)
        }
    }
    

این برنامه اکنون از OpenTelemetry SDK برای ابزار اجرای کد با ردیابی و برای پیاده سازی شمارش تعدادی از اجرای موفق به عنوان یک معیار استفاده می کند. روش main() برای تنظیم صادرکنندگان OpenTelemetry برای ردیابی ها و معیارها برای نوشتن مستقیم به Google Cloud Tracing و Monitoring اصلاح شده است. همچنین پیکربندی های اضافی را برای پر کردن ردیابی ها و معیارهای جمع آوری شده با ابرداده های مربوط به محیط Cloud Run انجام می دهد. تابع Handler() برای افزایش شمارنده متریک هر بار که فراخوانی Vertex AI API نتایج معتبری را برمی گرداند به روز می شود.

پس از چند ثانیه، Cloud Shell Editor تغییرات شما را به صورت خودکار ذخیره می کند.

کد برنامه Gen AI را در Cloud Run مستقر کنید

  1. در پنجره ترمینال دستور استقرار کد منبع برنامه را در Cloud Run اجرا کنید.
    gcloud run deploy codelab-o11y-service \
         --source="${HOME}/codelab-o11y/" \
         --region=us-central1 \
         --allow-unauthenticated
    
    اگر دستور زیر را مشاهده کردید، به شما اطلاع می دهد که این دستور یک مخزن جدید ایجاد می کند. روی 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)?
    
    فرآیند استقرار ممکن است تا چند دقیقه طول بکشد. پس از تکمیل فرآیند استقرار، خروجی هایی مانند زیر را مشاهده خواهید کرد:
    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. URL نمایش داده شده سرویس Cloud Run را در یک برگه یا پنجره جداگانه در مرورگر خود کپی کنید. همچنین، دستور زیر را در ترمینال اجرا کنید تا URL سرویس چاپ شود و در حالی که کلید Ctrl را نگه داشته اید، روی URL نشان داده شده کلیک کنید تا URL باز شود:
    gcloud run services list \
         --format='value(URL)' \
         --filter='SERVICE:"codelab-o11y-service"'
    
    وقتی URL باز می شود، ممکن است خطای 500 دریافت کنید یا این پیام را ببینید:
    Sorry, this is just a placeholder...
    
    این بدان معنی است که سرویس ها استقرار خود را به پایان نرسانده اند. چند لحظه صبر کنید و صفحه را رفرش کنید. در پایان متنی خواهید دید که با Fun Dog Facts شروع می شود و حاوی 10 واقعیت سرگرم کننده در مورد سگ است.

برای تولید داده های تله متری، URL سرویس را باز کنید. با تغییر مقدار پارامتر ?animal= صفحه را بازخوانی کنید تا نتایج متفاوتی دریافت کنید.

ردپای برنامه را کاوش کنید

  1. روی دکمه زیر کلیک کنید تا صفحه Trace explorer در کنسول Cloud باز شود:

  2. یکی از جدیدترین ردیابی ها را انتخاب کنید. شما قرار است 5 یا 6 دهانه را ببینید که در تصویر زیر به نظر می رسد.
    نمای دامنه برنامه در Trace explorer
  3. دامنه ای را پیدا کنید که تماس را به کنترل کننده رویداد ردیابی می کند (روش fun_facts ). این آخرین بازه با نام / خواهد بود.
  4. در قسمت Trace details Logs & events را انتخاب کنید. گزارش‌های برنامه‌ای را خواهید دید که با این محدوده خاص مرتبط هستند. همبستگی با استفاده از شناسه های ردیابی و دهانه در ردیابی و در گزارش شناسایی می شود. شما باید گزارش برنامه را ببینید که دستور و پاسخ Vertex API را نوشته است.

متریک شمارنده را کاوش کنید

  1. روی دکمه زیر کلیک کنید تا صفحه Metrics explorer در کنسول Cloud باز شود:

  2. در نوار ابزار پنجره query-builder، دکمه ای را انتخاب کنید که نام آن < > MQL یا < > PromQL است. برای دیدن مکان دکمه به تصویر زیر مراجعه کنید.
    مکان دکمه MQL در Metrics Explorer
  3. بررسی کنید که PromQL در تغییر زبان انتخاب شده باشد. تغییر زبان در همان نوار ابزار قرار دارد که به شما امکان می دهد پرس و جو خود را قالب بندی کنید.
  4. درخواست خود را در ویرایشگر Queries وارد کنید:
    sum(rate(workload_googleapis_com:model_call_counter{monitored_resource="generic_task"}[${__interval}]))
    
  5. روی Run Query کلیک کنید. وقتی کلید اجرای خودکار فعال است، دکمه اجرای پرس و جو نشان داده نمی شود.

11. (اختیاری) اطلاعات حساس مبهم از سیاهههای مربوط

در مرحله 10 اطلاعاتی در مورد تعامل برنامه با مدل Gemini ثبت کردیم. این اطلاعات شامل نام حیوان، درخواست واقعی و پاسخ مدل بود. اگرچه ذخیره این اطلاعات در گزارش باید ایمن باشد، اما برای بسیاری از سناریوهای دیگر درست نیست. درخواست ممکن است شامل برخی از اطلاعات شخصی یا حساس باشد که کاربر نمی خواهد ذخیره شود. برای رفع این مشکل می‌توانید داده‌های حساسی که در Cloud Logging نوشته شده‌اند را مبهم کنید. برای به حداقل رساندن تغییرات کد راه حل زیر توصیه می شود.

  1. یک موضوع PubSub برای ذخیره ورودی های گزارش ورودی ایجاد کنید
  2. یک سینک گزارش ایجاد کنید که گزارش‌های دریافت شده را به موضوع PubSub هدایت می‌کند.
  3. یک خط لوله Dataflow ایجاد کنید که گزارش‌های هدایت‌شده به موضوع PubSub را به دنبال این مراحل تغییر می‌دهد:
    1. یک ورودی گزارش از موضوع PubSub را بخوانید
    2. بار ورودی ورودی را برای اطلاعات حساس با استفاده از API بازرسی DLP بررسی کنید
    3. با استفاده از یکی از روش های ویرایش DLP، اطلاعات حساس موجود در محموله را ویرایش کنید
    4. ورودی log مبهم را در Cloud Logging بنویسید
  4. خط لوله را مستقر کنید

12. (اختیاری) پاکسازی کنید

برای جلوگیری از خطر تحمیل هزینه برای منابع و APIهای مورد استفاده در Codelab، توصیه می شود پس از اتمام آزمایشگاه پاکسازی کنید. ساده ترین راه برای حذف صورتحساب، حذف پروژه ای است که برای Codelab ایجاد کرده اید.

  1. برای حذف پروژه دستور delete project را در ترمینال اجرا کنید:
    PROJECT_ID=$(gcloud config get-value project)
    gcloud projects delete ${PROJECT_ID} --quiet
    
    با حذف پروژه Cloud، صورت‌حساب تمام منابع و APIهای مورد استفاده در آن پروژه متوقف می‌شود. باید این پیام را ببینید که در آن PROJECT_ID شناسه پروژه شما خواهد بود:
    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. (اختیاری) اگر خطایی دریافت کردید، با مرحله 5 مشورت کنید تا شناسه پروژه ای را که در طول آزمایشگاه استفاده کرده اید پیدا کنید. آن را با دستور در دستورالعمل اول جایگزین کنید. به عنوان مثال، اگر ID پروژه شما lab-example-project باشد، دستور به صورت زیر خواهد بود:
    gcloud projects delete lab-project-id-example --quiet
    

13. تبریک می گویم

در این آزمایشگاه، یک اپلیکیشن Gen AI ایجاد کردید که از مدل Gemini برای پیش‌بینی استفاده می‌کند. و برنامه را با قابلیت های ضروری نظارت و گزارش گیری مجهز کرد. شما برنامه را مستقر کرده اید و از کد منبع به Cloud Run تغییر می دهید. سپس شما محصولات Google Cloud Observability را دنبال می‌کنید تا عملکرد برنامه را ردیابی کنید، بنابراین می‌توانید از قابلیت اطمینان برنامه اطمینان حاصل کنید.

اگر می‌خواهید برای بهبود محصولاتی که امروز با آن‌ها کار می‌کنید، در یک مطالعه تحقیقاتی تجربه کاربری (UX) شرکت کنید، اینجا ثبت‌نام کنید .

در اینجا چند گزینه برای ادامه یادگیری وجود دارد: