TURTLEDOVE RTB 模擬 API

Privacy Sandbox 中,Chrome 提出了 TURTLEDOVE 這個瀏覽器 API,目的是讓廣告客戶和廣告技術公司不必使用第三方 Cookie,就能顯示按照興趣顯示的廣告目標,以免使用者受到跨網站追蹤。在 Chrome 導入 TURTLEDOVE 之前,Google RTB 小組提供了伺服器端 TURTLEDOVE API 模擬功能,允許其即時出價合作夥伴 (Authorized Buyers 和公開出價合作夥伴) 使用 API 進行實驗。透過這項模擬,合作夥伴和 Google 就能瞭解 TURTLEDOVE 樣式流程的效率,並在公開論壇提供潛在 API 改善項目的相關意見回饋,並簡化轉換程序,讓不依賴第三方 Cookie 來支援個人化廣告。

在模擬的第一階段,出價方會代管興趣群組成員。此外,出價方也能透過 API 事先向 Google 提供出價函式。在執行時,Google 會將模擬要求中的每個出價要求分成兩個要求:內容相關要求和興趣導向要求。內容比對要求與目前的出價要求類似,但不含使用者 ID (例如 google_user_idhosted_match_data 欄位)。興趣群組要求不包含任何情境資訊,而且只包含使用者識別欄位 (取決於現有的隱私權控制項)。興趣出價競價通常會以內容比對出價和出價函式計算的出價運作。

在模擬的第一階段,Google 會根據興趣要求傳送匿名的使用者 ID。目的是將兩邊的導入作業降到最低,以便更快開始實驗。在興趣導向要求中,使用者 ID 的存在將受到現有的隱私權保護和控制項規範 (例如使用者選擇停用個人化廣告時)。

資料流

流程圖

  1. RTB 出價工具會與廣告客戶合作,為各個廣告客戶群組和這些興趣群組的成員建立、維護及代管興趣群組。
  2. 當使用者造訪某個出版商網頁時,使用者的瀏覽器就會下載 Google 的廣告代碼。使用者的瀏覽器向 Google 發布商平台請求廣告。
  3. 即時出價工具會提前向 Google 提供一或多個出價函式 (做為 JavaScript 函式)。(請參閱「出價函式」一節)。
  4. 對於實驗範圍內的一小部分要求,Google 會針對參與實驗的每個出價方分別傳送內容比對出價要求按照興趣顯示的出價要求 (請參閱「出價要求」一節)。
  5. 出價工具會使用興趣群組出價要求中的匿名使用者 ID,並將其對應至相應的興趣群組。即時出價工具會傳回內容比對出價回應按照興趣出價回應 (請參閱「出價回應」一節)。
    1. 內容比對出價回應與今天的出價回應類似,出價為零或內容比對出價。除此之外,內容比對出價回應也可納入要提供給出價函式輸入出價工具的自訂比對內容訊號。
    2. 按照興趣顯示的出價回應不會指定出價,而是指定出價函式的名稱。Google 會執行出價函式來取得出價。
  6. Google 會使用下列候選伺服器進行伺服器端競價:

    1. 來自內容回應的出價。
    2. 由出價群組回應,透過執行出價函式計算得出的出價價格。
    3. 來自其他出價方的一般出價。

    請注意,這與今天的競價相同。

  7. Google 會將勝出的廣告傳回給使用者的瀏覽器,並正常顯示。

出價函式

出價函式是出價方提供的函式,可根據特定興趣群組 (亦即「按照興趣顯示的廣告」) 傳回出價值。在原始的 TURTLEDOVE 提案中,此函式會儲存在使用者瀏覽器中。在此模擬過程中,出價工具會在放送廣告前,使用 API 將出價函式上傳至 Google。廣告放送期間,「興趣群組」回應將會指定出價函式名稱。Google 會在沙箱環境中執行該出價函式以取得出價值。 此興趣出價將使用與內容比對相同的競價。

出價功能介面

出價函式應為 JavaScript 中導入的純副作用免費函式,該函式將在廣告交易平台提供的沙箱中執行。出價函式無法存取網路、儲存空間或其他形式的 I/O,也無法在不同的出價要求之間保留叫用之間的任何狀態。出價函式會根據情境資料和興趣群組資料組合,透過演算法計算並傳回特定廣告候選出價的出價千次曝光出價。

出價函式必須接受並使用空白物件做為輸入參數,且在一次性初始化期間,會向出價函式提供空白輸入。在初始化時,系統會建立並呼叫出價函式的快照數次,以允許 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 通訊協定中的內容比對出價要求。使用 Authorized Buyers RTB 通訊協定的出價方應忽略這些輸入內容,且不應在出價函式中使用。
googleContextualBidRequest (JS 物件) Google Authorized Buyers 通訊協定中的內容比對出價要求。使用 OpenRTB 通訊協定的出價方應忽略此輸入內容,且不應在其出價函式中使用。
customContextualSignal (JS 物件) 出價工具在內容出價回應中提供的自訂資料。出價工具會決定格式。
interestBasedBidData (JS 物件) 出價方在興趣群組出價回應中提供的自訂資料。出價工具會決定格式。

透過 API 管理出價函式

這個實驗性 API 資源可讓出價方將出價函式上傳到 Google 並管理這些函式。

基本服務端點:https://realtimebidding.googleapis.com

資源:出價函式

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

name 欄位代表出價函式的名稱,格式如下:bidders/{bidderAccountId}/biddingFunctions/{biddingFunctionName},其中 biddingFunctionName 是由出價方選擇。

biddingFunction 欄位是出價函式的 JavaScript 原始碼,且符合下列規定:

  • 大小小於 5 MiB。
  • 遵守出價函式介面的規定。
  • 在沙箱初始建立期間,必須支援執行,沒有任何輸入。

例子:

{
  "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}
內文:要建立的出價函式
{
  "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

TURTLEDOVE 模擬即時出價通訊協定異動

Authorized Buyers 即時出價通訊協定

出價要求

實驗中的內容比對出價要求看起來與傳統的出價要求相同,但會遮蓋匿名的使用者 ID。

// 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
    }]
  }]
}

實驗中的興趣導向要求會包含傳統的匿名使用者 ID,但這類 ID 必須符合所有現有的隱私權保護措施和控制項,但不含內容資訊 (網頁網址、發布商 ID 等)。

// 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": ...
       }
    }
    ...
  ]
}