強化商品線配對與組合 veto
All checks were successful
CD Pipeline / deploy (push) Successful in 1m11s

This commit is contained in:
OoO
2026-05-19 23:13:54 +08:00
parent 6fc064cd9a
commit 205b9ea3f6
3 changed files with 92 additions and 0 deletions

View File

@@ -448,6 +448,9 @@ def _has_bundle_offer(identity: ProductIdentity) -> bool:
or "優惠套組" in text
or "禮盒組" in text
or "加大組" in text
or "加量組" in text
or "分享組" in text
or "明星組" in text
or "套組" in text
)
@@ -461,6 +464,17 @@ def _has_multi_component(identity: ProductIdentity) -> bool:
)
def _has_refill_pack(identity: ProductIdentity) -> bool:
text = identity.normalized_name
return bool(
"補充瓶" in text
or "補充包" in text
or "替換蕊" in text
or "替換芯" in text
or "refill" in text
)
def _spec_mention_count(identity: ProductIdentity) -> int:
return len(re.findall(r"\d+(?:\.\d+)?\s*(?:ml|毫升|l|g|公克|kg)", identity.normalized_name, re.I))
@@ -488,6 +502,23 @@ def _chinese_bigram_score(left: ProductIdentity, right: ProductIdentity) -> floa
return 2 * len(left_signature & right_signature) / (len(left_signature) + len(right_signature))
def _has_strong_product_line_signal(
left: ProductIdentity,
right: ProductIdentity,
token_score: float,
chinese_name_score: float,
) -> bool:
shared_core = left.core_tokens & right.core_tokens
shared_latin_or_model = {
token for token in shared_core
if re.fullmatch(r"[a-z][a-z0-9-]{3,}", token)
or re.fullmatch(r"[a-z]{2,}-?\d+[a-z0-9-]*", token)
}
if shared_latin_or_model and token_score >= 0.50:
return True
return token_score >= 0.56 and chinese_name_score >= 0.45
def score_marketplace_match(
momo_name: str,
competitor_name: str,
@@ -525,6 +556,8 @@ def score_marketplace_match(
reasons.append("bundle_offer_conflict")
if _has_multi_component(left) != _has_multi_component(right):
reasons.append("multi_component_conflict")
if _has_refill_pack(left) != _has_refill_pack(right):
reasons.append("refill_pack_conflict")
left_spec_mentions = _spec_mention_count(left)
right_spec_mentions = _spec_mention_count(right)
if left_spec_mentions and right_spec_mentions and left_spec_mentions != right_spec_mentions:
@@ -537,6 +570,8 @@ def score_marketplace_match(
hard_veto = True
if _has_multi_component(left) != _has_multi_component(right):
hard_veto = True
if _has_refill_pack(left) != _has_refill_pack(right):
hard_veto = True
if left_spec_mentions and right_spec_mentions and left_spec_mentions != right_spec_mentions:
hard_veto = True
if chinese_name_score < 0.16 and token_score < 0.72:
@@ -568,6 +603,16 @@ def score_marketplace_match(
if token_score >= 0.72 and spec_score >= 0.82 and not brand_conflict:
score += 0.08
if (
brand_score >= 0.95
and not hard_veto
and price_penalty == 0
and type_score >= 0.55
and spec_score >= 0.55
and _has_strong_product_line_signal(left, right, token_score, chinese_name_score)
):
score += 0.07
reasons.append("strong_product_line_match")
if hard_veto:
score = min(score, 0.32)
score = max(0.0, min(1.0, score))