fix: 外股代码映射到中文显示名时英文新闻相关性漏判(issue #2026,PR #2047 关闭重做) (#2049)

* fix(search): 外股代码映射到中文显示名时英文新闻相关性漏判 (issue #2026)

问题:STOCK_NAME_MAP 把外股 ticker 映射为中文显示名(AAPL→苹果、00700→腾讯控股、
BABA→阿里巴巴集团)后,search 层在三个维度上误判英文新闻:

1. search_stock_news:prefer_chinese 看见 stock_name 含中文就在 is_foreign
   之前 return True,导致 AAPL 走中文查询「苹果 AAPL 股票 最新消息」,
   英文 provider 收不到 Apple 公司名。
2. search_comprehensive_intel / search_stock_events:英文查询分支直接拼
   stock_name,把「苹果」喂给英文 provider。
3. _score_news_relevance:company_identity_terms 仅来自中文 stock_name,
   英文标题「Apple reports earnings beat」既无中文匹配也无英文 alias 匹配,
   被判 macro_market_news 而非 direct_company_news。

方案 A(替代 PR #2047 的 SearchService 私有 29 项静态表):

1. src/data/stock_mapping.py 新增同源 sibling 英文表 STOCK_ENGLISH_NAME_MAP,
   与 STOCK_NAME_MAP 同文件维护;模块加载时 _assert_foreign_english_map_invariant
   静态校验 keys ⊆ STOCK_NAME_MAP 外股子集、且外股子集 ⊆ keys(双向不漂移)。
2. canonicalize_foreign_stock_code 提供 bare / .US / .N / .O / .A / .HK suffix
   / HK00700 prefix 全形式归一化为 STOCK_ENGLISH_NAME_MAP 的 key 形。
3. foreign_stock_english_aliases 在 stock_name 已含 CJK 时返回 canonical
   英文 alias 元组;已是英文名或非外股则返回 (),调用方各自回退到 stock_name。
4. SearchService._is_foreign_stock / _foreign_english_query_terms /
   search_stock_news / search_comprehensive_intel / search_stock_events /
   _score_news_relevance 全部改为读这一个真源;并修复 prefer_chinese 在
   is_foreign 且有 alias 时强制为 False,让 foreign 分支真正可达。
5. 测试:tests/test_search_news_freshness.py 新增 8 个用例覆盖
   _market_english_aliases 全场景、STOCK_ENGLISH_NAME_MAP ⊆ STOCK_NAME_MAP
   双向 invariant、AAPL/AMZN/MSFT/00700/BABA 五 ticker 在三入口的
   英文查询构建、_score_news_relevance 英文媒体短名标题归类、A 股对照
   不污染。

Fixes #2026.

* fix(search): alias 展开 term 去重 + 死分支清理 + CHANGELOG 标签格式 (PR #2049 review)

ZhuLinsen 在 PR #2049 复核给出 1 个高置信度 blocker、1 个非阻断建议;massif-01
AI bot 给出 1 个非阻断死代码建议。本次提交关闭三处:

1. OR-COR-3ff1e068 [correctness] 高 confidence blocker

   因:STOCK_ENGLISH_NAME_MAP['AAPL'] = ('Apple Inc.', 'Apple'),legal alias
   Apple Inc. 经 _company_identity_terms 展开后即包含 Apple,循环里
   拿 Apple Inc. 命中 snippet Apple 加 16 分;再到第二个 alias
   Apple 又对同一 snippet 同一 Apple 命中再加 16 分;再叠 12 分事件词
   得 44 分越 direct_signal >= 38 阈值,把摘要英文名歧义命中抬升为
   direct_company_news。

   果(修复前 massif-01 复现):

   title='US stocks mixed after Fed comments'
   snippet='Apple reports earnings beat and revenue grows.'
   stock_code='AAPL' stock_name='苹果'
   → relevance_score=44, classification=direct_company_news(错误)

   修:在 _score_news_relevance alias 路径引入 seen_identity_terms 集合,
   按 term 而非 alias 累加,同一 Apple 在任一 alias 展开中已被计分后,
   后续 alias 路径不再重复加分;保留每个 term 在标题/摘要 single-pass 命中
   即 break 的语义不变。

   果(修复后):

   同输入 → relevance_score=28, classification=sector_related_news
   (正确降级为背景新闻,因未命中股票代码或公司全称触发第三条原因
    '未命中股票代码或公司全称,降级为背景新闻')

   锁回归测试 test_score_news_relevance_english_alias_dedup_prevents_double_count
   覆盖:snippet-only + 英文短名歧义命中场景,断言 classification 必为
   sector_related_news、relevance_score < 38、reasons 含且仅含一次摘要命中。

2. ZhuLinsen non-blocking:docs/CHANGELOG.md [Unreleased] 条目未用
   AGENTS.md §1.4 要求的 - [类型] 描述 扁平标签格式。

   修:把 - 修复 #2026 ... 改为 - [修复] #2026 ...,并在描述末尾
   补充去重修复一并告知。

3. massif-01 AI bot non-blocking 死代码:_is_foreign_stock 在
   canonicalize_foreign_stock_code 已统一 HK00700 / 00700.HK 全形到 00700
   之后,原 lower.startswith('hk') 分支不可达。

   修:删除该分支,注释中说明 canonical 之后只剩 5 位纯数字一种 HK 形式。

验证:

\b

\b

\b

\b

无回归:标题命中、港股综合、A 股对照、reasons 解析、各 search 入口查询构建均保持。
This commit is contained in:
Nicholas-Xiong
2026-07-20 21:55:30 +08:00
committed by GitHub
parent e685751f16
commit 2933cdf8c5
4 changed files with 499 additions and 20 deletions

View File

@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
<!-- 新条目格式:- [类型] 描述(类型取值:新功能/改进/修复/文档/测试/chore-->
<!-- 每条独立一行追加到本段末尾,无需分类标题,合并时冲突最小 -->
- [修复] #2026 外股代码映射到中文显示名时英文新闻相关性判定漏判:新增同源 STOCK_ENGLISH_NAME_MAP 单一真源、canonicalize_foreign_stock_code 规范化入口与 _foreign_english_query_terms 别名解析,使 AAPL/00700/BABA 等 ticker 即使 stock_name 为中文也能在查询构建、相关性打分与多维度情报路径上复用 canonical 英文名,并补齐 .US/.HK suffix / HK 前缀全形式的归类与回归用例;同时在 _score_news_relevance 对 alias 展开 term 做去重,避免 legal alias 展开短名与显式 short alias 重复计分。
## [3.27.0] - 2026-07-19