|
@@ -18,8 +18,9 @@ import type { RecallSignals, VideoMatchEnrichedVO } from '../api/types'
|
|
|
* 公式 (MATERIAL):
|
|
* 公式 (MATERIAL):
|
|
|
* sim_norm = clip((sim - simThreshold) / (1 - simThreshold), 0, 1)
|
|
* sim_norm = clip((sim - simThreshold) / (1 - simThreshold), 0, 1)
|
|
|
* qualityScore = (wCtr × ctr + wViral × viral + wRoi × roi) / qualTotalW
|
|
* qualityScore = (wCtr × ctr + wViral × viral + wRoi × roi) / qualTotalW
|
|
|
- * composite = alpha × sim_norm + (1 - alpha) × qualityScore
|
|
|
|
|
|
|
+ * composite = alpha × boost × sim_norm + (1 - alpha) × qualityScore
|
|
|
* 质量缺失时按 materialMissingStrategy 处理:"group"(分组)或 "shrink"(收缩)
|
|
* 质量缺失时按 materialMissingStrategy 处理:"group"(分组)或 "shrink"(收缩)
|
|
|
|
|
+ * boost 仅作用于相关性分
|
|
|
*/
|
|
*/
|
|
|
export interface RankingParams {
|
|
export interface RankingParams {
|
|
|
simThreshold: number
|
|
simThreshold: number
|
|
@@ -138,7 +139,7 @@ export function computeCompositeScore(
|
|
|
// 素材模态:多维质量加权
|
|
// 素材模态:多维质量加权
|
|
|
if (item.modality === 'MATERIAL') {
|
|
if (item.modality === 'MATERIAL') {
|
|
|
const quality = item.signals?.quality ?? materialQualityFromDetail(item)
|
|
const quality = item.signals?.quality ?? materialQualityFromDetail(item)
|
|
|
- return rankMaterial(simNorm, lowerBound, passesThreshold, quality, params)
|
|
|
|
|
|
|
+ return rankMaterial(simNorm, lowerBound, passesThreshold, quality, item, params)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// VIDEO / ARTICLE 模态:ROV 公式
|
|
// VIDEO / ARTICLE 模态:ROV 公式
|
|
@@ -150,29 +151,35 @@ function rankMaterial(
|
|
|
lowerBound: number,
|
|
lowerBound: number,
|
|
|
passesThreshold: boolean,
|
|
passesThreshold: boolean,
|
|
|
quality: RecallSignals['quality'] | undefined,
|
|
quality: RecallSignals['quality'] | undefined,
|
|
|
|
|
+ item: VideoMatchEnrichedVO,
|
|
|
params: RankingParams,
|
|
params: RankingParams,
|
|
|
): ScoreBreakdown {
|
|
): ScoreBreakdown {
|
|
|
const alpha = params.alpha
|
|
const alpha = params.alpha
|
|
|
|
|
|
|
|
|
|
+ // boost 仅作用于相关性分,质量分不加 boost
|
|
|
|
|
+ const codeBoost = item.configCode
|
|
|
|
|
+ ? (params.boostsByCode?.[item.configCode] ?? getDefaultBoostForCode(item.configCode))
|
|
|
|
|
+ : params.deconstructBoost
|
|
|
|
|
+
|
|
|
if (quality == null || !quality.hasData) {
|
|
if (quality == null || !quality.hasData) {
|
|
|
// group(默认):无质量数据,仅依赖相关性
|
|
// group(默认):无质量数据,仅依赖相关性
|
|
|
if (params.materialMissingStrategy === 'group') {
|
|
if (params.materialMissingStrategy === 'group') {
|
|
|
return {
|
|
return {
|
|
|
- composite: alpha * simNorm,
|
|
|
|
|
|
|
+ composite: alpha * codeBoost * simNorm,
|
|
|
simNorm,
|
|
simNorm,
|
|
|
rovNorm: 0,
|
|
rovNorm: 0,
|
|
|
- boost: 1,
|
|
|
|
|
|
|
+ boost: codeBoost,
|
|
|
lowerBound,
|
|
lowerBound,
|
|
|
passesThreshold,
|
|
passesThreshold,
|
|
|
qualityMissing: true,
|
|
qualityMissing: true,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- // shrink: 无先验均值时退化为 alpha × simNorm
|
|
|
|
|
|
|
+ // shrink: 无先验均值时退化为 alpha × boost × simNorm
|
|
|
return {
|
|
return {
|
|
|
- composite: alpha * simNorm,
|
|
|
|
|
|
|
+ composite: alpha * codeBoost * simNorm,
|
|
|
simNorm,
|
|
simNorm,
|
|
|
rovNorm: 0,
|
|
rovNorm: 0,
|
|
|
- boost: 1,
|
|
|
|
|
|
|
+ boost: codeBoost,
|
|
|
lowerBound,
|
|
lowerBound,
|
|
|
passesThreshold,
|
|
passesThreshold,
|
|
|
}
|
|
}
|
|
@@ -183,12 +190,12 @@ function rankMaterial(
|
|
|
const roi = quality.roi ?? 0
|
|
const roi = quality.roi ?? 0
|
|
|
const qualTotalW = params.wCtr + params.wViral + params.wRoi || 1
|
|
const qualTotalW = params.wCtr + params.wViral + params.wRoi || 1
|
|
|
const weightedQuality = (params.wCtr * ctr + params.wViral * viral + params.wRoi * roi) / qualTotalW
|
|
const weightedQuality = (params.wCtr * ctr + params.wViral * viral + params.wRoi * roi) / qualTotalW
|
|
|
- const composite = alpha * simNorm + (1 - alpha) * weightedQuality
|
|
|
|
|
|
|
+ const composite = alpha * codeBoost * simNorm + (1 - alpha) * weightedQuality
|
|
|
return {
|
|
return {
|
|
|
composite,
|
|
composite,
|
|
|
simNorm,
|
|
simNorm,
|
|
|
rovNorm: 0,
|
|
rovNorm: 0,
|
|
|
- boost: 1,
|
|
|
|
|
|
|
+ boost: codeBoost,
|
|
|
lowerBound,
|
|
lowerBound,
|
|
|
passesThreshold,
|
|
passesThreshold,
|
|
|
weightedQuality,
|
|
weightedQuality,
|
|
@@ -212,12 +219,8 @@ function rankVideoArticle(
|
|
|
const hasRov = rov != null && Number.isFinite(rov)
|
|
const hasRov = rov != null && Number.isFinite(rov)
|
|
|
|
|
|
|
|
if (!hasRov) {
|
|
if (!hasRov) {
|
|
|
- if (item.modality === 'VIDEO') {
|
|
|
|
|
- const composite = codeBoost * params.alpha * simNorm
|
|
|
|
|
- return { composite, simNorm, rovNorm: 0, boost: codeBoost, lowerBound, passesThreshold }
|
|
|
|
|
- }
|
|
|
|
|
- const composite = simNorm
|
|
|
|
|
- return { composite, simNorm, rovNorm: 0, boost: 1, lowerBound, passesThreshold }
|
|
|
|
|
|
|
+ const composite = codeBoost * params.alpha * simNorm
|
|
|
|
|
+ return { composite, simNorm, rovNorm: 0, boost: codeBoost, lowerBound, passesThreshold }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const rovDenom = params.rovClipHigh - params.rovClipLow
|
|
const rovDenom = params.rovClipHigh - params.rovClipLow
|