TURTLEDOVE RTB Simulation API

כחלק מארגז החול לפרטיות, הציע Chrome את TURTLEDOVE – ממשק API בדפדפן שנועד לאפשר למפרסמים ולחברות טכנולוגיות פרסום להציג מודעות המטורגטות לקבוצות עניין, בלי להסתמך על קובצי cookie של צד שלישי. כך המשתמשים לא יכולים לעקוב אחרי אתרים שונים. לפני ש-Chrome מטמיע את TURTLEDOVE, צוות RTB של Google מציע סימולציה של TURTLEDOVE בצד השרת, שמאפשרת לשותפי RTB (שותפים מורשים ושותפים של Open Bidding) להתנסות בממשק ה-API. באמצעות הסימולציה, שותפים ו-Google יכולים ללמוד על היעילות של תהליך בסגנון TURTLEDOVE, ליצור משוב רלוונטי על שיפורים פוטנציאליים ב-API בפורומים ציבוריים ולהקל על המעבר לתמיכה בפרסום מותאם אישית, בלי להסתמך על קובצי cookie של צד שלישי.

בשלב הראשון של הסימולציה, מגישי הצעות המחיר מארחים חברות בקבוצות של תחומי עניין. בנוסף, מגישי הצעות המחיר מספקים ל-Google פונקציות של בידינג מראש באמצעות API. בזמן ההצגה, Google מפצלת כל בקשה להצעת מחיר בהיקף הסימולציה לשתי בקשות: בקשה לפי הקשר ובקשה מבוססת-עניין. הבקשה לפי הקשר תהיה דומה לבקשה הנוכחית להצעת מחיר, אבל ללא מזהי משתמשים זמינים (כמו שדות google_user_id,hosted_match_data). בקשות לקבוצות של תחומי עניין לא מכילות מידע הקשרי, והן מכילות רק שדות שמאפשרים לזהות את המשתמשים (בכפוף לאמצעי הבקרה הקיימים על פרטיות). הצעות מחיר לקבוצות תחומי עניין לא מכילות ערך של עלות לאלף חשיפות, אלא מפנות לפונקציית בידינג ש-Google מפעילה בצד השרת בסביבה שבארגז חול (sandbox). המכרז בדרך כלל פועל עם הצעות מחיר לפי הקשר והצעות מחיר שמחושבות על ידי פונקציות של הצעות מחיר.

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

זרימה

תרשים זרימה

  1. הצוות של RTB Bidder עובד עם מפרסמים כדי ליצור, לתחזק ולארח קבוצות של תחומי עניין עבור כל מפרסם וחברות של משתמשים בקבוצות העניין האלה.
  2. כשהמשתמש מבקר בדף אינטרנט של בעל תוכן דיגיטלי, הדפדפן של המשתמש יוריד את תג המודעה של Google. הדפדפן של המשתמש מבקש מודעה מפלטפורמת Google Publisher.
  3. RTB Bidder מספק ל-Google פונקציית בידינג אחת או יותר (כפונקציות JavaScript) לפני הזמן. (למידע נוסף: פונקציות של בידינג).
  4. עבור החלק הקטן של הבקשות המשויכות לניסוי, Google שולחת בקשות להצעות מחיר לפי הקשר ובקשות להצעות מחיר לפי הקשר לכל מגיש הצעת מחיר שמשתתף בניסוי (למידע נוסף: בקשות להצעת מחיר).
  5. מגיש הצעת המחיר משתמש במזהה המשתמש אנונימי שניתן בבקשה להצעת מחיר של קבוצת בעלי העניין, וממפה אותו לקבוצות העניין המתאימות. מגיש הצעת המחיר ב-RTB יחזיר תגובות להצעות מחיר לפי הקשר ותגובות להצעות מחיר המבוססות על תחומי עניין (למידע נוסף, עיינו בקטע: תגובות להצעות מחיר).
    1. התגובות לפי הצעת מחיר לפי הקשר יהיו דומות לתגובת הצעת המחיר בימינו, ברמת אפס של הצעות מחיר או יותר לפי הקשר. בנוסף, הצעת המחיר לפי הקשר תציב אותות מותאמים אישית של מגישי הצעות מחיר, שיסופקו לפונקציה של הצעת המחיר כקלט.
    2. בתגובות להצעת מחיר המבוססות על תחומי עניין לא תצוין הצעת מחיר, אלא השם של פונקציית הבידינג. Google תפעיל את פונקציית הבידינג כדי לקבל הצעת מחיר.
  6. Google מפעילה מכרז בצד השרת עם המועמדים הבאים:

    1. הצעות מחיר שהתקבלו מהתגובה לפי הקשר.
    2. הצעות המחיר שמתקבלות מהתגובה של קבוצת בעלי העניין ומתומחרות לפי הצעות המחיר על ידי הפעלת הפונקציות של הבידינג.
    3. הצעות מחיר רגילות ממגישי הצעות מחיר אחרים.

    חשוב לזכור שזו מכרז זהה לזה של היום.

  7. Google מחזירה מודעה מנצחת לדפדפן של המשתמש, שמוצגת באופן רגיל.

פונקציות בידינג

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

ממשק לפונקציות של בידינג

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

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

/**
 * Returns a bid price CPM for a given ad candidate.
 *
 * @param {Object} inputs an object with the
 *                 following named fields:
 *                   - openrtbContextualBidRequest or googleContextualBidRequest
 *                   - customContextualSignal
 *                   - interestBasedBidData
 */
function biddingFunction(inputs) {
  ...
  return inputs.interestBasedBidData.cpm
      * inputs.customContextualSignals.placementMultiplier;
}

השדות בעלי השם בארגומנט אובייקט inputs כוללים (עשויים להשתנות עם התקדמות הניסוי):

openrtbContextualBidRequest (אובייקט JS) הבקשה להצעת מחיר לפי הקשר בפרוטוקול OpenRTB. מגישי הצעות מחיר שמשתמשים בפרוטוקול RTB של Authorized Buyers צריכים להתעלם מהקלט הזה ולא להשתמש בפונקציות שלהם לבידינג.
googleContextualBidRequest (JS Object) הבקשה להצעת מחיר לפי הקשר בפרוטוקול Google Authorized Buyers. מגישי הצעות מחיר שמשתמשים בפרוטוקול OpenRTB צריכים להתעלם מהקלט הזה ולא להשתמש בו בפונקציות של הצעות המחיר.
customContextualSignal (JS Object) הנתונים המותאמים אישית שסופקו על ידי מגיש הצעות המחיר בתגובה להצעת המחיר לפי הקשר. מגיש הצעות המחיר קובע את הפורמט.
interestBasedBidData (אובייקט JS) נתונים מותאמים אישית שסופקו על ידי מגיש הצעת המחיר בתגובה להצעת המחיר של קבוצת בעלי העניין. מגיש הצעות המחיר קובע את הפורמט.

ניהול פונקציות של בידינג דרך API

משאב ה-API הניסיוני הזה מאפשר למגישי הצעות המחיר להעלות פונקציות של בידינג ל-Google ולנהל את הפונקציות האלה.

נקודת קצה של שירות בסיס: https://realtimebidding.googleapis.com

משאב: פונקציית בידינג

{
  "name": string,
  "biddingFunction": string
}

השדה name מייצג את השם של פונקציית הבידינג. הפורמט צריך להיות: bidders/{bidderAccountId}/biddingFunctions/{biddingFunctionName}, שבו biddingFunctionName ייבחר על ידי מגיש הצעת המחיר.

השדה biddingFunction הוא קוד המקור של JavaScript לפונקציית הבידינג, והוא עומד בדרישות הבאות:

דוגמה:

{
  "name": "bidders/1234567678/biddingFunctions/my_bidding_function_name",
  "biddingFunction": "(function(inputs) {return 1.23;})"
}

יצירת פונקציית בידינג

פונקציית מתן הצעות תהיה זמינה לשימוש בתגובות בקבוצת תחומי העניין תוך שעה בערך לאחר קריאה מוצלחת ל-CreateBiddingFunction API.

POST https://realtimebidding.googleapis.com/v1alpha/{parent=bidders/*}/biddingFunctions
פרמטרים של נתיב
parent מחרוזת בפורמט bidders/{bidderAccountId}
Body: פונקציה של בידינג ליצירת
{
  "name": "bidders/1234567678/biddingFunctions/my_bidding_function_name",
  "biddingFunction": "(function(inputs) {return 1.23;})"
}
תשובה (פונקציית בידינג)
{
  "name": "bidders/1234567678/biddingFunctions/my_bidding_function_name",
  "biddingFunction": "(function(inputs) {return 1.23;};)"
}

הצגת רשימה של פונקציות קיימות לבידינג

GET https://realtimebidding.googleapis.com/v1alpha/bidders/{bidderAccountId}/biddingFunctions
פרמטרים של נתיב
parent מחרוזת בפורמט bidders/{bidderAccountId}.
פרמטרים של שאילתה
pageToken אסימון מחרוזת, המזהה דף תוצאות שהשרת צריך להחזיר. הערך הזה מתקבל מתגובת שיחה קודמת של ListBiddingFunctions, אם התוצאות לא תואמות בדף אחד.
תשובה
{
  "biddingFunctions": [
    {
      object (BiddingFunction)
    }
  ],
  "nextPageToken": string
}
שיחה לדוגמה
GET https://realtimebidding.googleapis.com/v1alpha/bidders/123456789/biddingFunctions

הדמיה של שינויים בפרוטוקול RTB של TURTLEDOVE

פרוטוקול RTB של Authorized Buyers

בקשות להצעת מחיר

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

// All fields will be filled unless otherwise specified.
message BidRequest {
  // Fields below would not be populated in the experiment
  optional string google_user_id = ...;
  optional uint32 cookie_version = ...;
  optional int32 cookie_age_seconds = ...;
  optional bytes hosted_match_data = ...;
  optional string session_id = ...;

  // Contextual fields below will be populated
  optional string publisher_id = ...;
  optional string url = ...;
  ...
  message Mobile {
    // Device advertising identifiers below would not be populated
    // in the contextual requests in the experiment
    optional bytes encrypted_advertising_id = ...;
    optional bytes advertising_id = ...;
    ...
    optional bytes encrypted_hashed_idfa = ...;
    optional bytes hashed_idfa = ...;
    ...
  }
  ...
  message AdSlot {
    message MatchingAdData {
      repeated int64 billing_id = ...;
      ...
    }
    ...
  }
  repeated AdSlot adslot = ...;
  ...
}

למשל:

{
  id: "_\321\326\000\n\301\207\n\323\n\227",
  ip: "S\030\347",
  user_agent: "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36",
  publisher_country: "RU",
  geo_criteria_id: 9061060,
   adslot: [{
    id: 1,
    ad_block_key: 3613182520,
    width:   [240,200,120],
    height:   [400,200,240],
    matching_ad_data:   [{
      billing_id:     [923487589],
      minimum_cpm_micros: 850000
    }]
  }]
}

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

// Most fields would not be populated in the experiment unless otherwise specified.
message BidRequest {
   // Will be provided, subject to the existing privacy controls.
  optional string google_user_id = ...;
  optional uint32 cookie_version = ...;
  optional int32 cookie_age_seconds = ...;
  optional bytes hosted_match_data = ...;

  message AdSlot {
    // Will be filled.
    repeated int32 width = ...;
    repeated int32 height = ...;
    optional ConsentedProvidersSettings consented_providers_settings = ...;
    optional bool regs_gdpr = ...;
    optional bool regs_lgpd = ...;

    message MatchingAdData {
      // Will be filled.
      repeated int64 billing_id = ...;
      ...
    }
    ...
  }
  repeated AdSlot adslot = ...;
  ...
}

למשל:

id: "_\322\207\000\003\320\n\031\177C\307\215\035"
adslot {
  id: 1
  width: 1200
  height: 60
  consented_providers_settings {
    consented_providers: 292
    tcf_consent_string: "CO-eDrRO-eDrREkAAAAAAAAeYwf95y3p-wzhheMCY70-vv__7v3ff_3g"
  }
  regs_gdpr: true
  matching_ad_data {
    billing_id: 9833784997
  }
}
google_user_id: "ABCDEF1"
hosted_match_data: "ABCABCABC2"

תשובות להצעות מחיר

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

message BidResponse {
  message Ad {
    message AdSlot {
      required int64 max_cpm_micros = ...;
      ...
    }
    repeated AdSlot adslot = ...;
    ...
  }
  repeated Ad ad = ...;
  // Contains contextual signals that will be passed to the bidding function.
  // This can be any JSON value. For example:
  // {"foo": "bar", "base": [1, 2, 3]}
  optional google.protobuf.Value custom_contextual_signal = ...;

  ...
}

לדוגמה,

ad {
  html_snippet: "<iframe src=\"http://example.com/something" width=\"300\" height=\"250\" scrolling=\"no\" frameBorder=\"0\" ></iframe>"
  adslot {
    id: 1
    max_cpm_micros: 100000
    billing_id: 1234567890
  }
  click_through_url: "https://www.example.com.pl"
  attribute: 47
  buyer_creative_id: "FFI399F3HI9HFH"
  width: 300
  height: 250
  impression_tracking_url: "http://example.com/impression"
}
custom_contextual_signal {
 struct_value {
   fields {
     name: "string_data_name"
     value {
       string_value: "string_value_1"
     }
   }
   fields {
     name: "bool_data_name"
     value {
       bool_value: true
     }
   }
 }
}
processing_time_ms: 1

הצעת מחיר המבוססת על קבוצת בעלי עניין

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

// All fields should be filled by a bidder as discussed in
// https://developers.google.com/authorized-buyers/rtb/response-guide
// unless otherwise specified.
message BidResponse {
  // Ad HTML code that will be rendered normally upon winning.
  optional string html_snippet = ...;

  message Ad {
    message AdSlot {
      // Should not be populated for interest group-based bids.
      required int64 max_cpm_micros = ...;
      ...
    }
    repeated AdSlot adslot = ...;

    // Will be filled.
    // This is the bidding function name that references a bidding function
    // that is provided ahead of time through Bidding functions API resource.
    optional string bidding_function_name = ...;

    // Contains interest group-related data that will be passed
    // to the bidding function. This can be any JSON value.
    optional google.protobuf.Value interest_group_data = ...;
    ...
  }
  repeated Ad ad = ...;
  ...
}

למשל:

ad {
  html_snippet: "<iframe src=\"http://example.com/something" width=\"300\" height=\"250\" scrolling=\"no\" frameBorder=\"0\" ></iframe>"
  adslot {
    id: 1
    max_cpm_micros: 0
    billing_id: 1234567890
    bidding_function_name: "bidders/123/biddingFunctions/my_bidding_function_1"
    interest_group_data {
      struct_value {
        fields {
          name: "string_data_name"
          value {
            string_value: "string_value_1"
          }
        }
        fields {
          name: "bool_data_name"
          value {
            bool_value: true
          }
        }
      }
    }
  }
  click_through_url: "https://www.example.com.pl"
  attribute: 47
  buyer_creative_id: "FFI399F3HI9HFH"
  width: 300
  height: 250
  impression_tracking_url: "http://example.com/impression"
}
processing_time_ms: 1

נתוני OpenRTB

בקשות להצעת מחיר

בקשה לפי הקשר (ב-JSON)
// All fields will be filled unless otherwise specified.
{
  // Fields below would not be populated in the experiment
  "user": {...}

  "device": {
    // Fields below would not be populated in the experiment
    "ifa": ...
    "dpidsha1": ...
    "dpidmd5": ...


    // Other fields will not be affected by the experiment
    ...
  }

  // Other fields will not be affected by the experiment
  ...
}
בקשה המבוססת על תחומי עניין (ב-JSON)
// Most fields would not be populated in the experiment unless otherwise specified.
{
  // Will be provided, subject to the existing privacy controls.
  "user": {
    "id": "BFEUKH3"
    "buyeruid": "FEI3F3I29"
    "ext": {
      "consented_providers": [ 292 ]
      "tcf_consent_string": "CO-eDrRO-eDrREkAAilsbO2dYGD9Pn8HT3ZCY70-vv__7v3ff_3g"
    }
  }

  "imp": {
    // Will be provided, subject to the existing privacy controls.
    "banner": {
      "w": ...
      "h": ...
    }

    "ext": {
      // Will be provided, subject to the existing privacy controls.
      "billing_id": [...]

      // Other fields will not be provided by the experiment
      ...
    }
  }
}

תשובות להצעות מחיר

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

// All fields should be filled by a bidder as discussed in
// https://developers.google.com/authorized-buyers/rtb/response-guide
// unless otherwise specified.
{
  ...
  "seatbid": [{
     "bid": [...],
     ...
  }],
  ...
  "ext": {
    // Contains contextual signals that will be passed to the bidding function.
    // This signal can be any JSON blob. For example:
    // {"foo", "bar", "base": [1, 2, 3]}
    "custom_contextual_signal": ...
  }
}

תגובה להצעת מחיר מבוססת-קבוצה

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

// All fields should be filled by a bidder as discussed in
// https://developers.google.com/authorized-buyers/rtb/response-guide
// unless otherwise specified.
{
  "bid": [{
       "id": ...
       ...
       "ext": {
         // This is the bidding function name that references a bidding function
         // that is provided ahead of time through Bidding functions API resource.
         "bidding_function_name": ...

         // Contains interest group related data that will be passed to the
         // bidding function.
         // This signal can be any JSON blob. For example:
         // {"foo", "bar", "base": [1, 2, 3]}
         "interest_group_data": ...
       }
    }
    ...
  ]
}