Quellcode durchsuchen

优化语义匹配分析:添加定义支持和实质形式拆分

主要改进:
1. 调整输入结构:A_名称和A_定义分离,定义放在A_Context中
2. 新增match_with_definition函数:支持名称+定义的语义匹配
3. 强化实质vs形式的拆分标准:
   - 实质:去除后核心语义发生本质变化的成分
   - 形式:主体限定、修饰、程度等辅助成分
4. 添加拆分步骤指导:识别成分 → 去除测试 → 分类
5. 移除具体例子避免过拟合
6. 更新匹配关系判断:基于上下文中的作用/本质判断同一概念

技术细节:
- lib/match_analyzer.py: 新增MATCH_WITH_DEFINITION_PROMPT和match_with_definition函数
- step1_inspiration_match.py: 更新为使用match_with_definition,提取核心定义和分类定义

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

Co-Authored-By: Claude <noreply@anthropic.com>
yangxiaohui vor 2 Wochen
Ursprung
Commit
f6d1b61600
2 geänderte Dateien mit 287 neuen und 14 gelöschten Zeilen
  1. 258 0
      lib/match_analyzer.py
  2. 29 14
      step1_inspiration_match.py

+ 258 - 0
lib/match_analyzer.py

@@ -101,6 +101,175 @@ MATCH_SYSTEM_PROMPT = """
 """.strip()
 
 
+# ========== System Prompt for Name+Definition Match ==========
+MATCH_WITH_DEFINITION_PROMPT = """
+# 任务
+分析 <B> 与 <A> 的语义匹配关系。<A> 包含"名称"和"定义",定义作为辅助上下文帮助理解名称的含义。
+
+## 输入说明
+
+- **<B></B>**: 待匹配的内容(灵感点)
+- **<A></A>**: 名称(必选)
+- **<B_Context></B_Context>**: B 的补充上下文(可选)
+- **<A_Context></A_Context>**: A 的补充上下文,包含定义等信息(可选)
+
+**重要**:A_Context 中的定义仅用于辅助理解名称 <A> 的含义,主要分析 <B> 与 <A> 的匹配关系。
+
+## 分析方法
+
+### 核心原则:语义概念匹配
+
+先独立分析 <B> 和 <A> 的语义,再进行匹配,避免强行解释。
+
+### 分析步骤
+
+**第一步:独立分析 <B> 的语义**
+
+对 <B>(灵感点)进行语义分解,提取:
+
+**拆分步骤**:
+1. 先识别出所有语义成分(主体、动作/状态、属性、修饰等)
+2. 对每个成分做"去除测试":
+   - 如果去掉这个成分后,剩余部分的核心语义**没有发生本质变化** → 该成分是**形式**
+   - 如果去掉这个成分后,剩余部分的核心语义**发生了本质变化** → 该成分是**实质**
+3. 将成分分类到实质或形式中,每个成分只能出现在一个类别
+
+- **实质语义**:核心概念、本质内容(字典格式)
+  - 通常为1-2个核心概念
+  - 不要过度拆分复合词,保持核心概念的完整性
+  - key:语义片段(可以是词组)
+  - value:**客观地说明这个片段在B的上下文中的具体含义**
+
+- **形式语义**:具体限定、修饰、程度等(字典格式)
+  - 包括:主体限定词、属性修饰词、程度副词、时间/空间限定等
+  - key:语义片段
+  - value:**说明在B的上下文中修饰/限定什么,如何修饰/限定**
+
+**关键原则**:
+- 每个语义片段只能出现在一个类别中,不要重复
+- 表示主体、场景、对象的限定词通常属于形式,不要合并到实质中
+- 不要孤立地解释词语本身,要说明它**在当前B的上下文中**的具体含义
+- 不要过度推理,保持客观:描述B字面上说的是什么,而不是可能引发什么
+
+**注意**:纯粹分析 <B> 本身的语义,不要考虑与 <A> 的关系。
+
+**第二步:独立分析 <A> 的语义**
+
+对 <A>(名称)进行语义分解,提取:
+- **实质语义**:核心概念、本质内容(字典格式)
+  - **判断标准:单独拿出来看,语义跟原来相比没有发生本质变化**
+  - key:语义片段(可以是词组,保持完整的核心概念)
+  - value:**客观地说明这个片段在A的上下文中的具体含义和作用**
+- **形式语义**:具体限定、修饰等(字典格式)
+  - **判断标准:去掉后,核心概念本身不会丢失,只是失去了限定、修饰等细节**
+  - key:语义片段
+  - value:**说明在A的上下文中修饰/限定什么,如何修饰/限定**
+
+**关键**:
+- 可以参考<A_Context>中的定义来辅助理解名称的含义,但只分析名称本身
+- 同样要说明语义片段**在当前A的上下文中**的具体含义,不要孤立解释
+- 不要过度拆分复合词,保持核心概念的完整性
+
+**注意**:纯粹分析 <A> 本身的语义,不要考虑与 <B> 的关系。
+
+**第三步:建立匹配关系**
+
+遍历 <B> 的每个语义片段(实质+形式),找到与 <A> 中最接近的语义,判断关系:
+
+1. **判断是否同一概念**(基于上下文中的作用/本质)
+   - **不是看字面是否相同**,而是看**在各自上下文中的作用/本质是否相同**
+   - 同一概念:可以比较上下位/同义关系
+   - 不同概念:标记为无关
+
+2. **确定关系类型**(仅对同一概念)
+   - **同义/近义**:在各自上下文中的含义基本相同
+   - **上位词**:B在其上下文中的含义 比 A在其上下文中的含义 更抽象、更上位(B ⊇ A)
+   - **下位词**:B在其上下文中的含义 比 A在其上下文中的含义 更具体、更下位(B ⊆ A)
+   - **无关**:不同概念,无法比较
+
+3. **计算匹配分数**
+   - 关系得分:同义=1.0, 上位词=0.6, 下位词=0.4, 无关=0.0
+   - 实质语义权重=0.8,形式语义权重=0.2
+   - 计算公式:
+     ```
+     实质得分 = Σ(实质语义的关系得分) / 实质语义总数
+     形式得分 = Σ(形式语义的关系得分) / 形式语义总数
+     最终 score = 实质得分 × 0.8 + 形式得分 × 0.2
+     ```
+
+---
+
+## 评分标准(0-1分)
+
+**语义匹配度评分:**
+- **0.9-1.0**:<B> 的核心语义与名称/定义几乎完全对应
+- **0.7-0.8**:大部分核心语义匹配,少量增量
+- **0.5-0.6**:部分核心语义匹配,有一定增量
+- **0.3-0.4**:少量语义匹配,大部分概念不同
+- **0.1-0.2**:几乎无语义匹配,仅有弱关联
+- **0.0**:完全无关
+
+**重要原则:**
+- 关注有价值的语义片段,而非孤立的字面词语
+- 考虑概念之间的语义关系,不仅是字面匹配
+
+---
+
+## 输出格式(严格JSON)
+```json
+{
+  "B语义分析": {
+    "实质": {
+      "语义片段1": "说明"
+    },
+    "形式": {
+      "语义片段2": "说明",
+      "语义片段3": "说明"
+    }
+  },
+  "A语义分析": {
+    "实质": {
+      "语义片段1": "说明",
+      "语义片段2": "说明"
+    },
+    "形式": {
+      "语义片段3": "说明"
+    }
+  },
+  "匹配关系": [
+    {
+      "B语义": "语义片段1",
+      "A语义": "语义片段1",
+      "是否同一概念": true,
+      "关系": "下位词",
+      "说明": "说明两者的关系"
+    },
+    {
+      "B语义": "语义片段2",
+      "A语义": "语义片段2",
+      "是否同一概念": false,
+      "关系": "无关",
+      "说明": "说明为什么无关"
+    }
+  ],
+  "score": 0.65,
+  "score说明": "基于评分规则计算得出"
+}
+```
+
+**输出要求**:
+1. 必须严格按照上述JSON格式输出
+2. **B语义分析**:必须包含"实质"、"形式"两个字段,其中实质通常为1-2个核心概念
+3. **A语义分析**:必须包含"实质"、"形式"两个字段
+4. **实质和形式**:都是字典格式,key是语义片段,value是对该片段的说明
+5. **匹配关系**:数组格式,包含所有B语义片段的匹配情况,字段顺序为:B语义 → A语义 → 是否同一概念 → 关系 → 说明
+6. **是否同一概念**:布尔值,true或false
+7. **关系**:必须是以下之一:"同义/近义"、"上位词"、"下位词"、"无关"
+8. **score**:0-1之间的浮点数,保留2位小数,按照评分规则计算
+9. **score说明**:说明分数的计算依据
+""".strip()
+
+
 def create_match_agent(model_name: str) -> Agent:
     """创建信息匹配分析的 Agent
 
@@ -346,3 +515,92 @@ async def match_batch(
             "score": 0.0,
             "score说明": f"匹配过程出错: {str(e)}"
         } for _ in b_items]
+
+
+async def match_with_definition(
+    b_content: str,
+    element_name: str,
+    element_definition: str,
+    model_name: str,
+    b_context: str = "",
+    a_context: str = ""
+) -> dict:
+    """名称+定义匹配:分析 B 分别与名称、定义的语义匹配关系
+
+    Args:
+        b_content: B(待匹配)的内容
+        element_name: 要素名称
+        element_definition: 要素定义
+        model_name: 使用的模型名称
+        b_context: B 的补充上下文(可选,默认为空)
+        a_context: A 的补充上下文(可选,默认为空)
+
+    Returns:
+        匹配结果字典:{"名称匹配": {...}, "定义匹配": {...}}
+    """
+    try:
+        # 创建使用新 prompt 的 Agent
+        agent = Agent(
+            name="Name+Definition Match Expert",
+            instructions=MATCH_WITH_DEFINITION_PROMPT,
+            model=get_model(model_name),
+            tools=[],
+        )
+
+        # A 内容只包含名称,定义放在 A_Context 中
+        a_content = element_name
+
+        # 将定义添加到 A_Context(如果已有 a_context,则追加)
+        if element_definition:
+            definition_context = f"定义:{element_definition}"
+            if a_context:
+                full_a_context = f"{a_context}\n{definition_context}"
+            else:
+                full_a_context = definition_context
+        else:
+            full_a_context = a_context
+
+        # 运行匹配
+        output = await _run_match_agent(
+            agent, b_content, a_content, "并输出 JSON 格式",
+            b_context=b_context, a_context=full_a_context
+        )
+
+        # 解析响应
+        parsed_result = parse_match_response(output)
+
+        # 验证返回格式
+        required_fields = ["B语义分析", "A语义分析", "匹配关系", "score", "score说明"]
+        missing_fields = [f for f in required_fields if f not in parsed_result]
+
+        if missing_fields:
+            return {
+                "B语义分析": {
+                    "实质": {},
+                    "形式": {}
+                },
+                "A语义分析": {
+                    "实质": {},
+                    "形式": {}
+                },
+                "匹配关系": [],
+                "score": 0.0,
+                "score说明": f"解析失败:缺少字段 {missing_fields}"
+            }
+
+        return parsed_result
+
+    except Exception as e:
+        return {
+            "B语义分析": {
+                "实质": {},
+                "形式": {}
+            },
+            "A语义分析": {
+                "实质": {},
+                "形式": {}
+            },
+            "匹配关系": [],
+            "score": 0.0,
+            "score说明": f"匹配过程出错: {str(e)}"
+        }

+ 29 - 14
step1_inspiration_match.py

@@ -14,7 +14,7 @@ from agents import trace
 from agents.tracing.create import custom_span
 from lib.my_trace import set_trace_smith as set_trace
 from lib.async_utils import process_tasks_with_semaphore
-from lib.match_analyzer import match_single
+from lib.match_analyzer import match_with_definition
 from lib.data_loader import load_persona_data, load_inspiration_list, select_inspiration
 
 # 模型配置
@@ -47,7 +47,8 @@ async def match_single_task(task: dict, _index: int) -> dict:
     Args:
         task: 匹配任务,包含:
             - 灵感: 灵感点文本
-            - 要素: 要素名称
+            - 要素名称: 要素名称
+            - 要素定义: 要素定义
             - 要素类型: "一级分类" 或 "二级分类"
             - 上下文: 上下文字符串
         _index: 任务索引(由 async_utils 传入,此处未使用)
@@ -56,14 +57,16 @@ async def match_single_task(task: dict, _index: int) -> dict:
         匹配结果
     """
     inspiration = task["灵感"]
-    element = task["要素"]
+    element_name = task["要素名称"]
+    element_definition = task["要素定义"]
     context_str = task["上下文"]
 
-    # 调用通用匹配模块(内部已包含错误处理和 custom_span 追踪)
-    # B = 灵感, A = 要素, A_Context = 上下文
-    match_result = await match_single(
+    # 调用名称+定义匹配模块(内部已包含错误处理和 custom_span 追踪)
+    # B = 灵感, A = 名称+定义, A_Context = 上下文
+    match_result = await match_with_definition(
         b_content=inspiration,
-        a_content=element,
+        element_name=element_name,
+        element_definition=element_definition,
         model_name=MODEL_NAME,
         a_context=context_str  # 要素的上下文
     )
@@ -72,14 +75,16 @@ async def match_single_task(task: dict, _index: int) -> dict:
     full_result = {
         "输入信息": {
             "B": inspiration,         # 待匹配:灵感
-            "A": element,             # 上下文:要素
+            "A_名称": element_name,    # 要素名称
+            "A_定义": element_definition,  # 要素定义
             "B_Context": "",          # B的上下文(暂时为空)
             "A_Context": context_str  # A的上下文:所属视角/一级分类
         },
-        "匹配结果": match_result,     # {"相同部分": {}, "增量部分": {}, "score": 0.0, "score说明": ""}
+        "匹配结果": match_result,     # {"名称匹配": {...}, "定义匹配": {...}}
         "业务信息": {                 # 业务语义信息(统一存储在最后)
             "灵感": inspiration,
-            "匹配要素": element
+            "匹配要素名称": element_name,
+            "匹配要素定义": element_definition
         }
     }
 
@@ -116,12 +121,14 @@ def build_match_tasks(
                 break
 
             level1_name = pattern.get("分类名称", "")
+            level1_definition = pattern.get("核心定义", "")
 
             # 添加一级分类任务
             context_str = build_context_str(perspective_name)
             tasks.append({
                 "灵感": inspiration,
-                "要素": level1_name,
+                "要素名称": level1_name,
+                "要素定义": level1_definition,
                 "要素类型": "一级分类",
                 "上下文": context_str
             })
@@ -132,11 +139,13 @@ def build_match_tasks(
                     break
 
                 level2_name = level2.get("分类名称", "")
+                level2_definition = level2.get("分类定义", "")
 
                 context_str = build_context_str(perspective_name, level1_name)
                 tasks.append({
                     "灵感": inspiration,
-                    "要素": level2_name,
+                    "要素名称": level2_name,
+                    "要素定义": level2_definition,
                     "要素类型": "二级分类",
                     "上下文": context_str
                 })
@@ -191,8 +200,14 @@ async def process_inspiration_match(
             show_progress=True
         )
 
-        # 按 score 降序排序
-        results.sort(key=lambda x: x.get('匹配结果', {}).get('score', 0), reverse=True)
+        # 按名称匹配 score 降序排序(主要)+ 定义匹配 score(次要)
+        results.sort(
+            key=lambda x: (
+                x.get('匹配结果', {}).get('名称匹配', {}).get('score', 0),
+                x.get('匹配结果', {}).get('定义匹配', {}).get('score', 0)
+            ),
+            reverse=True
+        )
 
     # 构建输出结果
     output = {