Skip to content

广告混排

广告混排:六种混排器、默认 multi_risk、品牌安全五档裁定、三类相邻检查与丢弃原因记录

Updated

这一页回答什么

本页回答四个问题:混排器怎样把广告插进 For You 信息流;广告插在哪个位置;系统怎样计算帖子的品牌安全裁定;混排器在哪些情况下丢弃广告。

核心结论

  1. 混排发生在外层选择器。BlenderSelector 按 AdsBlenderType 参数从六种混排器中选一种(home-mixer/selectors/blender_selector.rs:61-79)。旧版只有 safe_gap 与 partition_organic 两种。
  2. 开源默认值是 multi_risk(home-mixer/params/param.rs:957-962)。参数取未知值时退回 PartitionOrganicAdsBlender(blender_selector.rs:78)。
  3. 品牌安全裁定从四档变成五档,新增 HighRisk(home-mixer/models/brand_safety.rs:7-14)。中风险与高风险帖子都不能紧挨广告(home-mixer/ads/util.rs:24-29)。
  4. 广告数取三个上限中的最小值:请求的广告数;按广告间距算出的容量;可作邻居的安全帖数的一半。帖子不足 5 条时,混排器不插广告(ads/util.rs:9)。
  5. 混排器为每条被丢弃的广告记录原因:帖子太少、超出间距容量、没有合格邻居、低风险邻居、排除账号、排除关键词、被截断。混排器把原因写入 query.ad_drops(blender_selector.rs:80-82、ads/drops.rs:41-89)。

在哪里发生

外层 ForYouCandidatePipeline 的 BlenderSelector 先把 FeedItem 按类型分桶,再选混排器:

// home-mixer/selectors/blender_selector.rs:61-81(节选)
let blender: &dyn AdsBlender = match query.params.get(AdsBlenderType).as_str() {
    "partition_organic_low_risk" => &self.partition_organic_blender,
    "safe_gap" => &self.safe_gap_blender,
    "multi_risk" => &self.multi_risk_blender,
    "slot_spread" => &self.slot_spread_blender,
    "least_movement" => &self.least_movement_blender,
    "time_gap" => { /* 用 AdsTimeGap* 参数构造 TimeGapAdsBlender */ }
    _ => &self.partition_organic_blender,
};
let mut blended = blender.blend(posts, ads, &mut drops);
query.ad_drops.extend(drops);

Prompt、Who to Follow、视频轮播、PushToHome 等模块在混排之后插入。详见 候选选择与成型。

AdsBlender trait(ads/mod.rs:28-52)采用模板方法模式:blend() 先记录帖子品牌安全裁定与广告风险等级的分布,再调用实现类的 blend_inner_with_drops()。

时序“关注”页使用另一个混排器 FollowingAdBlender。FollowingBlenderSelector 调用它(selectors/following_blender_selector.rs:36-37)。它以会话单元为单位插入广告(ads/following_ad_blender.rs:76-96)。本页只讲 For You。

品牌安全裁定

内层流水线在选后水合阶段计算裁定,只计算入选候选。AdsBrandSafetyVfHydrator 读取帖子的安全标签,再调用 compute_verdict(candidate_hydrators/ads_brand_safety_vf_hydrator.rs:110-118)。安全标签客户端的超时为 500ms,每批最多查询 100 条(server.rs:831-838)。

BrandSafetyVerdict 五档(models/brand_safety.rs:7-14):

档位 值 对广告的含义
Unspecified 0 未裁定
Safe 1 可作广告邻居
LowRisk 2 可作邻居,但低容忍广告拒绝
MediumRisk 3 不能作普通广告的邻居
HighRisk 4 不能作任何广告的邻居

compute_verdict()(brand_safety.rs:103-131)按顺序判定:

flowchart TB
    A[帖子安全标签] --> H{"含高风险标签?"}
    H -->|是| HR[HighRisk]
    H -->|否| M{"含中风险标签?"}
    M -->|是| MR[MediumRisk]
    M -->|否| G{"有 GROK_SFA 或<br/>GROK_NSFA_LIMITED?"}
    G -->|否| MR
    G -->|是| P{"要求 PTOS 复审<br/>且未通过?"}
    P -->|是| MR
    P -->|否| L{"含低风险标签?"}
    L -->|是| LR[LowRisk]
    L -->|否| SF[Safe]
  • 高风险标签 6 个:NSFW_HIGH_PRECISION、NSFW_HIGH_RECALL、GORE_AND_VIOLENCE_HIGH_PRECISION、PDNA、EGREGIOUS_NSFW、SOFT_NSFW(brand_safety.rs:73-80)。
  • 中风险标签 10 个:如 NSFA_HIGH_PRECISION、DO_NOT_AMPLIFY、MALICIOUS_URL、NSFA_COMMUNITY_NOTE、GROK_NSFA、NSFW_TEXT(brand_safety.rs:82-93)。
  • 低风险标签 3 个:NSFA_LIMITED_INVENTORY、GROK_NSFA_LIMITED、NSFA_HIGH_RECALL(brand_safety.rs:95-99)。
  • PTOS 复审:tweet_id ≥ PTOS_CUTOFF_TWEET_ID(2_054_275_414_225_846_272)且没有 PTOS_REVIEWED 标签即不通过(brand_safety.rs:101、:190-192)。雪花 ID 随时间递增。因此这条规则等价于“截止时刻之后发布的帖子须经复审”。

作者兜底是新增机制。EnableAdsAuthorBrandSafetyFallback 默认 true(param.rs:971-976)。帖子没有任何 Grok 标签时,水合器按原帖作者拉取作者级品牌安全标签,把它当作帖子的 Grok 标签,再计算一次裁定(ads_brand_safety_vf_hydrator.rs:28-58、:136-148)。启用作者兜底时,水合器关闭 PTOS 复审要求(:111)。

EnableAdsBrandSafetyVerdictV2 默认 false(param.rs:964-969)。打开后,带 V2 版 Grok 标签的帖子用 compute_verdict_v2 的标签表,其余仍走 V1(brand_safety.rs:158-188)。

帖子转成 ScoredPost 时,系统把缺失的裁定视为 MediumRisk(home-mixer/scored_posts_server.rs:134-137)。

三类相邻检查

所有混排器共用 ads/util.rs 的检查函数。检查函数从广告的 ad_adjacency_control 字段读取相邻控制规则。

检查 触发条件 丢弃原因
low_risk_block 广告风险容忍为 BsrLow 或 BsrIas,且上下邻居有一条 LowRisk(util.rs:112-128) LowRiskNeighbour
handle_block 邻居的作者、被转发作者、被引用作者或任一祖先作者在广告的排除账号列表里(util.rs:130-149) ExcludedHandle
keyword_block 广告的排除关键词经分词后出现在邻居正文中(util.rs:151-174) ExcludedKeyword

旧版 handle_block 只检查帖子作者。新版还检查被转发作者、被引用作者与祖先作者。

默认策略:MultiRiskAdsBlender

multi_risk 把帖子分为安全(含 LowRisk)、中风险、高风险三桶,用“帖-广告-帖”三元组插广告(ads/multi_risk_blender.rs:38-260)。

flowchart TB
    A[帖子序列] --> B["分桶:safe / medium / high"]
    B --> C["safe_budget = min(广告数, 间距容量, safe 数 / 2)"]
    C --> D{"广告是否 BsrHigh?"}
    D -->|"是,且有中风险帖对"| E["用两条中风险帖夹住<br/>只做账号与关键词检查"]
    D -->|否| F["从 safe 分组取上下两条<br/>做三类相邻检查"]
    E --> T[三元组]
    F -->|通过| T
    F -->|命中| X["丢弃并记录原因"]
    T --> G["三元组与剩余帖子交错<br/>剩余帖子按分数降序"]
    G --> H["截断到 35 项,末项是广告则去掉"]
  1. 前置条件:没有广告或帖子少于 5 条时,只返回帖子,广告记 TooFewPosts(:46-57)。
  2. 间距容量:spacing_cap = (n − 1) / spacing.requested(:59-63)。compute_spacing 取前 4 条广告 insert_position 的最小正差;差值不小于 3 时采用,否则用默认间距 3(util.rs:54-75)。
  3. 安全帖名额:safe_budget = min(广告数, spacing_cap, safe 帖数 / 2)(:65-73)。每个广告占用两条安全帖作邻居。
  4. 高容忍广告的额外通道:风险容忍为 BsrHigh 的广告可以放在两条中风险帖之间,名额是中风险帖数的一半(:127-151)。这条通道只做账号与关键词检查。
  5. 普通通道:从安全帖第 组号 × group_size 条开始取上下两条,依次做低风险、账号、关键词检查。命中任一检查时,混排器丢弃该广告,下一条广告再尝试同一组(:153-180)。
  6. 交错:剩余的安全、中风险、高风险帖按分数降序作填充,均分到各三元组之后(:211-252)。
  7. 收尾:截断到 RESULT_SIZE 35 项(广告也计数)。末项是广告时去掉该广告。被截掉的广告记为 Truncated(:254、drops.rs:76-89)。

高风险帖不作广告邻居,但作为填充帖出现在信息流里。

其他策略

参数值 混排器 做法
partition_organic_low_risk 及未知值 PartitionOrganicAdsBlender 旧默认策略。has_avoid(中风险或高风险)的帖子进不安全桶,广告数上限 min(广告数, 间距容量, 安全帖数 / 2),三类检查后组成三元组(partition_organic_blender.rs:21-200)。没有高容忍广告通道
safe_gap SafeGapAdsBlender 找两侧都不需回避的间隙,按理想间距把广告分配到最近的合格间隙(safe_gap_blender.rs:25-107、util.rs:39-52)
slot_spread SlotSpreadAdsBlender 先按间距把广告槽均匀铺开,再给每个槽找合格邻居(slot_spread_blender.rs:45-114)
least_movement LeastMovementAdsBlender 先筛出至少有两条兼容邻居的广告,再求一种让帖子位置移动最少的布局;首个广告位的偏移代价权重 50,其余为 2(least_movement_blender.rs:13-14、:46-128)
time_gap TimeGapAdsBlender 用模型预测的停留时长衡量广告间隔:每条帖子的停留时长截到 [0.5t, 2t],t 默认 4.0 秒;两条广告之间的帖子停留时长合计须达到 t × 间距,且至少 3 条帖子(time_gap_blender.rs:19-35、:193-246、param.rs:977-1000)

丢弃原因与统计

DroppedAd 记录广告 ID、丢弃原因、阻挡它的帖子 ID、命中的账号或关键词(ads/drops.rs:41-74)。选择阶段把这些记录写入 query.ad_drops。外层的广告注入日志 side effect 负责上报(for_you_candidate_pipeline.rs:241-260)。

各混排器还上报三类指标(ads/health_metrics.rs):enforcement(三类检查各丢了多少)、slot_outcome(提供的槽位中填了多少)、serving_limitation(广告数受哪个上限约束)。

设计决策

决策 理由
混排放在外层选择器 帖子已排好序,混排是成品组装
六种混排器由参数切换 可灰度比较不同混排策略;未知值退回旧默认,配置写错也能出广告
品牌安全只对入选候选计算 标签查询有成本,落选候选不需要
无 Grok 标签时默认中风险 未经内容理解的帖子保守处理;作者兜底减少新帖被误判的数量
高容忍广告可夹在中风险帖之间 广告主接受更高风险时,多一条投放通道
丢弃原因逐条记录 广告未投放时可追溯到具体帖子、账号或关键词

FAQ

Q:为什么帖子不足 5 条就不插广告? A:MIN_POSTS_FOR_ADS = 5(ads/util.rs:9)。帖子太少时插入广告,会让广告占比过高。6 种 For You 混排器都把这个常量传给混排函数(例如 ads/multi_risk_blender.rs:25、ads/partition_organic_blender.rs:17)。

Q:广告会不会把帖子挤出信息流? A:会。multi_risk 截断到 35 项时广告也计数(multi_risk_blender.rs:254)。被挤出的帖子计入 non_selected(blender_selector.rs:95-100)。

源码锚点

  • home-mixer/selectors/blender_selector.rs:61-82 —— 混排器选择
  • home-mixer/ads/mod.rs:28-52 —— AdsBlender trait
  • home-mixer/ads/util.rs:9-75 —— 帖数下限、间距、回避判定
  • home-mixer/ads/util.rs:112-174 —— 三类相邻检查
  • home-mixer/ads/multi_risk_blender.rs:38-260 —— 默认混排策略
  • home-mixer/models/brand_safety.rs:7-199 —— 裁定档位与判定逻辑
  • home-mixer/candidate_hydrators/ads_brand_safety_vf_hydrator.rs:28-148 —— 裁定计算与作者兜底

相关页面

Type to search…

↑↓ navigate↵ selectEsc close