ChatGPT投放广告。以下是完整的归因循环
Source: Hacker News
Source: …
广告投放机制
OpenAI 的广告平台由两部分组成。 在 ChatGPT 端,后端在模型响应时向对话 SSE 流注入结构化的 single_advertiser_ad_unit 对象。 在商家端,一个名为 OAIQ 的跟踪 SDK 在访客浏览器中运行,并将产品浏览信息回报给 OpenAI。 两者通过 Fernet 加密的点击令牌(每个广告四个)关联在一起。
我在一支已获同意的移动流量研究车队上捕获了这两部分。以下内容全部来源于观察到的流量。
当你向 ChatGPT 发送消息时,后端会在 https://chatgpt.com/backend-api/f/conversation 打开一个 SSE 响应。该流中的大多数事件是模型输出;部分事件是广告单元。它们的形式如下:
event: delta
data: {
"type": "single_advertiser_ad_unit",
"ads_request_id": "069e89b3-c038-7764-8000-6e5a193e5f69",
"ads_spam_integrity_payload": "gAAAAABp6Js_",
"preamble": "",
"advertiser_brand": {
"name": "Grubhub",
"url": "www.grubhub.com",
"favicon_url": "https://bzrcdn.openai.com/cabfae7ead26b03d.png",
"id": "adacct_6984ed0ba55481a29894bb192f7773b4"
},
"carousel_cards": [{
"title": "Get Chinese Food Delivered",
"body": "Satisfy Your Cravings with Grubhub Delivery.",
"image_url": "https://bzrcdn.openai.com/cabfae7ead26b03d.png",
"target": {
"type": "url",
"value": "https://www.grubhub.com/?utm_source=chatgptpilot&utm_medium=paid&utm_campaign=diner_gh_search_chatgpt_kw_traffic_nb_x_nat_x&utm_content=nbchinese&oppref=gAAAA&olref=gAAAA",
"open_externally": false
},
"ad_data_token": "eyJwYXlsb2"
}]
}
注意事项
single_advertiser_ad_unit是一种已定义的模式;其命名暗示了同类的其他类型(例如 multi‑advertiser)。advertiser_brand.id采用adacct_前缀的形式——这是每个商家账户的稳定标识符。- 品牌的 favicon 与广告图片均从
bzrcdn.openai.com加载。OpenAI 托管广告创意,而非商家自行托管。 target.open_externally: false表示链接将在 ChatGPT 的内置网页视图中打开,从而使 OpenAI 能在任何像素信号之上观察点击后的导航行为。- 每条广告包含四个 Fernet 令牌:
ads_spam_integrity_payload、oppref、olref以及经过 base64 包装的ad_data_token。它们均使用服务器专用密钥进行 AES‑128‑CBC 加密,并通过 HMAC‑SHA256 确保完整性。
广告如何被选中
面板中的单个账户在六次对话、六个不同主题中收到了六个不同的广告。投放是基于聊天内容进行上下文定位的:
| 对话主题 | 投放的广告商 |
|---|---|
| 北京旅行计划(长城,故宫) | Grubhub – “获取中餐外送” |
| 北京旅游预订 | GetYourGuide – 长城之旅 (ad_id=beijing003) |
| 北京航班 | Axel – utm_term=vflight_beijing_03 |
| NBA季后赛 | Gametime – utm_campaign=nba&utm_content=playoffs |
| 春季时尚/潮流 | Aritzia – utm_campaign=chatgptpilot_trav3 |
| 生产力 / 幻灯片 | Canva – utm_campaign=…link-clicks_products |
同一个账户,不同的话题,不同的品牌。我没有找到证据表明投放是否会考虑之前的对话历史。
四令牌归因链
每个广告都会携带四个不同的 Fernet 加密块。它们的作用取决于出现的位置:
ads_spam_integrity_payload– 在 SSE 数据中发送,绝不会出现在点击 URL 中。用于服务器端对伪造广告点击的完整性检查。oppref– 出现在点击 URL 上,并被 OAIQ 像素原样复制到 cookie__oppref(TTL 720 小时 ≈ 30 天)。前向归因令牌;随每一次后续的商家像素事件一起传递。olref– 与oppref一起出现在点击 URL 上,但我们观察到的 SDK 并未存储它。可能用于 OpenAI 服务器上的展示侧/外链引用日志记录。ad_data_token– 包含另一个 Fernet 令牌的 base64 包装 JSON。随 SSE 负载携带,推测在点击时由服务器端进行对账。
Fernet 的前九个字节是公开的:版本字节 0x80 加上一个 8 字节的大端序 Unix 时间戳。因此可以在不使用 OpenAI 密钥的情况下恢复这些令牌的生成时间:
import base64, struct, datetime
b = base64.urlsafe_b64decode("gAAAAABp7fdA" + "==")
print(datetime.datetime.utcfromtimestamp(struct.unpack(">Q", b[1:9])[0]))
# → 2026-04-26 11:30:08 UTC
我捕获的 Home Depot 点击 URL 的生成时间是 11:30:08;浏览器在 11:31:43 获取了商家页面。点击延迟: 95 秒。
如何在商户端关闭循环
用户点击卡片。浏览器会打开类似以下的 URL:
https://www.grubhub.com/?utm_source=chatgptpilot&...&oppref=gAAAA&olref=gAAAA
商户页面加载 OAIQ SDK:
<script src="https://bzrcdn.openai.com/oaiq.min.js"></script>
<script>
oaiq('init', { pid: '' });
oaiq('measure', 'contents_viewed', { /* … */ });
</script>
oaiq.min.js 的版本为 0.1.3。init 时它会从 window.location 中读取 ?oppref=,并将其写入第一方 cookie __oppref(TTL 为 720 小时),同时设置探测 cookie __oaiq_domain_probe。随后每一次 measure 调用都会向以下地址 POST JSON:
POST https://bzr.openai.com/v1/sdk/events?pid=&st=oaiq-web&sv=0.1.3
如果想要阻止 ChatGPT 广告事件,请将以下两个域名加入过滤列表:
bzrcdn.openai.com、bzr.openai.com。
在任何 ChatGPT 推荐的点击之后,请检查以下两个 cookie 名称:__oppref、__oaiq_domain_probe。