这一页回答什么
本页说明 home-mixer 的 VMRanker 打分器调用的 vm-ranker gRPC 重排服务。内容包括该服务的输入参数、计算流程、基于帖子多模态向量的行列式点过程(Determinantal Point Processes,简称 DPP)多样性筛选机制、配置参数,以及调用失败时的多级降级策略。
加权求和、作者多样性衰减和站外降权的具体公式详见 打分与排序。本页侧重说明各项算法在服务内部的调用机制与执行逻辑。
核心结论
- vm-ranker 是独立的 gRPC 服务:服务仅暴露
rank与get_carousel_model两个 RPC 接口(vm-ranker/ranker_service.rs:70-173)。home-mixer 在每次 For You 推荐请求中均调用一次rank接口。 - 服务先计算价值模型得分,再执行 DPP 筛选:价值模型综合分由加权求和、作者探索奖励、新作者扶持以及作者多样性衰减与站外降权构成。所有算法逻辑均调用共享 crate
xai-value-model(vm-ranker/scoring/value_model.rs:159-219)。 - 权重参数在服务内部按用户上下文动态解析:服务定期从配置中心同步特性开关(feature switch)。服务根据请求中携带的用户上下文(国家、语言、账号年龄等)为当前用户解析适用的参数组合(
vm-ranker/ranking_config.rs:55-61)。 - DPP 仅负责候选子集筛选,不修改入选帖子的得分与相对排序:算法从得分最高的前
max_selected_rank条候选帖子中,依据“高得分且彼此低相似度”准则贪心挑选最多top_k条帖子。入选候选保留原始得分,未入选候选得分置为 0(vm-ranker/scoring/dpp_model.rs:196-206;vm-ranker/dpp.rs:155-169)。 - 相似度基于帖子多模态向量的余弦相似度计算:向量数据来源于 Phoenix 的
xai-recsys-mm-server客户端,采用 1024 维 f16 格式。针对转发帖子,服务查询原帖标识符。当缓存缺失向量时,服务生成随机单位向量替代(vm-ranker/scoring/dpp_model.rs:43-51、80-84)。 - DPP 在开源配置中的默认参数:
theta = 0.65、max_selected_rank = 150(vm-ranker/params.rs:253-259);top_k = 50取自服务启动参数(vm-ranker/args.rs:18-19)。话题请求默认跳过 DPP 筛选(params.rs:247-252)。 - 系统提供三级异常降级保障:服务并发满载时直接拒绝请求;缺少配置数据时透传 home-mixer 的输入得分;home-mixer 远程调用失败时采用本地加权分与新作者扶持结果,此时跳过多样性衰减、站外降权和 DPP 筛选。
在系统中的位置
flowchart LR
subgraph HM["home-mixer"]
VMS["VMRanker 打分器<br/>本地加权分 + 新作者扶持"]
end
subgraph VR["vm-ranker 服务"]
RS["VMRankerServiceImpl<br/>信号量限流"]
CFG["RankingConfig<br/>按观看者解析参数"]
VAL["value_model::compute<br/>xai-value-model"]
DPP["dpp_model + dpp<br/>贪心 DPP 筛选"]
EMB[("EmbeddingStore<br/>帖子多模态 embedding")]
end
GIT[("配置仓库<br/>feature switch 文件")] -->|"git 同步 30 秒"| CFG
VMS -->|"RankRequest"| RS
RS --> VAL
CFG --> VAL
VAL --> DPP
EMB --> DPP
DPP -->|"RankResponse"| VMS
调用方 VMRanker 的请求组装和降级逻辑详见 打分与排序 的“VMRanker 打分器与兜底”小节。
调用方式
请求内容
home-mixer 的 RequestShape::build() 负责组装 RankRequest(home-mixer/scorers/vm_ranker_request.rs:27-76):
| 字段 | 内容 |
|---|---|
viewer_id / viewer |
观看者 id 与上下文(国家、语言、客户端、角色、now_ms、feature switch 覆盖等),供服务解析参数 |
candidates[] |
每个候选的 tweet_id、author_id、retweeted_tweet_id、本地分 score、in_network、is_reply、is_retweet、is_mutual_follow_author、cold_start_lift_to_rank、视频时长、全部 Phoenix 预测、缓存的 weighted_score、semantic_ids |
value_model_id |
固定为 "dpp"(vm_ranker_request.rs:8) |
compute_value_model |
固定为 true,让服务端重算价值模型分 |
topic_request |
是否话题请求,影响站外系数和 DPP 开关 |
include_top_heads |
Under the Hood 请求时为真,服务返回每条帖子贡献最大的头 |
experiment_payload |
可选的实验载荷:用户行为序列(去偏用)、内容特征、话题历史等 |
home-mixer 请求默认不设置 dpp_params 与 seed_tweet_id,两项字段在服务中采用默认空值。
客户端与超时
home-mixer 设定客户端超时时间为 400 毫秒(home-mixer/clients/vm_ranker_client.rs:21)。目标集群由 VMRankerClusterId 配置,默认值为 "Experiment1"(home-mixer/params/param.rs:560-565)。xDS 寻址与 DNS 寻址通道的切换机制见调用方页面说明。
服务入口
方法 VMRankerServiceImpl::rank()(vm-ranker/ranker_service.rs:72-149)按以下三步执行:
- 并发限流:服务通过
try_acquire获取信号量许可。许可耗尽时立即返回RESOURCE_EXHAUSTED错误,不进入排队(ranker_service.rs:85-96)。最大并发许可数max_concurrent_requests默认值为 256(vm-ranker/args.rs:12-13)。 - 执行打分:组装包含 DPP 上下文、去偏模型与配置信息的
RankContext,随后调用scoring::rank()(ranker_service.rs:111-129)。 - 返回结果:向调用方返回各候选帖子的
score、weighted_score以及可选的top_heads贡献列表。
服务支持 gzip 与 zstd 格式压缩,单条 gRPC 消息大小上限为 20 MB(ranker_service.rs:59-67)。
参数来源与同步
当配置项 config_sync_enabled 为真时,服务在启动阶段从配置仓库拉取特性开关文件,随后每隔 config_sync_interval_secs(默认 30 秒)自动同步一次(vm-ranker/main.rs:69-98;vm-ranker/args.rs:48-61)。同步流程通过 git fetch 与 git reset --hard 执行(vm-ranker/config_sync.rs:99-125)。
每次接收到推荐请求时,RankingConfig::resolve() 利用用户上下文构建受众标识符(recipient),匹配实验规则并叠加请求自带的 fs_overrides 覆盖参数(vm-ranker/ranking_config.rs:55-61)。受众信息包含根据用户标识符 snowflake 时间戳推导出的账号年龄(天数、分钟数)与回流时间等特征字段(ranking_config.rs:70-103)。
文件 vm-ranker/params.rs 中定义的数值为代码默认值,仅在配置文件缺失对应键时生效。该文件与 home-mixer/params/param.rs 使用相同的配置键名,但部分代码默认值存在差异,具体对照见 打分与排序 的权重表。
rank() 的执行流程
flowchart TD
A["RankRequest"] --> B["resolve_params<br/>按观看者解析参数"]
B --> C{"DPP 上下文存在<br/>且 dpp_enabled?"}
C -- 否 --> D["value_model::compute"]
D --> E["返回价值模型分"]
C -- 是 --> F["spawn_blocking"]
F --> G["value_model::compute"]
G --> H["dpp_model::rank_with_memo<br/>贪心选子集"]
H --> I["入选保留原分<br/>未入选置 0"]
上述流程对应 vm-ranker/scoring/mod.rs:40-94。DPP 筛选属于纯 CPU 密集型核矩阵运算,因此服务将其派发至 spawn_blocking 专用线程池执行。
函数 dpp_enabled() 判定是否启用 DPP(scoring/mod.rs:96-98):当未解析到配置参数(例如未开启配置同步)时默认判定为开启;解析到参数时,要求 DppEnabled 为真,且必须是非话题请求或配置项 TopicDppEnabled 为真。参数解析成功后,配置中的 theta 与 max_selected_rank 会覆盖启动参数,启动参数设定的 top_k 保持不变(scoring/mod.rs:55-62)。
价值模型分
方法 value_model::compute()(vm-ranker/scoring/value_model.rs:48-70)按以下逻辑计算:
- 当请求字段
compute_value_model为假且参数ComputeValueModel同样为假时,服务直接透传请求自带的score。 - 当未成功解析到配置参数时,服务记录
fallback监控指标并同样透传输入得分。 - 其余情况调用
compute_or_fallback()计算得分(value_model.rs:159-219):weights_from_params()根据参数构建权重,并通过WeightPerturbationSigma执行针对用户的扰动(value_model.rs:72-121);scoring_context()选取适用的站外降权系数(value_model.rs:123-147);- 根据作者实验分组注入作者探索奖励(
value_model.rs:243-262); - 调用
xai_value_model::compute_value_scores()计算加权分与最终得分; - 当启用去偏逻辑时,服务通过用户去偏模型校准各行为概率并重新计算得分(
value_model.rs:196-218)。
当所有候选帖子均包含缓存加权分且调用方未请求 top heads 时,服务直接返回首轮结果,跳过去偏计算(value_model.rs:180-194)。
用户去偏(默认关闭)
配置项 UserDebiasEnabled 默认值为 false(vm-ranker/params.rs:260-265)。开启该功能后,服务加载针对用户行为序列训练的轻量模型,估计当前用户在各项行为上的个人基线 logit,比对全局基线并将 Phoenix 输出概率向全局均值修正(vm-ranker/scoring/debias.rs:125-152、221-233)。校准强度由参数 UserDebiasAlpha(默认值为 0.0)与截断阈值 UserDebiasClipLogit(默认值为 2.0)控制(params.rs:266-277)。当模型权重文件超过 UserDebiasMaxArtifactAgeSecs(3600 秒)未更新时,服务跳过去偏步骤(params.rs:296-301)。去偏模型的权重存储路径与训练代码未包含在开源仓库中。
DPP 多样性筛选
输入数据
函数 build_dpp_inputs()(vm-ranker/scoring/dpp_model.rs:54-100)按以下步骤准备数据:
- 按照价值模型得分降序排列候选列表,截取前
max_selected_rank条候选(dpp_model.rs:78)。 - 查询各帖子的向量表示:转发帖子使用被转发的原帖标识符,其余帖子使用自身标识符(
dpp_model.rs:80-84)。 - 向量查询缺失时,生成随机单位向量并记录
embedding_missing标记(dpp_model.rs:43-51、23-37)。
向量数据由 EmbeddingStore 提供,底层调用 xai_recsys_mm_server 的 MmEmbeddingsClient(vm-ranker/embedding_store.rs:19-22)。客户端在宿主机共享内存中维护按帖子标识符索引的 f16 向量缓存。服务启动时从对象存储拉取快照,并在内存中保留最近 2 天的向量条目(phoenix/crates/serving/xai-recsys-mm-server/src/mm_embedding_client.rs:24-30)。在向量快照预加载完成前,服务保持未就绪状态(vm-ranker/main.rs:143-151)。向量的生成流程详见 多模态帖子嵌入器。
核矩阵构建
函数 dpp::rescore()(vm-ranker/dpp.rs:35-221)构建 L 核矩阵:
s_max = 池内最高分
q_i = exp(α · s_i / s_max), α = θ / (2·(1 − θ))
L_ii = q_i²
L_ij = q_i · q_j · cos(e_i, e_j)上述公式实现于 dpp.rs:92-133。参数 θ 被限制在 [0, 1) 区间内。θ 越大,α 越大,分数差异在子集选择中的权重越高;θ 越小,算法越倾向于挑选语义正交的内容。在开源默认配置下 θ = 0.65,对应 α ≈ 0.929,此时得分最高的候选帖子的质量项 q 约为零分候选帖子的 2.53 倍。
贪心求解
函数 greedy_dpp()(vm-ranker/dpp.rs:223-314)基于增量 Cholesky 分解执行贪心最大后验概率(MAP)推断:
- 优先选取对角线元素
L_ii最大的候选帖子(即质量分最高项;若请求包含种子帖子,则固定优先纳入种子)。 - 在每轮迭代中,为剩余候选计算加入当前集合后的行列式增益(条件方差),选取增益最大的候选帖子。
- 当增益低于
1e-6(表明候选与已选集合高度线性相关)或选满top_k条候选时终止迭代(dpp.rs:288-292)。
候选帖子与已选帖子的向量相似度越高,其条件方差越小,入选概率随之降低。该算法实现了兼顾高得分与内容差异性的目标。
输出结果
筛选出的候选集合按原始价值模型得分重新排序(dpp.rs:155-169)。函数 rank_with_memo() 保持入选帖子的原始分数值,未入选帖子的得分全部置为 0(dpp_model.rs:196-206)。DPP 筛选不重排入选帖子的相对顺序,主要将语义高相似度的低分候选排挤出展示队列。分数为 0 的候选帖子将在下游选择阶段被排至队列末尾。
种子帖子(seed_tweet_id)仅用于压制与其语义相似的候选,本身不进入输出列表,亦不占用 top_k 筛选配额(dpp_model.rs:102-125)。home-mixer 发起的 For You 推荐请求不传递种子帖子。
参数配置
| 参数 | 开源默认值 | 来源 |
|---|---|---|
DppEnabled |
true |
vm-ranker/params.rs:241-246 |
TopicDppEnabled |
false |
vm-ranker/params.rs:247-252 |
DppTheta |
0.65 | vm-ranker/params.rs:253 |
DppMaxSelectedRank |
150 | vm-ranker/params.rs:254-259 |
--dpp-enabled(启动参数) |
false |
vm-ranker/args.rs:15-16 |
--dpp-top-k |
50 | vm-ranker/args.rs:18-19 |
--dpp-theta |
0.5 | vm-ranker/args.rs:21-22 |
--dpp-max-selected-rank |
100 | vm-ranker/args.rs:24-25 |
--embedding-dim |
1024 | vm-ranker/args.rs:27-28 |
DPP 生效要求同时满足两个前提条件:启动参数 --dpp-enabled 处于开启状态(否则服务不初始化 DPP 上下文,main.rs:31-52),且请求级参数 DppEnabled 为真。开源仓库未包含生产环境的部署启动参数。当特性开关解析成功时,theta 与 max_selected_rank 采用配置值(0.65 与 150),top_k 维持启动参数配置(50)。
失败与兜底
| 失败点 | 结果 | 位置 |
|---|---|---|
| 服务并发已满 | 返回 RESOURCE_EXHAUSTED;home-mixer 用本地分 |
vm-ranker/ranker_service.rs:85-96 |
| 未启用配置同步或未带观看者上下文 | 价值模型直通,返回 home-mixer 带来的本地分 | vm-ranker/scoring/value_model.rs:159-165、vm-ranker/scoring/mod.rs:126-134 |
| 帖子 embedding 缺失 | 用随机单位向量参与 DPP,近似视为与其他帖不相似 | vm-ranker/scoring/dpp_model.rs:43-51 |
| DPP 的阻塞任务异常 | rank 返回错误;home-mixer 用本地分 |
vm-ranker/scoring/mod.rs:65-79 |
| home-mixer 调用失败或超时 | 用本地加权分 + 新作者扶持;无多样性、站外降权、DPP | home-mixer/scorers/vm_ranker.rs:107-113 |
| 响应缺少某个候选 | 该候选保留本地分 | home-mixer/scorers/vm_ranker.rs:122-139 |
本地得分未包含作者多样性衰减和站外降权调整。因此在服务降级期间,推荐流中同一作者的帖子密度相对上升,站外帖子的排序位次相对靠前。该现象依据源码逻辑推导得出,开源仓库未提供生产环境的实测数据。
视频轮播模型
RPC 接口 get_carousel_model 根据名称返回 JSON 格式的模型配置文件。文件从 --carousel-model-dir 目录读取,服务每隔 60 秒自动重新加载(vm-ranker/ranker_service.rs:151-172;vm-ranker/carousel_model.rs:10-40;vm-ranker/args.rs:45-46)。未配置该目录时服务返回 UNAVAILABLE 状态码。开源仓库未包含模型具体内容与调用端细节的详细说明。
设计决策
| 决策 | 理由 |
|---|---|
| 价值模型分在服务端按观看者重算 | 实验参数按用户解析,可在不改 home-mixer 的情况下调权重 |
| DPP 只筛选、不改分 | 保留价值模型对入选帖的排序,DPP 只负责把相似内容挤出前列 |
| 用多模态 embedding 余弦衡量相似 | 能识别文字不同但内容相近的帖子(同一张图、同一段视频),作者多样性做不到这一点 |
| 缺 embedding 时用随机向量 | 不让缺数据的帖子整体出局;随机向量与其他向量近似正交 |
| 话题请求默认不做 DPP | 话题页本来就要同类内容,按相似度去重会伤害相关性 |
try_acquire 立即拒绝 |
调用方有本地兜底,排队等待只会拉长尾延迟 |
| embedding 预加载完才就绪 | 冷启动时缺 embedding 会让 DPP 退化成随机筛选 |
源码锚点
vm-ranker/main.rs:31-151—— 启动:DPP 上下文、去偏、配置同步、就绪门控vm-ranker/args.rs:12-64—— 启动参数默认值vm-ranker/params.rs:235-301—— 价值模型、DPP、去偏参数vm-ranker/ranker_service.rs:72-149——rankRPC 入口与限流vm-ranker/ranking_config.rs:55-103—— 按观看者解析参数vm-ranker/scoring/mod.rs:40-134——rank()流程与 DPP 开关vm-ranker/scoring/value_model.rs:48-219—— 价值模型计算与直通vm-ranker/scoring/dpp_model.rs:43-206—— DPP 输入、embedding 查询与输出vm-ranker/dpp.rs:35-314—— 核矩阵与贪心 DPPhome-mixer/scorers/vm_ranker_request.rs:27-76—— 调用方请求组装
相关页面
- 打分与排序 —— 权重表、加权求和、多样性与站外降权公式、调用方兜底
- 多模态帖子嵌入器 —— DPP 所用帖子 embedding 的来源
- 候选选择与成型 —— vm-ranker 之后的选择阶段
- Phoenix 排序模型 —— vm-ranker 输入里各行为预测的来源
- home-mixer 编排层 ——
VMRanker打分器所在的流水线