Explorar el Código

fix(context): normalize genre aliases for profile extraction

lingfengQAQ hace 4 meses
padre
commit
5fa39ecf08

+ 30 - 4
.claude/scripts/data_modules/context_manager.py

@@ -390,7 +390,9 @@ class ContextManager:
 
         genre_aliases = {
             "修仙": "xianxia",
+            "修仙/玄幻": "xianxia",
             "玄幻": "xianxia",
+            "爽文/系统流": "shuangwen",
             "高武": "xianxia",
             "西幻": "xianxia",
             "都市异能": "urban-power",
@@ -609,7 +611,8 @@ class ContextManager:
         if not text:
             return []
         if not getattr(self.config, "context_genre_profile_support_composite", True):
-            return [text]
+            normalized_single = self._normalize_genre_token(text)
+            return [normalized_single] if normalized_single else [text]
 
         separators = getattr(self.config, "context_genre_profile_separators", ("+", "/", "|", ",", ",", "、"))
         pattern = "|".join(re.escape(str(token)) for token in separators if str(token))
@@ -620,12 +623,35 @@ class ContextManager:
         deduped: List[str] = []
         seen = set()
         for token in tokens:
-            lower = token.lower()
+            normalized_token = self._normalize_genre_token(token)
+            if not normalized_token:
+                continue
+            lower = normalized_token.lower()
             if lower in seen:
                 continue
             seen.add(lower)
-            deduped.append(token)
-        return deduped or [text]
+            deduped.append(normalized_token)
+        if deduped:
+            return deduped
+
+        fallback_token = self._normalize_genre_token(text)
+        return [fallback_token] if fallback_token else [text]
+
+    def _normalize_genre_token(self, token: str) -> str:
+        value = str(token or "").strip()
+        if not value:
+            return ""
+
+        aliases = {
+            "游戏电竞": "电竞",
+            "电竞文": "电竞",
+            "直播": "直播文",
+            "直播带货": "直播文",
+            "主播": "直播文",
+            "克系": "克苏鲁",
+            "克系悬疑": "克苏鲁",
+        }
+        return aliases.get(value, value)
 
     def _build_composite_genre_hints(self, genres: List[str], refs: List[str]) -> List[str]:
         if len(genres) <= 1:

+ 52 - 0
.claude/scripts/data_modules/tests/test_context_manager.py

@@ -389,6 +389,58 @@ def test_context_manager_genre_alias_guidance_and_heading_extraction(temp_projec
     assert any("兑现密度基线" in str(text) for text in items)
 
 
+def test_context_manager_genre_aliases_normalized_for_profile_lookup(temp_project):
+    refs_dir = temp_project.project_root / ".claude" / "references"
+    refs_dir.mkdir(parents=True, exist_ok=True)
+    (refs_dir / "genre-profiles.md").write_text(
+        """
+## 电竞
+- 联赛升级
+
+## 直播文
+- 实时反馈
+
+## 克苏鲁
+- 真相代价
+""".strip(),
+        encoding="utf-8",
+    )
+    (refs_dir / "reading-power-taxonomy.md").write_text(
+        """
+## 电竞
+- 决策后果
+
+## 直播文
+- 数据闭环
+
+## 克苏鲁
+- 规则优先
+""".strip(),
+        encoding="utf-8",
+    )
+
+    manager = ContextManager(temp_project)
+
+    assert manager._parse_genre_tokens("电竞文") == ["电竞"]
+    assert manager._parse_genre_tokens("直播") == ["直播文"]
+    assert manager._parse_genre_tokens("克系") == ["克苏鲁"]
+
+    state = {
+        "project": {"genre": "电竞文+直播"},
+        "protagonist_state": {"name": "叶修"},
+        "chapter_meta": {},
+        "disambiguation_warnings": [],
+        "disambiguation_pending": [],
+    }
+    temp_project.state_file.write_text(json.dumps(state, ensure_ascii=False), encoding="utf-8")
+
+    payload = manager.build_context(20, template="plot", use_snapshot=False, save_snapshot=False)
+    profile = payload["sections"]["genre_profile"]["content"]
+
+    assert profile.get("genre") == "电竞"
+    assert "直播文" in (profile.get("genres") or [])
+
+
 def test_context_manager_compact_text_truncation(temp_project):
     manager = ContextManager(temp_project)
     manager.config.context_compact_text_enabled = True

+ 2 - 2
README.md

@@ -742,7 +742,7 @@ git checkout ch0045
 
 ### v5.4.2 (当前)
 - **创意约束系统**:三轴混搭 + 反套路触发器 + 镜像对抗 + 约束继承
-- **题材模板扩展**:从10+扩展到35+种题材模板
+- **题材模板扩展**:从10+扩展到38+种题材模板
 - **复合题材支持**:支持"题材A+题材B"组合(1主1辅)
 - **反套路库**:修仙/玄幻反套路库(20条限制 + 15种非套路爽点)
 - **规则怪谈反套路库**:20条限制 + 20种非套路爽点
@@ -837,7 +837,7 @@ git checkout ch0045
 - **约束分层机制**:Hard Invariants (4条) + Soft Guidance (可Override)
 - **Override Contract**:违反软建议需记录理由和偿还计划
 - **追读力债务**:债务追踪、利息计算、逾期管理
-- **题材Profile**:8种内置题材配置(偏好钩子/爽点/微兑现要求)
+- **题材Profile**:11种内置题材配置(偏好钩子/爽点/微兑现要求)
 - **SQLite新表**:override_contracts、chase_debt、debt_events、chapter_reading_power
 - **Context Agent**:输出精简为7个板块(含追读力策略,债务状态按需输出)
 - **CLI新命令**:get-debt-summary、get-recent-reading-power、accrue-interest 等