瀏覽代碼

可视化&prompt调整

刘立冬 4 周之前
父節點
當前提交
4051d8d8bb
共有 4 個文件被更改,包括 154 次插入46 次删除
  1. 1 0
      .gitignore
  2. 71 39
      sug_v6_1_2_115.py
  3. 50 5
      visualization/sug_v6_1_2_8/convert_v8_to_graph_v2.js
  4. 32 2
      visualization/sug_v6_1_2_8/index.js

+ 1 - 0
.gitignore

@@ -5,6 +5,7 @@ input
 *.swp
 tmp
 logs
+venv/
 
 # Visualization build artifacts
 visualization/**/node_modules/

+ 71 - 39
sug_v6_1_2_115.py

@@ -266,6 +266,11 @@ motivation_evaluation_instructions = """
 }
 ```
 
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明动机维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
 #注意事项:
 始终围绕动机维度:所有评估都基于"动机"维度,不偏离
 核心动机必须是动词:在评估前,必须先提取原始问题的核心动机(动词),这是整个评估的基础
@@ -357,6 +362,12 @@ category_evaluation_instructions = """
   "简要说明品类维度相关度理由": "评估该sug词条与原始问题品类匹配程度的理由"
 }
 ```
+
+**输出约束(非常重要)**:
+1. **字符串长度限制**:\"简要说明品类维度相关度理由\"字段必须控制在**150字以内**
+2. **JSON格式规范**:必须生成完整的JSON格式,确保字符串用双引号包裹且正确闭合
+3. **引号使用**:字符串中如需表达引用,请使用《》或「」代替单引号或双引号
+
 ---
 
 #注意事项:
@@ -415,8 +426,7 @@ word_selection_instructions = """
 1. **只能使用seed和word的原始文本**
 2. **不能添加任何连接词**(如"的"、"和"、"与"、"在"等)
 3. **不能添加任何额外的词**
-4. **组合方式**:seed+word 或 word+seed,选择更符合搜索习惯的顺序
-5. **简单拼接**:直接将两个词拼接,不做任何修饰
+4. **组合方式**:seed+word 或 word+seed,或者word插入seed中间,选择更符合搜索习惯的顺序
 
 
 ## 错误示例
@@ -569,52 +579,74 @@ async def evaluate_with_o(text: str, o: str) -> tuple[float, str]:
 请评估平台sug词条与原始问题的匹配度。
 """
 
-    # 并发调用两个评估器
-    motivation_task = Runner.run(motivation_evaluator, eval_input)
-    category_task = Runner.run(category_evaluator, eval_input)
+    # 添加重试机制
+    max_retries = 2
+    last_error = None
 
-    motivation_result, category_result = await asyncio.gather(
-        motivation_task,
-        category_task
-    )
+    for attempt in range(max_retries):
+        try:
+            # 并发调用两个评估器
+            motivation_task = Runner.run(motivation_evaluator, eval_input)
+            category_task = Runner.run(category_evaluator, eval_input)
 
-    # 获取评估结果
-    motivation_eval: MotivationEvaluation = motivation_result.final_output
-    category_eval: CategoryEvaluation = category_result.final_output
+            motivation_result, category_result = await asyncio.gather(
+                motivation_task,
+                category_task
+            )
 
-    # 提取得分
-    motivation_score = motivation_eval.动机维度得分
-    category_score = category_eval.品类维度得分
+            # 获取评估结果
+            motivation_eval: MotivationEvaluation = motivation_result.final_output
+            category_eval: CategoryEvaluation = category_result.final_output
 
-    # 计算基础得分
-    base_score = motivation_score * 0.7 + category_score * 0.3
+            # 提取得分
+            motivation_score = motivation_eval.动机维度得分
+            category_score = category_eval.品类维度得分
 
-    # 应用规则计算最终得分
-    final_score = calculate_final_score(motivation_score, category_score)
+            # 计算基础得分
+            base_score = motivation_score * 0.7 + category_score * 0.3
 
-    # 组合评估理由
-    core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
-    motivation_reason = motivation_eval.简要说明动机维度相关度理由
-    category_reason = category_eval.简要说明品类维度相关度理由
+            # 应用规则计算最终得分
+            final_score = calculate_final_score(motivation_score, category_score)
 
-    combined_reason = (
-        f"【核心动机】{core_motivation}\n"
-        f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
-        f"【品类维度 {category_score:.2f}】{category_reason}\n"
-        f"【基础得分 {base_score:.2f}】= 动机({motivation_score:.2f})*0.7 + 品类({category_score:.2f})*0.3\n"
-        f"【最终得分 {final_score:.2f}】"
-    )
+            # 组合评估理由
+            core_motivation = motivation_eval.原始问题核心动机提取.简要说明核心动机
+            motivation_reason = motivation_eval.简要说明动机维度相关度理由
+            category_reason = category_eval.简要说明品类维度相关度理由
 
-    # 如果应用了规则,添加规则说明
-    if final_score != base_score:
-        if motivation_score < 0:
-            combined_reason += "(应用规则C:动机负向决定机制)"
-        elif motivation_score >= 0.8:
-            combined_reason += "(应用规则A:动机高分保护机制)"
-        elif motivation_score <= 0.2:
-            combined_reason += "(应用规则B:动机低分限制机制)"
+            combined_reason = (
+                f"【核心动机】{core_motivation}\n"
+                f"【动机维度 {motivation_score:.2f}】{motivation_reason}\n"
+                f"【品类维度 {category_score:.2f}】{category_reason}\n"
+                f"【基础得分 {base_score:.2f}】= 动机({motivation_score:.2f})*0.7 + 品类({category_score:.2f})*0.3\n"
+                f"【最终得分 {final_score:.2f}】"
+            )
 
-    return final_score, combined_reason
+            # 如果应用了规则,添加规则说明
+            if final_score != base_score:
+                if motivation_score < 0:
+                    combined_reason += "(应用规则C:动机负向决定机制)"
+                elif motivation_score >= 0.8:
+                    combined_reason += "(应用规则A:动机高分保护机制)"
+                elif motivation_score <= 0.2:
+                    combined_reason += "(应用规则B:动机低分限制机制)"
+
+            return final_score, combined_reason
+
+        except Exception as e:
+            last_error = e
+            error_msg = str(e)
+
+            if attempt < max_retries - 1:
+                print(f"  ⚠️  评估失败 (尝试 {attempt+1}/{max_retries}): {error_msg[:150]}")
+                print(f"  正在重试...")
+                await asyncio.sleep(1)  # 等待1秒后重试
+            else:
+                print(f"  ❌ 评估失败 (已达最大重试次数): {error_msg[:150]}")
+
+    # 所有重试失败后,返回默认值
+    fallback_reason = f"评估失败(重试{max_retries}次): {str(last_error)[:200]}"
+    print(f"  使用默认值: score=0.0, reason={fallback_reason[:100]}...")
+    return 0.0, fallback_reason
 
 
 # ============================================================================

+ 50 - 5
visualization/sug_v6_1_2_8/convert_v8_to_graph_v2.js

@@ -329,12 +329,30 @@ function convertV8ToGraphV2(runContext, searchResults) {
         // 为每个 Seed 创建节点
         Object.keys(round.add_word_details).forEach((seedText, seedIndex) => {
           const seedId = `seed_${seedText}_r${roundNum}_${seedIndex}`;
+
+          // 查找seed的来源信息 - 从Round 0的seed_list查找基础种子的from_type
+          const round0 = rounds.find(r => r.round_num === 0 || r.type === 'initialization');
+          const seedInfo = round0?.seed_list?.find(s => s.text === seedText) || {};
+          const fromType = seedInfo.from_type || 'unknown';
+
+          // 根据来源设置strategy
+          let strategy;
+          if (fromType === 'seg') {
+            strategy = '初始分词';
+          } else if (fromType === 'add') {
+            strategy = '加词';
+          } else if (fromType === 'sug') {
+            strategy = '调用sug';
+          } else {
+            strategy = 'Seed';  // 默认灰色
+          }
+
           nodes[seedId] = {
             type: 'seed',
             query: seedText,
             level: roundNum * 10 + 2,
             relevance_score: 0,
-            strategy: 'Seed',
+            strategy: strategy,
             iteration: roundNum,
             is_selected: true
           };
@@ -476,13 +494,26 @@ function convertV8ToGraphV2(runContext, searchResults) {
         // 添加下轮查询列表
         round.output_q_list.forEach((q, qIndex) => {
           const nextQId = `next_q_${q.text}_r${roundNum}_${qIndex}`;
+
+          // 根据来源设置strategy
+          let strategy;
+          if (q.from === 'seg') {
+            strategy = '初始分词';
+          } else if (q.from === 'add') {
+            strategy = '加词';
+          } else if (q.from === 'sug') {
+            strategy = '调用sug';
+          } else {
+            strategy = 'Query'; // 默认
+          }
+
           nodes[nextQId] = {
             type: 'next_q',
             query: q.text,
             level: roundNum * 10 + 3,
             relevance_score: q.score || 0,
             evaluationReason: q.reason || '',
-            strategy: q.from === 'add' ? '来自加词' : '来自推荐词',
+            strategy: strategy,
             iteration: roundNum,
             is_selected: true,
             from_source: q.from
@@ -492,7 +523,7 @@ function convertV8ToGraphV2(runContext, searchResults) {
             from: nextQStepId,
             to: nextQId,
             edge_type: 'step_to_next_q',
-            strategy: q.from === 'add' ? '加词' : '推荐词'
+            strategy: strategy
           });
 
           if (!iterations[roundNum * 10 + 3]) iterations[roundNum * 10 + 3] = [];
@@ -526,12 +557,25 @@ function convertV8ToGraphV2(runContext, searchResults) {
         // 添加下轮种子列表
         round.seed_list_next.forEach((seed, seedIndex) => {
           const nextSeedId = `next_seed_${seed.text}_r${roundNum}_${seedIndex}`;
+
+          // 根据来源设置strategy
+          let strategy;
+          if (seed.from === 'seg') {
+            strategy = '初始分词';
+          } else if (seed.from === 'add') {
+            strategy = '加词';
+          } else if (seed.from === 'sug') {
+            strategy = '调用sug';
+          } else {
+            strategy = 'Seed'; // 默认
+          }
+
           nodes[nextSeedId] = {
             type: 'next_seed',
             query: seed.text,
             level: roundNum * 10 + 3,
             relevance_score: seed.score || 0,
-            strategy: seed.from === 'seg' ? '来自分词' : '来自推荐词',
+            strategy: strategy,
             iteration: roundNum,
             is_selected: true,
             from_source: seed.from
@@ -541,7 +585,7 @@ function convertV8ToGraphV2(runContext, searchResults) {
             from: nextSeedStepId,
             to: nextSeedId,
             edge_type: 'step_to_next_seed',
-            strategy: seed.from === 'seg' ? '分词' : '推荐词'
+            strategy: strategy
           });
 
           if (!iterations[roundNum * 10 + 3]) iterations[roundNum * 10 + 3] = [];
@@ -722,6 +766,7 @@ function convertV8ToGraphSimplified(runContext, searchResults) {
       relevance_score: latestOccurrence.score || 0,
       evaluationReason: latestOccurrence.reason || '',
       strategy: data.occurrences.map(o => o.strategy).join(' + '),
+      primaryStrategy: latestOccurrence.strategy || '未知',  // 添加主要策略字段
       iteration: Math.max(...data.occurrences.map(o => o.round), 0),
       is_selected: true,
       occurrences: data.occurrences,

+ 32 - 2
visualization/sug_v6_1_2_8/index.js

@@ -716,6 +716,33 @@ function truncateMiddle(text, maxLength = 20) {
 }
 
 // 根据策略获取颜色
+// 智能提取主要策略的辅助函数
+function getPrimaryStrategy(nodeData) {
+  // 优先级1: 使用 primaryStrategy 字段
+  if (nodeData.primaryStrategy) {
+    return nodeData.primaryStrategy;
+  }
+
+  // 优先级2: 从 occurrences 数组中获取最新的策略
+  if (nodeData.occurrences && Array.isArray(nodeData.occurrences) && nodeData.occurrences.length > 0) {
+    const latestOccurrence = nodeData.occurrences[nodeData.occurrences.length - 1];
+    if (latestOccurrence && latestOccurrence.strategy) {
+      return latestOccurrence.strategy;
+    }
+  }
+
+  // 优先级3: 拆分组合策略字符串,取第一个
+  if (nodeData.strategy && typeof nodeData.strategy === 'string') {
+    const strategies = nodeData.strategy.split(' + ');
+    if (strategies.length > 0 && strategies[0]) {
+      return strategies[0].trim();
+    }
+  }
+
+  // 默认返回原始strategy或未知
+  return nodeData.strategy || '未知';
+}
+
 function getStrategyColor(strategy) {
   const strategyColors = {
     '初始分词': '#10b981',
@@ -732,6 +759,9 @@ function getStrategyColor(strategy) {
     '加词生成': '#ef4444',
     '建议词': '#06b6d4',
     '执行搜索': '#8b5cf6',
+    // 添加简化版本的策略映射
+    '分词': '#10b981',
+    '推荐词': '#06b6d4',
   };
   return strategyColors[strategy] || '#9ca3af';
 }
@@ -740,7 +770,7 @@ function getStrategyColor(strategy) {
 function TreeNode({ node, level, children, isCollapsed, onToggle, isSelected, onSelect }) {
   const hasChildren = children && children.length > 0;
   const score = node.data.score ? parseFloat(node.data.score) : 0;
-  const strategy = node.data.strategy || '';
+  const strategy = getPrimaryStrategy(node.data);  // 使用智能提取函数
   const strategyColor = getStrategyColor(strategy);
   const nodeActualType = node.data.nodeType || node.type; // 获取实际节点类型
 
@@ -1731,7 +1761,7 @@ function FlowContent() {
                         {path.map((node, index) => {
                           // 获取节点的 score、strategy 和 isSelected
                           const nodeScore = node.data.score ? parseFloat(node.data.score) : 0;
-                          const nodeStrategy = node.data.strategy || '';
+                          const nodeStrategy = getPrimaryStrategy(node.data);  // 使用智能提取函数
                           const strategyColor = getStrategyColor(nodeStrategy);
                           const nodeIsSelected = node.type === 'note' ? node.data.matchLevel !== 'unsatisfied' : node.data.isSelected !== false;
                           const nodeActualType = node.data.nodeType || node.type; // 获取实际节点类型