[V10.346] 補強 PChome identity anchor scorer

This commit is contained in:
OoO
2026-05-20 19:34:21 +08:00
parent 67cd35e2de
commit 8edd8a8604
4 changed files with 86 additions and 2 deletions

View File

@@ -114,6 +114,8 @@ GENERIC_TOKENS = {
SEARCH_NOISE_PHRASES = (
"新品上市",
"全新上市",
"國際航空版",
"超取免運",
"任選一款",
"任選1款",
"任選一色",
@@ -134,6 +136,10 @@ SEARCH_NOISE_PHRASES = (
"卸除髒汙",
"卸除防曬",
"卸防曬",
"防水眼線",
"寶寶牙刷",
"紗布牙刷",
"調節亮度",
"韓國彩妝",
"水光感",
"官方直營",
@@ -161,6 +167,14 @@ SEARCH_NOISE_TOKENS = {
"防曬",
"彩妝",
"水光感",
"超取",
"免運",
"航空版",
"國際版",
"附燈泡",
"定時",
"眼妝",
"滅菌",
"保濕",
"抗老",
"超品日",
@@ -178,6 +192,17 @@ SEARCH_IDENTITY_ANCHORS = (
"青春敷面膜",
"長效潤膚霜",
"小黑瓶",
"私密處護潔露",
"私密護潔露",
"口腔清潔棒",
"含氟防蛀修護牙膏",
"自然遮瑕素顏霜",
"超持久細滑眼線筆",
"香氛融蠟燈",
"水晶香氛能量寶盒禮盒組",
"零粉感超持久柔焦蜜粉餅",
"私密肌潔淨露",
"身體除毛器",
"免用水潔淨液",
"身體按摩精油",
"按摩精油",
@@ -487,6 +512,11 @@ def _leading_brand_tokens(original: str, normalized: str) -> set[str]:
tokens.add(token)
leading = normalized[:48]
leading_tokens = _tokenize(leading)
if leading_tokens:
first_token = leading_tokens[0]
if re.fullmatch(r"[\u4e00-\u9fff]{2,6}", first_token) and first_token not in GENERIC_TOKENS:
tokens.add(first_token)
for token in _tokenize(leading):
if re.fullmatch(r"[a-z][a-z0-9\-']{2,}", token):
tokens.add(token)
@@ -1183,6 +1213,17 @@ def score_marketplace_match(
):
score += 0.025
reasons.append("strong_exact_spec_match")
shared_anchor = _shared_identity_anchor(left, right)
if (
shared_anchor
and brand_score >= 0.95
and not hard_veto
and price_penalty == 0
and spec_score >= 0.85
and (token_score >= 0.43 or sequence_score >= 0.58)
):
score += 0.03
reasons.append("shared_identity_anchor")
if (
brand_score >= 0.95
and not hard_veto
@@ -1267,6 +1308,25 @@ def _extract_anchor_phrases(token: str) -> list[str]:
return phrases
def _shared_identity_anchor(left: ProductIdentity, right: ProductIdentity) -> str:
left_anchors: set[str] = set()
right_anchors: set[str] = set()
for token in left.core_tokens:
left_anchors.update(_extract_anchor_phrases(token))
for token in right.core_tokens:
right_anchors.update(_extract_anchor_phrases(token))
shared = sorted(
{
anchor
for anchor in left_anchors & right_anchors
if len(anchor.replace(" ", "")) >= 5 and anchor not in SEARCH_BROAD_ANCHORS
},
key=lambda anchor: (-len(anchor.replace(" ", "")), anchor),
)
return shared[0] if shared else ""
def _search_core_score(token: str, all_tokens: set[str]) -> tuple[int, int, str]:
cleaned = _clean_search_phrase(token)
if not cleaned: