TURTLEDOVE RTB Simulation API

作为 Privacy Sandbox 的一部分,Chrome 提出了 TURTLEDOVE,这是一种浏览器内 API,旨在让广告客户和广告技术公司在不依赖第三方 Cookie 的情况下展示针对用户兴趣的广告,从而保护用户免受跨网站跟踪。在 Chrome 实现 TURTLEDOVE 之前,Google 实时出价团队将提供服务器端 TURTLEDOVE API 模拟,允许其实时出价合作伙伴(Authorized Buyers 和公开出价合作伙伴)试用该 API。通过模拟,合作伙伴和 Google 可以了解 TURTLEDOVE 式流程的效果,获得关于公共论坛中潜在 API 改进的反馈,以及简化过渡到支持个性化广告而不依赖于第三方 Cookie 的流程。

在模拟过程的第一阶段,出价方会托管兴趣组成员资格。出价方还通过 API 提前向 Google 提供出价功能。在投放时,Google 会将模拟范围内的每个出价请求拆分成两个请求:内容相关请求和针对用户兴趣的请求。内容相关请求将与当前出价请求类似,但没有用户标识符(例如 google_user_idhosted_match_data 字段)。兴趣组请求不包含任何上下文信息,仅包含用户身份识别字段(取决于现有的隐私控制)。兴趣组出价不包含 CPM 值,而是引用 Google 在沙盒化环境中运行的服务器端的出价函数。该竞价将与内容相关出价和由出价函数计算得出的出价正常运行。

在模拟的第一阶段,Google 会在针对用户兴趣的请求上发送假名化的用户 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;})"
}

创建出价函数

出价函数将在成功调用 BidBiddingFunction 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 实时出价协议

出价请求

实验中的内容相关出价请求看起来与常规出价请求相同,但会隐藏假名化的用户标识符。

// 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 等)。

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