Преглед изворни кода

feat: 新增整体相关性评分,优化最终分数计算

主要改动:
1. 评分体系重构:引入规则分数和相关性分数
   - 规则分数:基于匹配关系机械计算(Σ关系得分 / 实质语义总数)
   - 相关性分数:LLM整体判断(考虑语义距离、上下文契合度、表达意图)
   - 最终分数 = 0.8 × 规则分数 + 0.2 × 相关性分数

2. Prompt更新:增加第4步"评估整体相关性"
   - 要求LLM从宏观角度评估整体相关性
   - 补充规则分数可能遗漏的语义关联(如因果关系、主题关联)

3. 输出格式新增字段:
   - 规则分数、规则分数说明
   - 相关性分数、相关性说明
   - score:从单一规则计算改为加权融合

4. calculate_score_by_code 函数更新:
   - 计算规则分数(基于匹配关系)
   - 提取LLM的相关性分数
   - 按相同公式计算最终分数进行验证

5. 字段验证更新:
   - required_fields 新增规则分数和相关性分数相关字段
   - 错误返回结构同步更新

优势:
- 规则分数保证客观性和可解释性
- 相关性分数补充语义理解能力(捕捉因果、主题等隐含关系)
- 两者结合更全面、更合理

测试结果:
- 规则分数 = 0.0(迷惑行为 vs 感受思考:无关)
- 相关性分数 = 0.4(有因果关联:行为引发感受)
- 最终分数 = 0.08
- score_by_code = 0.08(验证通过)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
yangxiaohui пре 2 недеља
родитељ
комит
fee57b2951
1 измењених фајлова са 60 додато и 16 уклоњено
  1. 60 16
      lib/match_analyzer.py

+ 60 - 16
lib/match_analyzer.py

@@ -195,14 +195,26 @@ MATCH_WITH_DEFINITION_PROMPT = """
    - **下位词**:B在其上下文中的含义 比 A在其上下文中的含义 更具体、更下位(B ⊆ A)
    - **无关**:不同概念,无法比较
 
-3. **计算匹配分数**
+3. **计算规则分数**
    - 关系得分:同义=1.0, 上位词=0.6, 下位词=0.4, 无关=0.0
-   - 只基于实质语义匹配计算score
+   - 只基于实质语义匹配计算规则分数
    - 计算公式:
      ```
-     score = Σ(实质语义的关系得分) / 实质语义总数
+     规则分数 = Σ(实质语义的关系得分) / 实质语义总数
      ```
-   - 在score说明中,可以提及形式语义的差异作为补充参考
+
+4. **评估整体相关性**
+   - 在完成上述匹配关系分析后,从宏观角度评估 <B> 和 <A> 的整体相关性
+   - 考虑因素:
+     - 语义距离:核心概念之间的距离
+     - 上下文契合度:在各自上下文中的关联程度
+     - 表达意图的一致性
+   - 给出相关性分数(0-1之间的浮点数,保留2位小数)
+   - 给出相关性说明
+
+5. **计算最终分数**
+   - 最终分数 = 0.8 × 规则分数 + 0.2 × 相关性分数
+   - 在score说明中说明计算过程,可以提及形式语义的差异作为补充参考
 
 ---
 
@@ -263,8 +275,12 @@ MATCH_WITH_DEFINITION_PROMPT = """
       "说明": "说明为什么无关"
     }
   ],
-  "score": 0.4,
-  "score说明": "只基于实质语义计算。B有2个实质语义,第1个是A的下位词(0.4),第2个无关(0.0),最终score = (0.4 + 0.0) / 2 = 0.4。形式语义差异:B有'修饰词x',A有'修饰词y',作为补充参考。"
+  "规则分数": 0.2,
+  "规则分数说明": "B有2个实质语义,第1个是A的下位词(0.4),第2个无关(0.0),规则分数 = (0.4 + 0.0) / 2 = 0.2",
+  "相关性分数": 0.3,
+  "相关性说明": "虽然有部分语义重叠,但整体上下文契合度较低,因为B强调具体事物,A强调抽象概念",
+  "score": 0.22,
+  "score说明": "最终分数 = 0.8 × 0.2 + 0.2 × 0.3 = 0.22。形式语义差异:B有'修饰词x',A有'修饰词y',作为补充参考。"
 }
 ```
 
@@ -277,8 +293,12 @@ MATCH_WITH_DEFINITION_PROMPT = """
 6. **B语义说明和A语义说明**:必须从前面的实质语义分析中复述,保持一致
 7. **是否同一概念**:布尔值,true或false
 8. **关系**:必须是以下之一:"同义/近义"、"上位词"、"下位词"、"无关"
-9. **score**:0-1之间的浮点数,保留2位小数,只基于实质语义匹配计算
-10. **score说明**:说明分数的计算依据,可以提及形式语义的差异作为补充
+9. **规则分数**:0-1之间的浮点数,保留2位小数,基于匹配关系计算
+10. **规则分数说明**:说明规则分数的计算依据
+11. **相关性分数**:0-1之间的浮点数,保留2位小数,整体相关性评估
+12. **相关性说明**:说明相关性分数的判断依据
+13. **score**:0-1之间的浮点数,保留2位小数,= 0.8 × 规则分数 + 0.2 × 相关性分数
+14. **score说明**:说明最终分数的计算过程,可以提及形式语义的差异作为补充
 """.strip()
 
 
@@ -302,16 +322,19 @@ def create_match_agent(model_name: str) -> Agent:
 
 
 def calculate_score_by_code(match_result: dict) -> float:
-    """根据匹配关系用代码计算score(只基于实质语义)
+    """根据匹配关系和相关性分数用代码计算最终score
+
+    计算公式:score = 0.8 × 规则分数 + 0.2 × 相关性分数
+    其中规则分数基于匹配关系计算,相关性分数由LLM给出
 
     Args:
-        match_result: 匹配结果字典,包含 B语义分析、A语义分析、匹配关系
+        match_result: 匹配结果字典,包含 B语义分析、匹配关系、相关性分数
 
     Returns:
-        计算得出的score(0-1之间)
+        计算得出的最终score(0-1之间)
 
     Raises:
-        ValueError: 当匹配关系为空或数据不完整时
+        ValueError: 当数据不完整时
     """
     # 关系得分映射
     RELATION_SCORES = {
@@ -323,20 +346,25 @@ def calculate_score_by_code(match_result: dict) -> float:
 
     b_semantics = match_result.get("B语义分析", {})
     match_relations = match_result.get("匹配关系", [])
+    relevance_score = match_result.get("相关性分数")
 
+    # 验证必需字段
     if not match_relations:
         raise ValueError("匹配关系为空,无法计算score_by_code")
 
     if not b_semantics:
         raise ValueError("B语义分析为空,无法计算score_by_code")
 
+    if relevance_score is None:
+        raise ValueError("相关性分数为空,无法计算score_by_code")
+
     # 提取B的实质语义片段
     b_essence = set(b_semantics.get("实质", {}).keys())
 
     if not b_essence:
         raise ValueError("B语义分析中实质为空,无法计算score_by_code")
 
-    # 只计算实质语义的得分
+    # 计算规则分数:基于实质语义的匹配关系
     essence_scores = []
 
     for relation in match_relations:
@@ -354,8 +382,11 @@ def calculate_score_by_code(match_result: dict) -> float:
     if not essence_scores:
         raise ValueError("匹配关系中没有实质语义,无法计算score_by_code")
 
-    # 只基于实质语义计算平均分
-    final_score = sum(essence_scores) / len(essence_scores)
+    # 计算规则分数(基于匹配关系)
+    rule_score = sum(essence_scores) / len(essence_scores)
+
+    # 计算最终分数:0.8 × 规则分数 + 0.2 × 相关性分数
+    final_score = 0.8 * rule_score + 0.2 * relevance_score
 
     return round(final_score, 2)
 
@@ -641,7 +672,12 @@ async def match_with_definition(
         parsed_result = parse_match_response(output)
 
         # 验证返回格式
-        required_fields = ["B语义分析", "A语义分析", "匹配关系", "score", "score说明"]
+        required_fields = [
+            "B语义分析", "A语义分析", "匹配关系",
+            "规则分数", "规则分数说明",
+            "相关性分数", "相关性说明",
+            "score", "score说明"
+        ]
         missing_fields = [f for f in required_fields if f not in parsed_result]
 
         if missing_fields:
@@ -655,6 +691,10 @@ async def match_with_definition(
                     "形式": {}
                 },
                 "匹配关系": [],
+                "规则分数": 0.0,
+                "规则分数说明": f"解析失败:缺少字段 {missing_fields}",
+                "相关性分数": 0.0,
+                "相关性说明": f"解析失败:缺少字段 {missing_fields}",
                 "score": 0.0,
                 "score_by_code": 0.0,
                 "score说明": f"解析失败:缺少字段 {missing_fields}"
@@ -677,6 +717,10 @@ async def match_with_definition(
                 "形式": {}
             },
             "匹配关系": [],
+            "规则分数": 0.0,
+            "规则分数说明": f"匹配过程出错: {str(e)}",
+            "相关性分数": 0.0,
+            "相关性说明": f"匹配过程出错: {str(e)}",
             "score": 0.0,
             "score_by_code": 0.0,
             "score说明": f"匹配过程出错: {str(e)}"